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