github.com/iotexproject/iotex-core@v1.14.1-rc1/action/stake_transferownership.go (about) 1 // Copyright (c) 2020 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package action 7 8 import ( 9 "bytes" 10 "math/big" 11 "strings" 12 13 "github.com/ethereum/go-ethereum/accounts/abi" 14 "github.com/ethereum/go-ethereum/common" 15 "github.com/ethereum/go-ethereum/core/types" 16 "github.com/pkg/errors" 17 "google.golang.org/protobuf/proto" 18 19 "github.com/iotexproject/iotex-address/address" 20 "github.com/iotexproject/iotex-core/pkg/util/byteutil" 21 "github.com/iotexproject/iotex-proto/golang/iotextypes" 22 23 "github.com/iotexproject/iotex-core/pkg/version" 24 ) 25 26 const ( 27 _transferStakeInterfaceABI = `[ 28 { 29 "inputs": [ 30 { 31 "internalType": "address", 32 "name": "voterAddress", 33 "type": "address" 34 }, 35 { 36 "internalType": "uint64", 37 "name": "bucketIndex", 38 "type": "uint64" 39 }, 40 { 41 "internalType": "uint8[]", 42 "name": "data", 43 "type": "uint8[]" 44 } 45 ], 46 "name": "transferStake", 47 "outputs": [], 48 "stateMutability": "nonpayable", 49 "type": "function" 50 } 51 ]` 52 ) 53 54 var ( 55 // _transferStakeMethod is the interface of the abi encoding of stake action 56 _transferStakeMethod abi.Method 57 _ EthCompatibleAction = (*TransferStake)(nil) 58 ) 59 60 // TransferStake defines the action of transfering stake ownership ts the other 61 type TransferStake struct { 62 AbstractAction 63 64 voterAddress address.Address 65 bucketIndex uint64 66 payload []byte 67 } 68 69 func init() { 70 transferStakeInterface, err := abi.JSON(strings.NewReader(_transferStakeInterfaceABI)) 71 if err != nil { 72 panic(err) 73 } 74 var ok bool 75 _transferStakeMethod, ok = transferStakeInterface.Methods["transferStake"] 76 if !ok { 77 panic("fail to load the method") 78 } 79 } 80 81 // NewTransferStake returns a TransferStake instance 82 func NewTransferStake( 83 nonce uint64, 84 voterAddress string, 85 bucketIndex uint64, 86 payload []byte, 87 gasLimit uint64, 88 gasPrice *big.Int, 89 ) (*TransferStake, error) { 90 voterAddr, err := address.FromString(voterAddress) 91 if err != nil { 92 return nil, errors.Wrap(err, "failed to load address from string") 93 } 94 return &TransferStake{ 95 AbstractAction: AbstractAction{ 96 version: version.ProtocolVersion, 97 nonce: nonce, 98 gasLimit: gasLimit, 99 gasPrice: gasPrice, 100 }, 101 voterAddress: voterAddr, 102 bucketIndex: bucketIndex, 103 payload: payload, 104 }, nil 105 } 106 107 // VoterAddress returns the address of recipient 108 func (ts *TransferStake) VoterAddress() address.Address { return ts.voterAddress } 109 110 // BucketIndex returns bucket index 111 func (ts *TransferStake) BucketIndex() uint64 { return ts.bucketIndex } 112 113 // Payload returns the payload bytes 114 func (ts *TransferStake) Payload() []byte { return ts.payload } 115 116 // Serialize returns a raw byte stream of the transfer stake action struct 117 func (ts *TransferStake) Serialize() []byte { 118 return byteutil.Must(proto.Marshal(ts.Proto())) 119 } 120 121 // Proto converts transfer stake to protobuf 122 func (ts *TransferStake) Proto() *iotextypes.StakeTransferOwnership { 123 act := &iotextypes.StakeTransferOwnership{ 124 VoterAddress: ts.voterAddress.String(), 125 BucketIndex: ts.bucketIndex, 126 Payload: ts.payload, 127 } 128 129 return act 130 } 131 132 // LoadProto loads transfer stake protobuf 133 func (ts *TransferStake) LoadProto(pbAct *iotextypes.StakeTransferOwnership) error { 134 if pbAct == nil { 135 return ErrNilProto 136 } 137 voterAddress, err := address.FromString(pbAct.GetVoterAddress()) 138 if err != nil { 139 return errors.Wrap(err, "failed to load address from string") 140 } 141 ts.voterAddress = voterAddress 142 ts.bucketIndex = pbAct.GetBucketIndex() 143 ts.payload = pbAct.GetPayload() 144 return nil 145 } 146 147 // IntrinsicGas returns the intrinsic gas of a TransferStake 148 func (ts *TransferStake) IntrinsicGas() (uint64, error) { 149 payloadSize := uint64(len(ts.Payload())) 150 return CalculateIntrinsicGas(MoveStakeBaseIntrinsicGas, MoveStakePayloadGas, payloadSize) 151 } 152 153 // Cost returns the tstal cost of a TransferStake 154 func (ts *TransferStake) Cost() (*big.Int, error) { 155 intrinsicGas, err := ts.IntrinsicGas() 156 if err != nil { 157 return nil, errors.Wrap(err, "failed ts get intrinsic gas for the TransferStake") 158 } 159 transferStakeFee := big.NewInt(0).Mul(ts.GasPrice(), big.NewInt(0).SetUint64(intrinsicGas)) 160 return transferStakeFee, nil 161 } 162 163 // EncodeABIBinary encodes data in abi encoding 164 func (ts *TransferStake) EncodeABIBinary() ([]byte, error) { 165 return ts.encodeABIBinary() 166 } 167 168 func (ts *TransferStake) encodeABIBinary() ([]byte, error) { 169 voterEthAddr := common.BytesToAddress(ts.voterAddress.Bytes()) 170 data, err := _transferStakeMethod.Inputs.Pack(voterEthAddr, ts.bucketIndex, ts.payload) 171 if err != nil { 172 return nil, err 173 } 174 return append(_transferStakeMethod.ID, data...), nil 175 } 176 177 // NewTransferStakeFromABIBinary decodes data into TransferStake action 178 func NewTransferStakeFromABIBinary(data []byte) (*TransferStake, error) { 179 var ( 180 paramsMap = map[string]interface{}{} 181 ok bool 182 err error 183 ts TransferStake 184 ) 185 // sanity check 186 if len(data) <= 4 || !bytes.Equal(_transferStakeMethod.ID, data[:4]) { 187 return nil, errDecodeFailure 188 } 189 if err := _transferStakeMethod.Inputs.UnpackIntoMap(paramsMap, data[4:]); err != nil { 190 return nil, err 191 } 192 if ts.voterAddress, err = ethAddrToNativeAddr(paramsMap["voterAddress"]); err != nil { 193 return nil, err 194 } 195 if ts.bucketIndex, ok = paramsMap["bucketIndex"].(uint64); !ok { 196 return nil, errDecodeFailure 197 } 198 if ts.payload, ok = paramsMap["data"].([]byte); !ok { 199 return nil, errDecodeFailure 200 } 201 return &ts, nil 202 } 203 204 // ToEthTx converts action to eth-compatible tx 205 func (ts *TransferStake) ToEthTx(_ uint32) (*types.Transaction, error) { 206 data, err := ts.encodeABIBinary() 207 if err != nil { 208 return nil, err 209 } 210 return types.NewTx(&types.LegacyTx{ 211 Nonce: ts.Nonce(), 212 GasPrice: ts.GasPrice(), 213 Gas: ts.GasLimit(), 214 To: &_stakingProtocolEthAddr, 215 Value: big.NewInt(0), 216 Data: data, 217 }), nil 218 }