github.com/fff-chain/go-fff@v0.0.0-20220726032732-1c84420b8a99/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/fff-chain/go-fff" 28 "github.com/fff-chain/go-fff/accounts/abi" 29 "github.com/fff-chain/go-fff/common" 30 "github.com/fff-chain/go-fff/core/types" 31 "github.com/fff-chain/go-fff/crypto" 32 "github.com/fff-chain/go-fff/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 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 the gas allowance and gas price values 229 gasPrice := opts.GasPrice 230 if gasPrice == nil { 231 gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context)) 232 if err != nil { 233 return nil, fmt.Errorf("failed to suggest gas price: %v", err) 234 } 235 } 236 gasLimit := opts.GasLimit 237 if gasLimit == 0 { 238 // Gas estimation cannot succeed without code for method invocations 239 if contract != nil { 240 if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil { 241 return nil, err 242 } else if len(code) == 0 { 243 return nil, ErrNoCode 244 } 245 } 246 // If the contract surely has code (or code is not needed), estimate the transaction 247 msg := ethereum.CallMsg{From: opts.From, To: contract, GasPrice: gasPrice, Value: value, Data: input} 248 gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg) 249 if err != nil { 250 return nil, fmt.Errorf("failed to estimate gas needed: %v", err) 251 } 252 } 253 // Create the transaction, sign it and schedule it for execution 254 var rawTx *types.Transaction 255 if contract == nil { 256 rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input) 257 } else { 258 rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input) 259 } 260 if opts.Signer == nil { 261 return nil, errors.New("no signer to authorize the transaction with") 262 } 263 signedTx, err := opts.Signer(opts.From, rawTx) 264 if err != nil { 265 return nil, err 266 } 267 if opts.NoSend { 268 return signedTx, nil 269 } 270 if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil { 271 return nil, err 272 } 273 return signedTx, nil 274 } 275 276 // FilterLogs filters contract logs for past blocks, returning the necessary 277 // channels to construct a strongly typed bound iterator on top of them. 278 func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) { 279 // Don't crash on a lazy user 280 if opts == nil { 281 opts = new(FilterOpts) 282 } 283 // Append the event selector to the query parameters and construct the topic set 284 query = append([][]interface{}{{c.abi.Events[name].ID}}, query...) 285 286 topics, err := abi.MakeTopics(query...) 287 if err != nil { 288 return nil, nil, err 289 } 290 // Start the background filtering 291 logs := make(chan types.Log, 128) 292 293 config := ethereum.FilterQuery{ 294 Addresses: []common.Address{c.address}, 295 Topics: topics, 296 FromBlock: new(big.Int).SetUint64(opts.Start), 297 } 298 if opts.End != nil { 299 config.ToBlock = new(big.Int).SetUint64(*opts.End) 300 } 301 /* TODO(karalabe): Replace the rest of the method below with this when supported 302 sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs) 303 */ 304 buff, err := c.filterer.FilterLogs(ensureContext(opts.Context), config) 305 if err != nil { 306 return nil, nil, err 307 } 308 sub, err := event.NewSubscription(func(quit <-chan struct{}) error { 309 for _, log := range buff { 310 select { 311 case logs <- log: 312 case <-quit: 313 return nil 314 } 315 } 316 return nil 317 }), nil 318 319 if err != nil { 320 return nil, nil, err 321 } 322 return logs, sub, nil 323 } 324 325 // WatchLogs filters subscribes to contract logs for future blocks, returning a 326 // subscription object that can be used to tear down the watcher. 327 func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) { 328 // Don't crash on a lazy user 329 if opts == nil { 330 opts = new(WatchOpts) 331 } 332 // Append the event selector to the query parameters and construct the topic set 333 query = append([][]interface{}{{c.abi.Events[name].ID}}, query...) 334 335 topics, err := abi.MakeTopics(query...) 336 if err != nil { 337 return nil, nil, err 338 } 339 // Start the background filtering 340 logs := make(chan types.Log, 128) 341 342 config := ethereum.FilterQuery{ 343 Addresses: []common.Address{c.address}, 344 Topics: topics, 345 } 346 if opts.Start != nil { 347 config.FromBlock = new(big.Int).SetUint64(*opts.Start) 348 } 349 sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs) 350 if err != nil { 351 return nil, nil, err 352 } 353 return logs, sub, nil 354 } 355 356 // UnpackLog unpacks a retrieved log into the provided output structure. 357 func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error { 358 if len(log.Data) > 0 { 359 if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil { 360 return err 361 } 362 } 363 var indexed abi.Arguments 364 for _, arg := range c.abi.Events[event].Inputs { 365 if arg.Indexed { 366 indexed = append(indexed, arg) 367 } 368 } 369 return abi.ParseTopics(out, indexed, log.Topics[1:]) 370 } 371 372 // UnpackLogIntoMap unpacks a retrieved log into the provided map. 373 func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error { 374 if len(log.Data) > 0 { 375 if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil { 376 return err 377 } 378 } 379 var indexed abi.Arguments 380 for _, arg := range c.abi.Events[event].Inputs { 381 if arg.Indexed { 382 indexed = append(indexed, arg) 383 } 384 } 385 return abi.ParseTopicsIntoMap(out, indexed, log.Topics[1:]) 386 } 387 388 // ensureContext is a helper method to ensure a context is not nil, even if the 389 // user specified it as such. 390 func ensureContext(ctx context.Context) context.Context { 391 if ctx == nil { 392 return context.TODO() 393 } 394 return ctx 395 } 396 397 // MetaData collects all metadata for a bound contract. 398 type MetaData struct { 399 mu sync.Mutex 400 Sigs map[string]string 401 Bin string 402 ABI string 403 ab *abi.ABI 404 } 405 406 func (m *MetaData) GetAbi() (*abi.ABI, error) { 407 m.mu.Lock() 408 defer m.mu.Unlock() 409 if m.ab != nil { 410 return m.ab, nil 411 } 412 if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil { 413 return nil, err 414 } else { 415 m.ab = &parsed 416 } 417 return m.ab, nil 418 }