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