github.com/dim4egster/coreth@v0.10.2/accounts/abi/bind/base.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2015 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package bind 28 29 import ( 30 "context" 31 "errors" 32 "fmt" 33 "math/big" 34 "strings" 35 "sync" 36 37 "github.com/dim4egster/coreth/accounts/abi" 38 "github.com/dim4egster/coreth/core/types" 39 "github.com/dim4egster/coreth/core/vm" 40 "github.com/dim4egster/coreth/interfaces" 41 "github.com/ethereum/go-ethereum/common" 42 "github.com/ethereum/go-ethereum/crypto" 43 "github.com/ethereum/go-ethereum/event" 44 ) 45 46 var ( 47 ErrNilAssetAmount = errors.New("cannot specify nil asset amount for native asset call") 48 errNativeAssetDeployContract = errors.New("cannot specify native asset params while deploying a contract") 49 ) 50 51 // SignerFn is a signer function callback when a contract requires a method to 52 // sign the transaction before submission. 53 type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error) 54 55 // CallOpts is the collection of options to fine tune a contract call request. 56 type CallOpts struct { 57 Accepted bool // Whether to operate on the accepted state or the last known one 58 From common.Address // Optional the sender address, otherwise the first account is used 59 BlockNumber *big.Int // Optional the block number on which the call should be performed 60 Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) 61 } 62 63 // NativeAssetCallOpts contains params for native asset call 64 type NativeAssetCallOpts struct { 65 AssetID common.Hash // Asset ID 66 AssetAmount *big.Int // Asset amount 67 } 68 69 // TransactOpts is the collection of authorization data required to create a 70 // valid Ethereum transaction. 71 type TransactOpts struct { 72 From common.Address // Ethereum account to send the transaction from 73 Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state) 74 Signer SignerFn // Method to use for signing the transaction (mandatory) 75 76 Value *big.Int // Funds to transfer along the transaction (nil = 0 = no funds) 77 GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle) 78 GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle) 79 GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle) 80 GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate) 81 82 Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) 83 84 NoSend bool // Do all transact steps but do not send the transaction 85 86 // If set, the transaction is transformed to perform the requested call through the native asset 87 // precompile. This will update the to address of the transaction to that of the native asset precompile 88 // and pack the requested [to] address, [assetID], [assetAmount], and [input] data for the transaction 89 // into the call data of the transaction. When executed within the EVM, the precompile will parse the input 90 // data and attempt to atomically transfer [assetAmount] of [assetID] to the [to] address and invoke the 91 // contract at [to] if present, passing in the original [input] data. 92 NativeAssetCall *NativeAssetCallOpts 93 } 94 95 // FilterOpts is the collection of options to fine tune filtering for events 96 // within a bound contract. 97 type FilterOpts struct { 98 Start uint64 // Start of the queried range 99 End *uint64 // End of the range (nil = latest) 100 101 Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) 102 } 103 104 // WatchOpts is the collection of options to fine tune subscribing for events 105 // within a bound contract. 106 type WatchOpts struct { 107 Start *uint64 // Start of the queried range (nil = latest) 108 Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) 109 } 110 111 // MetaData collects all metadata for a bound contract. 112 type MetaData struct { 113 mu sync.Mutex 114 Sigs map[string]string 115 Bin string 116 ABI string 117 ab *abi.ABI 118 } 119 120 func (m *MetaData) GetAbi() (*abi.ABI, error) { 121 m.mu.Lock() 122 defer m.mu.Unlock() 123 if m.ab != nil { 124 return m.ab, nil 125 } 126 if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil { 127 return nil, err 128 } else { 129 m.ab = &parsed 130 } 131 return m.ab, nil 132 } 133 134 // BoundContract is the base wrapper object that reflects a contract on the 135 // Ethereum network. It contains a collection of methods that are used by the 136 // higher level contract bindings to operate. 137 type BoundContract struct { 138 address common.Address // Deployment address of the contract on the Ethereum blockchain 139 abi abi.ABI // Reflect based ABI to access the correct Ethereum methods 140 caller ContractCaller // Read interface to interact with the blockchain 141 transactor ContractTransactor // Write interface to interact with the blockchain 142 filterer ContractFilterer // Event filtering to interact with the blockchain 143 } 144 145 // NewBoundContract creates a low level contract interface through which calls 146 // and transactions may be made through. 147 func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, filterer ContractFilterer) *BoundContract { 148 return &BoundContract{ 149 address: address, 150 abi: abi, 151 caller: caller, 152 transactor: transactor, 153 filterer: filterer, 154 } 155 } 156 157 // DeployContract deploys a contract onto the Ethereum blockchain and binds the 158 // deployment address with a Go wrapper. 159 func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) { 160 // Otherwise try to deploy the contract 161 c := NewBoundContract(common.Address{}, abi, backend, backend, backend) 162 163 input, err := c.abi.Pack("", params...) 164 if err != nil { 165 return common.Address{}, nil, nil, err 166 } 167 tx, err := c.transact(opts, nil, append(bytecode, input...)) 168 if err != nil { 169 return common.Address{}, nil, nil, err 170 } 171 c.address = crypto.CreateAddress(opts.From, tx.Nonce()) 172 return c.address, tx, c, nil 173 } 174 175 // Call invokes the (constant) contract method with params as input values and 176 // sets the output to result. The result type might be a single field for simple 177 // returns, a slice of interfaces for anonymous returns and a struct for named 178 // returns. 179 func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error { 180 // Don't crash on a lazy user 181 if opts == nil { 182 opts = new(CallOpts) 183 } 184 if results == nil { 185 results = new([]interface{}) 186 } 187 // Pack the input, call and unpack the results 188 input, err := c.abi.Pack(method, params...) 189 if err != nil { 190 return err 191 } 192 var ( 193 msg = interfaces.CallMsg{From: opts.From, To: &c.address, Data: input} 194 ctx = ensureContext(opts.Context) 195 code []byte 196 output []byte 197 ) 198 if opts.Accepted { 199 pb, ok := c.caller.(AcceptedContractCaller) 200 if !ok { 201 return ErrNoAcceptedState 202 } 203 output, err = pb.AcceptedCallContract(ctx, msg) 204 if err != nil { 205 return err 206 } 207 if len(output) == 0 { 208 // Make sure we have a contract to operate on, and bail out otherwise. 209 if code, err = pb.AcceptedCodeAt(ctx, c.address); err != nil { 210 return err 211 } else if len(code) == 0 { 212 return ErrNoCode 213 } 214 } 215 } else { 216 output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber) 217 if err != nil { 218 return err 219 } 220 if len(output) == 0 { 221 // Make sure we have a contract to operate on, and bail out otherwise. 222 if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil { 223 return err 224 } else if len(code) == 0 { 225 return ErrNoCode 226 } 227 } 228 } 229 230 if len(*results) == 0 { 231 res, err := c.abi.Unpack(method, output) 232 *results = res 233 return err 234 } 235 res := *results 236 return c.abi.UnpackIntoInterface(res[0], method, output) 237 } 238 239 // Transact invokes the (paid) contract method with params as input values. 240 func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 241 // Otherwise pack up the parameters and invoke the contract 242 input, err := c.abi.Pack(method, params...) 243 if err != nil { 244 return nil, err 245 } 246 // todo(rjl493456442) check the method is payable or not, 247 // reject invalid transaction at the first place 248 return c.transact(opts, &c.address, input) 249 } 250 251 // RawTransact initiates a transaction with the given raw calldata as the input. 252 // It's usually used to initiate transactions for invoking **Fallback** function. 253 func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) { 254 // todo(rjl493456442) check the method is payable or not, 255 // reject invalid transaction at the first place 256 return c.transact(opts, &c.address, calldata) 257 } 258 259 // Transfer initiates a plain transaction to move funds to the contract, calling 260 // its default method if one is available. 261 func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) { 262 // todo(rjl493456442) check the payable fallback or receive is defined 263 // or not, reject invalid transaction at the first place 264 return c.transact(opts, &c.address, nil) 265 } 266 267 // wrapNativeAssetCall preprocesses the arguments to transform the requested call to go through the 268 // native asset call precompile if it is specified on [opts]. 269 func wrapNativeAssetCall(opts *TransactOpts, contract *common.Address, input []byte) (*common.Address, []byte, error) { 270 if opts.NativeAssetCall != nil { 271 // Prevent the user from sending a non-zero value through native asset call precompile as this will 272 // transfer the funds to the precompile address and essentially burn the funds. 273 if opts.Value != nil && opts.Value.Cmp(common.Big0) != 0 { 274 return nil, nil, fmt.Errorf("value must be 0 when performing native asset call, found %d", opts.Value) 275 } 276 if opts.NativeAssetCall.AssetAmount == nil { 277 return nil, nil, ErrNilAssetAmount 278 } 279 if opts.NativeAssetCall.AssetAmount.Cmp(common.Big0) < 0 { 280 return nil, nil, fmt.Errorf("asset value cannot be < 0 when performing native asset call, found %d", opts.NativeAssetCall.AssetAmount) 281 } 282 // Prevent potential panic if [contract] is nil in the case that transact is called through DeployContract. 283 if contract == nil { 284 return nil, nil, errNativeAssetDeployContract 285 } 286 // wrap input with native asset call params 287 input = vm.PackNativeAssetCallInput( 288 *contract, 289 opts.NativeAssetCall.AssetID, 290 opts.NativeAssetCall.AssetAmount, 291 input, 292 ) 293 // target addr is now precompile 294 contract = &vm.NativeAssetCallAddr 295 } 296 return contract, input, nil 297 } 298 299 func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Address, input []byte, head *types.Header) (*types.Transaction, error) { 300 // Normalize value 301 value := opts.Value 302 if value == nil { 303 value = new(big.Int) 304 } 305 // Estimate TipCap 306 gasTipCap := opts.GasTipCap 307 if gasTipCap == nil { 308 tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context)) 309 if err != nil { 310 return nil, err 311 } 312 gasTipCap = tip 313 } 314 // Estimate FeeCap 315 gasFeeCap := opts.GasFeeCap 316 if gasFeeCap == nil { 317 gasFeeCap = new(big.Int).Add( 318 gasTipCap, 319 new(big.Int).Mul(head.BaseFee, big.NewInt(2)), 320 ) 321 } 322 if gasFeeCap.Cmp(gasTipCap) < 0 { 323 return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap) 324 } 325 // Estimate GasLimit 326 gasLimit := opts.GasLimit 327 if opts.GasLimit == 0 { 328 var err error 329 gasLimit, err = c.estimateGasLimit(opts, contract, input, nil, gasTipCap, gasFeeCap, value) 330 if err != nil { 331 return nil, err 332 } 333 } 334 // create the transaction 335 nonce, err := c.getNonce(opts) 336 if err != nil { 337 return nil, err 338 } 339 baseTx := &types.DynamicFeeTx{ 340 To: contract, 341 Nonce: nonce, 342 GasFeeCap: gasFeeCap, 343 GasTipCap: gasTipCap, 344 Gas: gasLimit, 345 Value: value, 346 Data: input, 347 } 348 return types.NewTx(baseTx), nil 349 } 350 351 func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { 352 if opts.GasFeeCap != nil || opts.GasTipCap != nil { 353 return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") 354 } 355 // Normalize value 356 value := opts.Value 357 if value == nil { 358 value = new(big.Int) 359 } 360 // Estimate GasPrice 361 gasPrice := opts.GasPrice 362 if gasPrice == nil { 363 price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context)) 364 if err != nil { 365 return nil, err 366 } 367 gasPrice = price 368 } 369 // Estimate GasLimit 370 gasLimit := opts.GasLimit 371 if opts.GasLimit == 0 { 372 var err error 373 gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value) 374 if err != nil { 375 return nil, err 376 } 377 } 378 // create the transaction 379 nonce, err := c.getNonce(opts) 380 if err != nil { 381 return nil, err 382 } 383 baseTx := &types.LegacyTx{ 384 To: contract, 385 Nonce: nonce, 386 GasPrice: gasPrice, 387 Gas: gasLimit, 388 Value: value, 389 Data: input, 390 } 391 return types.NewTx(baseTx), nil 392 } 393 394 func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Address, input []byte, gasPrice, gasTipCap, gasFeeCap, value *big.Int) (uint64, error) { 395 if contract != nil { 396 // Gas estimation cannot succeed without code for method invocations. 397 if code, err := c.transactor.AcceptedCodeAt(ensureContext(opts.Context), c.address); err != nil { 398 return 0, err 399 } else if len(code) == 0 { 400 return 0, ErrNoCode 401 } 402 } 403 msg := interfaces.CallMsg{ 404 From: opts.From, 405 To: contract, 406 GasPrice: gasPrice, 407 GasTipCap: gasTipCap, 408 GasFeeCap: gasFeeCap, 409 Value: value, 410 Data: input, 411 } 412 return c.transactor.EstimateGas(ensureContext(opts.Context), msg) 413 } 414 415 func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) { 416 if opts.Nonce == nil { 417 return c.transactor.AcceptedNonceAt(ensureContext(opts.Context), opts.From) 418 } else { 419 return opts.Nonce.Uint64(), nil 420 } 421 } 422 423 // transact executes an actual transaction invocation, first deriving any missing 424 // authorization fields, and then scheduling the transaction for execution. 425 func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { 426 if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) { 427 return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") 428 } 429 // Create the transaction 430 var ( 431 rawTx *types.Transaction 432 err error 433 ) 434 // Preprocess native asset call arguments if present 435 contract, input, err = wrapNativeAssetCall(opts, contract, input) 436 if err != nil { 437 return nil, err 438 } 439 if opts.GasPrice != nil { 440 rawTx, err = c.createLegacyTx(opts, contract, input) 441 } else { 442 // Only query for basefee if gasPrice not specified 443 if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); errHead != nil { 444 return nil, errHead 445 } else if head.BaseFee != nil { 446 rawTx, err = c.createDynamicTx(opts, contract, input, head) 447 } else { 448 // Chain is not London ready -> use legacy transaction 449 rawTx, err = c.createLegacyTx(opts, contract, input) 450 } 451 } 452 if err != nil { 453 return nil, err 454 } 455 // Sign the transaction and schedule it for execution 456 if opts.Signer == nil { 457 return nil, errors.New("no signer to authorize the transaction with") 458 } 459 signedTx, err := opts.Signer(opts.From, rawTx) 460 if err != nil { 461 return nil, err 462 } 463 if opts.NoSend { 464 return signedTx, nil 465 } 466 if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil { 467 return nil, err 468 } 469 return signedTx, nil 470 } 471 472 // FilterLogs filters contract logs for past blocks, returning the necessary 473 // channels to construct a strongly typed bound iterator on top of them. 474 func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) { 475 // Don't crash on a lazy user 476 if opts == nil { 477 opts = new(FilterOpts) 478 } 479 // Append the event selector to the query parameters and construct the topic set 480 query = append([][]interface{}{{c.abi.Events[name].ID}}, query...) 481 482 topics, err := abi.MakeTopics(query...) 483 if err != nil { 484 return nil, nil, err 485 } 486 // Start the background filtering 487 logs := make(chan types.Log, 128) 488 489 config := interfaces.FilterQuery{ 490 Addresses: []common.Address{c.address}, 491 Topics: topics, 492 FromBlock: new(big.Int).SetUint64(opts.Start), 493 } 494 if opts.End != nil { 495 config.ToBlock = new(big.Int).SetUint64(*opts.End) 496 } 497 /* TODO(karalabe): Replace the rest of the method below with this when supported 498 sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs) 499 */ 500 buff, err := c.filterer.FilterLogs(ensureContext(opts.Context), config) 501 if err != nil { 502 return nil, nil, err 503 } 504 sub, err := event.NewSubscription(func(quit <-chan struct{}) error { 505 for _, log := range buff { 506 select { 507 case logs <- log: 508 case <-quit: 509 return nil 510 } 511 } 512 return nil 513 }), nil 514 515 if err != nil { 516 return nil, nil, err 517 } 518 return logs, sub, nil 519 } 520 521 // WatchLogs filters subscribes to contract logs for future blocks, returning a 522 // subscription object that can be used to tear down the watcher. 523 func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) { 524 // Don't crash on a lazy user 525 if opts == nil { 526 opts = new(WatchOpts) 527 } 528 // Append the event selector to the query parameters and construct the topic set 529 query = append([][]interface{}{{c.abi.Events[name].ID}}, query...) 530 531 topics, err := abi.MakeTopics(query...) 532 if err != nil { 533 return nil, nil, err 534 } 535 // Start the background filtering 536 logs := make(chan types.Log, 128) 537 538 config := interfaces.FilterQuery{ 539 Addresses: []common.Address{c.address}, 540 Topics: topics, 541 } 542 if opts.Start != nil { 543 config.FromBlock = new(big.Int).SetUint64(*opts.Start) 544 } 545 sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs) 546 if err != nil { 547 return nil, nil, err 548 } 549 return logs, sub, nil 550 } 551 552 // UnpackLog unpacks a retrieved log into the provided output structure. 553 func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error { 554 if log.Topics[0] != c.abi.Events[event].ID { 555 return fmt.Errorf("event signature mismatch") 556 } 557 if len(log.Data) > 0 { 558 if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil { 559 return err 560 } 561 } 562 var indexed abi.Arguments 563 for _, arg := range c.abi.Events[event].Inputs { 564 if arg.Indexed { 565 indexed = append(indexed, arg) 566 } 567 } 568 return abi.ParseTopics(out, indexed, log.Topics[1:]) 569 } 570 571 // UnpackLogIntoMap unpacks a retrieved log into the provided map. 572 func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error { 573 if log.Topics[0] != c.abi.Events[event].ID { 574 return fmt.Errorf("event signature mismatch") 575 } 576 if len(log.Data) > 0 { 577 if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil { 578 return err 579 } 580 } 581 var indexed abi.Arguments 582 for _, arg := range c.abi.Events[event].Inputs { 583 if arg.Indexed { 584 indexed = append(indexed, arg) 585 } 586 } 587 return abi.ParseTopicsIntoMap(out, indexed, log.Topics[1:]) 588 } 589 590 // ensureContext is a helper method to ensure a context is not nil, even if the 591 // user specified it as such. 592 func ensureContext(ctx context.Context) context.Context { 593 if ctx == nil { 594 return context.Background() 595 } 596 return ctx 597 }