github.com/iotexproject/iotex-core@v1.14.1-rc1/action/stake_adddeposit.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 didslaimed. 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/core/types" 15 "github.com/pkg/errors" 16 "google.golang.org/protobuf/proto" 17 18 "github.com/iotexproject/iotex-proto/golang/iotextypes" 19 20 "github.com/iotexproject/iotex-core/pkg/util/byteutil" 21 "github.com/iotexproject/iotex-core/pkg/version" 22 ) 23 24 const ( 25 // DepositToStakePayloadGas represents the DepositToStake payload gas per uint 26 DepositToStakePayloadGas = uint64(100) 27 // DepositToStakeBaseIntrinsicGas represents the base intrinsic gas for DepositToStake 28 DepositToStakeBaseIntrinsicGas = uint64(10000) 29 30 _depositToStakeInterfaceABI = `[ 31 { 32 "inputs": [ 33 { 34 "internalType": "uint64", 35 "name": "bucketIndex", 36 "type": "uint64" 37 }, 38 { 39 "internalType": "uint256", 40 "name": "amount", 41 "type": "uint256" 42 }, 43 { 44 "internalType": "uint8[]", 45 "name": "data", 46 "type": "uint8[]" 47 } 48 ], 49 "name": "depositToStake", 50 "outputs": [], 51 "stateMutability": "nonpayable", 52 "type": "function" 53 } 54 ]` 55 ) 56 57 var ( 58 // _depositToStakeMethod is the interface of the abi encoding of stake action 59 _depositToStakeMethod abi.Method 60 _ EthCompatibleAction = (*DepositToStake)(nil) 61 ) 62 63 // DepositToStake defines the action of stake add deposit 64 type DepositToStake struct { 65 AbstractAction 66 67 bucketIndex uint64 68 amount *big.Int 69 payload []byte 70 } 71 72 func init() { 73 depositToStakeInterface, err := abi.JSON(strings.NewReader(_depositToStakeInterfaceABI)) 74 if err != nil { 75 panic(err) 76 } 77 var ok bool 78 _depositToStakeMethod, ok = depositToStakeInterface.Methods["depositToStake"] 79 if !ok { 80 panic("fail to load the method") 81 } 82 } 83 84 // NewDepositToStake returns a DepositToStake instance 85 func NewDepositToStake( 86 nonce uint64, 87 index uint64, 88 amount string, 89 payload []byte, 90 gasLimit uint64, 91 gasPrice *big.Int, 92 ) (*DepositToStake, error) { 93 stake, ok := new(big.Int).SetString(amount, 10) 94 if !ok { 95 return nil, errors.Wrapf(ErrInvalidAmount, "amount %s", amount) 96 } 97 return &DepositToStake{ 98 AbstractAction: AbstractAction{ 99 version: version.ProtocolVersion, 100 nonce: nonce, 101 gasLimit: gasLimit, 102 gasPrice: gasPrice, 103 }, 104 bucketIndex: index, 105 amount: stake, 106 payload: payload, 107 }, nil 108 } 109 110 // Amount returns the amount 111 func (ds *DepositToStake) Amount() *big.Int { return ds.amount } 112 113 // Payload returns the payload bytes 114 func (ds *DepositToStake) Payload() []byte { return ds.payload } 115 116 // BucketIndex returns bucket indexs 117 func (ds *DepositToStake) BucketIndex() uint64 { return ds.bucketIndex } 118 119 // Serialize returns a raw byte stream of the DepositToStake struct 120 func (ds *DepositToStake) Serialize() []byte { 121 return byteutil.Must(proto.Marshal(ds.Proto())) 122 } 123 124 // Proto converts to protobuf DepositToStake Action 125 func (ds *DepositToStake) Proto() *iotextypes.StakeAddDeposit { 126 act := &iotextypes.StakeAddDeposit{ 127 BucketIndex: ds.bucketIndex, 128 Payload: ds.payload, 129 } 130 131 if ds.amount != nil { 132 act.Amount = ds.amount.String() 133 } 134 return act 135 } 136 137 // LoadProto converts a protobuf's Action to DepositToStake 138 func (ds *DepositToStake) LoadProto(pbAct *iotextypes.StakeAddDeposit) error { 139 if pbAct == nil { 140 return ErrNilProto 141 } 142 143 ds.bucketIndex = pbAct.GetBucketIndex() 144 ds.payload = pbAct.GetPayload() 145 if pbAct.GetAmount() == "" { 146 ds.amount = big.NewInt(0) 147 } else { 148 amount, ok := new(big.Int).SetString(pbAct.GetAmount(), 10) 149 if !ok { 150 return errors.Errorf("invalid amount %s", pbAct.GetAmount()) 151 } 152 ds.amount = amount 153 } 154 155 return nil 156 } 157 158 // IntrinsicGas returns the intrinsic gas of a DepositToStake 159 func (ds *DepositToStake) IntrinsicGas() (uint64, error) { 160 payloadSize := uint64(len(ds.Payload())) 161 return CalculateIntrinsicGas(DepositToStakeBaseIntrinsicGas, DepositToStakePayloadGas, payloadSize) 162 } 163 164 // Cost returns the total cost of a DepositToStake 165 func (ds *DepositToStake) Cost() (*big.Int, error) { 166 intrinsicGas, err := ds.IntrinsicGas() 167 if err != nil { 168 return nil, errors.Wrap(err, "failed to get intrinsic gas for the DepositToStake") 169 } 170 depositToStakeFee := big.NewInt(0).Mul(ds.GasPrice(), big.NewInt(0).SetUint64(intrinsicGas)) 171 return big.NewInt(0).Add(ds.Amount(), depositToStakeFee), nil 172 } 173 174 // SanityCheck validates the variables in the action 175 func (ds *DepositToStake) SanityCheck() error { 176 if ds.Amount().Sign() <= 0 { 177 return errors.Wrap(ErrInvalidAmount, "negative value") 178 } 179 180 return ds.AbstractAction.SanityCheck() 181 } 182 183 // EncodeABIBinary encodes data in abi encoding 184 func (ds *DepositToStake) EncodeABIBinary() ([]byte, error) { 185 return ds.encodeABIBinary() 186 } 187 188 func (ds *DepositToStake) encodeABIBinary() ([]byte, error) { 189 data, err := _depositToStakeMethod.Inputs.Pack(ds.bucketIndex, ds.amount, ds.payload) 190 if err != nil { 191 return nil, err 192 } 193 return append(_depositToStakeMethod.ID, data...), nil 194 } 195 196 // NewDepositToStakeFromABIBinary decodes data into depositToStake action 197 func NewDepositToStakeFromABIBinary(data []byte) (*DepositToStake, error) { 198 var ( 199 paramsMap = map[string]interface{}{} 200 ok bool 201 ds DepositToStake 202 ) 203 // sanity check 204 if len(data) <= 4 || !bytes.Equal(_depositToStakeMethod.ID[:], data[:4]) { 205 return nil, errDecodeFailure 206 } 207 if err := _depositToStakeMethod.Inputs.UnpackIntoMap(paramsMap, data[4:]); err != nil { 208 return nil, err 209 } 210 if ds.bucketIndex, ok = paramsMap["bucketIndex"].(uint64); !ok { 211 return nil, errDecodeFailure 212 } 213 if ds.amount, ok = paramsMap["amount"].(*big.Int); !ok { 214 return nil, errDecodeFailure 215 } 216 if ds.payload, ok = paramsMap["data"].([]byte); !ok { 217 return nil, errDecodeFailure 218 } 219 return &ds, nil 220 } 221 222 // ToEthTx converts action to eth-compatible tx 223 func (ds *DepositToStake) ToEthTx(_ uint32) (*types.Transaction, error) { 224 data, err := ds.encodeABIBinary() 225 if err != nil { 226 return nil, err 227 } 228 return types.NewTx(&types.LegacyTx{ 229 Nonce: ds.Nonce(), 230 GasPrice: ds.GasPrice(), 231 Gas: ds.GasLimit(), 232 To: &_stakingProtocolEthAddr, 233 Value: big.NewInt(0), 234 Data: data, 235 }), nil 236 }