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