github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/evm/types/call.go (about) 1 package types 2 3 import ( 4 "fmt" 5 "math/big" 6 7 gethCommon "github.com/onflow/go-ethereum/common" 8 gethCore "github.com/onflow/go-ethereum/core" 9 gethTypes "github.com/onflow/go-ethereum/core/types" 10 gethParams "github.com/onflow/go-ethereum/params" 11 "github.com/onflow/go-ethereum/rlp" 12 ) 13 14 const ( 15 // tx type 255 is used for direct calls from COAs 16 DirectCallTxType = byte(255) 17 18 UnknownCallSubType = byte(0) 19 DepositCallSubType = byte(1) 20 WithdrawCallSubType = byte(2) 21 TransferCallSubType = byte(3) 22 DeployCallSubType = byte(4) 23 ContractCallSubType = byte(5) 24 25 // Note that these gas values might need to change if we 26 // change the transaction (e.g. add accesslist), 27 // then it has to be updated to use Intrinsic function 28 // to calculate the minimum gas needed to run the transaction. 29 IntrinsicFeeForTokenTransfer = gethParams.TxGas 30 31 // 21_000 is the minimum for a transaction + max gas allowed for receive/fallback methods 32 DefaultGasLimitForTokenTransfer = IntrinsicFeeForTokenTransfer + 2_300 33 34 // the value is set to the gas limit for transfer to facilitate transfers 35 // to smart contract addresses. 36 DepositCallGasLimit = DefaultGasLimitForTokenTransfer 37 WithdrawCallGasLimit = DefaultGasLimitForTokenTransfer 38 ) 39 40 // DirectCall captures all the data related to a direct call to evm 41 // direct calls are similar to transactions but they don't have 42 // signatures and don't need sequence number checks 43 // Note that eventhough we don't check the nonce, it impacts 44 // hash calculation and also impacts the address of resulting contract 45 // when deployed through direct calls. 46 // Users don't have the worry about the nonce, handler sets 47 // it to the right value. 48 type DirectCall struct { 49 Type byte 50 SubType byte 51 From Address 52 To Address 53 Data []byte 54 Value *big.Int 55 GasLimit uint64 56 Nonce uint64 57 } 58 59 // DirectCallFromEncoded constructs a DirectCall from encoded data 60 func DirectCallFromEncoded(encoded []byte) (*DirectCall, error) { 61 if encoded[0] != DirectCallTxType { 62 return nil, fmt.Errorf("tx type mismatch") 63 } 64 dc := &DirectCall{} 65 return dc, rlp.DecodeBytes(encoded[1:], dc) 66 } 67 68 // Encode encodes the direct call it also adds the type 69 // as the very first byte, similar to how evm encodes types. 70 func (dc *DirectCall) Encode() ([]byte, error) { 71 encoded, err := rlp.EncodeToBytes(dc) 72 return append([]byte{dc.Type}, encoded...), err 73 } 74 75 // Hash computes the hash of a direct call 76 func (dc *DirectCall) Hash() (gethCommon.Hash, error) { 77 // we use geth transaction hash calculation since direct call hash is included in the 78 // block transaction hashes, and thus observed as any other transaction 79 return dc.Transaction().Hash(), nil 80 } 81 82 // Message constructs a core.Message from the direct call 83 func (dc *DirectCall) Message() *gethCore.Message { 84 return &gethCore.Message{ 85 From: dc.From.ToCommon(), 86 To: dc.to(), 87 Value: dc.Value, 88 Data: dc.Data, 89 Nonce: dc.Nonce, 90 GasLimit: dc.GasLimit, 91 GasPrice: big.NewInt(0), // price is set to zero fo direct calls 92 GasTipCap: big.NewInt(0), // also known as maxPriorityFeePerGas (in GWei) 93 GasFeeCap: big.NewInt(0), // also known as maxFeePerGas (in GWei) 94 // AccessList: tx.AccessList(), // TODO revisit this value, the cost matter but performance might 95 SkipAccountChecks: true, // this would let us not set the nonce 96 } 97 } 98 99 // Transaction constructs a geth.Transaction from the direct call 100 func (dc *DirectCall) Transaction() *gethTypes.Transaction { 101 return gethTypes.NewTx(&gethTypes.LegacyTx{ 102 GasPrice: big.NewInt(0), 103 Gas: dc.GasLimit, 104 To: dc.to(), 105 Value: dc.Value, 106 Data: dc.Data, 107 Nonce: dc.Nonce, 108 }) 109 } 110 111 // EmptyToField returns true if `to` field contains an empty address 112 func (dc *DirectCall) EmptyToField() bool { 113 return dc.To == EmptyAddress 114 } 115 116 func (dc *DirectCall) to() *gethCommon.Address { 117 if !dc.EmptyToField() { 118 ct := dc.To.ToCommon() 119 return &ct 120 } 121 return nil 122 } 123 124 func NewDepositCall( 125 bridge Address, 126 address Address, 127 amount *big.Int, 128 nonce uint64, 129 ) *DirectCall { 130 return &DirectCall{ 131 Type: DirectCallTxType, 132 SubType: DepositCallSubType, 133 From: bridge, 134 To: address, 135 Data: nil, 136 Value: amount, 137 GasLimit: DepositCallGasLimit, 138 Nonce: nonce, 139 } 140 } 141 142 func NewWithdrawCall( 143 bridge Address, 144 address Address, 145 amount *big.Int, 146 nonce uint64, 147 ) *DirectCall { 148 return &DirectCall{ 149 Type: DirectCallTxType, 150 SubType: WithdrawCallSubType, 151 From: address, 152 To: bridge, 153 Data: nil, 154 Value: amount, 155 GasLimit: WithdrawCallGasLimit, 156 Nonce: nonce, 157 } 158 } 159 160 func NewTransferCall( 161 from Address, 162 to Address, 163 amount *big.Int, 164 nonce uint64, 165 ) *DirectCall { 166 return &DirectCall{ 167 Type: DirectCallTxType, 168 SubType: TransferCallSubType, 169 From: from, 170 To: to, 171 Data: nil, 172 Value: amount, 173 GasLimit: DefaultGasLimitForTokenTransfer, 174 Nonce: nonce, 175 } 176 } 177 178 func NewDeployCall( 179 caller Address, 180 code Code, 181 gasLimit uint64, 182 value *big.Int, 183 nonce uint64, 184 ) *DirectCall { 185 return &DirectCall{ 186 Type: DirectCallTxType, 187 SubType: DeployCallSubType, 188 From: caller, 189 To: EmptyAddress, 190 Data: code, 191 Value: value, 192 GasLimit: gasLimit, 193 Nonce: nonce, 194 } 195 } 196 197 // this subtype should only be used internally for 198 // deploying contracts at given addresses (e.g. COA account init setup) 199 // should not be used for other means. 200 func NewDeployCallWithTargetAddress( 201 caller Address, 202 to Address, 203 code Code, 204 gasLimit uint64, 205 value *big.Int, 206 nonce uint64, 207 ) *DirectCall { 208 return &DirectCall{ 209 Type: DirectCallTxType, 210 SubType: DeployCallSubType, 211 From: caller, 212 To: to, 213 Data: code, 214 Value: value, 215 GasLimit: gasLimit, 216 Nonce: nonce, 217 } 218 } 219 220 func NewContractCall( 221 caller Address, 222 to Address, 223 data Data, 224 gasLimit uint64, 225 value *big.Int, 226 nonce uint64, 227 ) *DirectCall { 228 return &DirectCall{ 229 Type: DirectCallTxType, 230 SubType: ContractCallSubType, 231 From: caller, 232 To: to, 233 Data: data, 234 Value: value, 235 GasLimit: gasLimit, 236 Nonce: nonce, 237 } 238 } 239 240 type GasLimit uint64 241 242 type Code []byte 243 244 type Data []byte 245 246 // AsBigInt process the data and return it as a big integer 247 func (d Data) AsBigInt() *big.Int { 248 return new(big.Int).SetBytes(d) 249 }