github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/accounts/abi/bind/base.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 package bind 19 20 import ( 21 "context" 22 "errors" 23 "fmt" 24 "math/big" 25 26 "github.com/AigarNetwork/aigar" 27 "github.com/AigarNetwork/aigar/accounts/abi" 28 "github.com/AigarNetwork/aigar/common" 29 "github.com/AigarNetwork/aigar/core/types" 30 "github.com/AigarNetwork/aigar/crypto" 31 "github.com/AigarNetwork/aigar/event" 32 ) 33 34 // SignerFn is a signer function callback when a contract requires a method to 35 // sign the transaction before submission. 36 type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Transaction, error) 37 38 // CallOpts is the collection of options to fine tune a contract call request. 39 type CallOpts struct { 40 Pending bool // Whether to operate on the pending state or the last known one 41 From common.Address // Optional the sender address, otherwise the first account is used 42 BlockNumber *big.Int // Optional the block number on which the call should be performed 43 Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) 44 } 45 46 // TransactOpts is the collection of authorization data required to create a 47 // valid Ethereum transaction. 48 type TransactOpts struct { 49 From common.Address // Ethereum account to send the transaction from 50 Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state) 51 Signer SignerFn // Method to use for signing the transaction (mandatory) 52 53 Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds) 54 GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle) 55 GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate) 56 57 Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) 58 } 59 60 // FilterOpts is the collection of options to fine tune filtering for events 61 // within a bound contract. 62 type FilterOpts struct { 63 Start uint64 // Start of the queried range 64 End *uint64 // End of the range (nil = latest) 65 66 Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) 67 } 68 69 // WatchOpts is the collection of options to fine tune subscribing for events 70 // within a bound contract. 71 type WatchOpts struct { 72 Start *uint64 // Start of the queried range (nil = latest) 73 Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) 74 } 75 76 // BoundContract is the base wrapper object that reflects a contract on the 77 // Ethereum network. It contains a collection of methods that are used by the 78 // higher level contract bindings to operate. 79 type BoundContract struct { 80 address common.Address // Deployment address of the contract on the Ethereum blockchain 81 abi abi.ABI // Reflect based ABI to access the correct Ethereum methods 82 caller ContractCaller // Read interface to interact with the blockchain 83 transactor ContractTransactor // Write interface to interact with the blockchain 84 filterer ContractFilterer // Event filtering to interact with the blockchain 85 } 86 87 // NewBoundContract creates a low level contract interface through which calls 88 // and transactions may be made through. 89 func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, filterer ContractFilterer) *BoundContract { 90 return &BoundContract{ 91 address: address, 92 abi: abi, 93 caller: caller, 94 transactor: transactor, 95 filterer: filterer, 96 } 97 } 98 99 // DeployContract deploys a contract onto the Ethereum blockchain and binds the 100 // deployment address with a Go wrapper. 101 func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) { 102 // Otherwise try to deploy the contract 103 c := NewBoundContract(common.Address{}, abi, backend, backend, backend) 104 105 input, err := c.abi.Pack("", params...) 106 if err != nil { 107 return common.Address{}, nil, nil, err 108 } 109 tx, err := c.transact(opts, nil, append(bytecode, input...)) 110 if err != nil { 111 return common.Address{}, nil, nil, err 112 } 113 c.address = crypto.CreateAddress(opts.From, tx.Nonce()) 114 return c.address, tx, c, nil 115 } 116 117 // Call invokes the (constant) contract method with params as input values and 118 // sets the output to result. The result type might be a single field for simple 119 // returns, a slice of interfaces for anonymous returns and a struct for named 120 // returns. 121 func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, params ...interface{}) error { 122 // Don't crash on a lazy user 123 if opts == nil { 124 opts = new(CallOpts) 125 } 126 // Pack the input, call and unpack the results 127 input, err := c.abi.Pack(method, params...) 128 if err != nil { 129 return err 130 } 131 var ( 132 msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input} 133 ctx = ensureContext(opts.Context) 134 code []byte 135 output []byte 136 ) 137 if opts.Pending { 138 pb, ok := c.caller.(PendingContractCaller) 139 if !ok { 140 return ErrNoPendingState 141 } 142 output, err = pb.PendingCallContract(ctx, msg) 143 if err == nil && len(output) == 0 { 144 // Make sure we have a contract to operate on, and bail out otherwise. 145 if code, err = pb.PendingCodeAt(ctx, c.address); err != nil { 146 return err 147 } else if len(code) == 0 { 148 return ErrNoCode 149 } 150 } 151 } else { 152 output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber) 153 if err == nil && len(output) == 0 { 154 // Make sure we have a contract to operate on, and bail out otherwise. 155 if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil { 156 return err 157 } else if len(code) == 0 { 158 return ErrNoCode 159 } 160 } 161 } 162 if err != nil { 163 return err 164 } 165 return c.abi.Unpack(result, method, output) 166 } 167 168 // Transact invokes the (paid) contract method with params as input values. 169 func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 170 // Otherwise pack up the parameters and invoke the contract 171 input, err := c.abi.Pack(method, params...) 172 if err != nil { 173 return nil, err 174 } 175 return c.transact(opts, &c.address, input) 176 } 177 178 // Transfer initiates a plain transaction to move funds to the contract, calling 179 // its default method if one is available. 180 func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) { 181 return c.transact(opts, &c.address, nil) 182 } 183 184 // transact executes an actual transaction invocation, first deriving any missing 185 // authorization fields, and then scheduling the transaction for execution. 186 func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { 187 var err error 188 189 // Ensure a valid value field and resolve the account nonce 190 value := opts.Value 191 if value == nil { 192 value = new(big.Int) 193 } 194 var nonce uint64 195 if opts.Nonce == nil { 196 nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From) 197 if err != nil { 198 return nil, fmt.Errorf("failed to retrieve account nonce: %v", err) 199 } 200 } else { 201 nonce = opts.Nonce.Uint64() 202 } 203 // Figure out the gas allowance and gas price values 204 gasPrice := opts.GasPrice 205 if gasPrice == nil { 206 gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context)) 207 if err != nil { 208 return nil, fmt.Errorf("failed to suggest gas price: %v", err) 209 } 210 } 211 gasLimit := opts.GasLimit 212 if gasLimit == 0 { 213 // Gas estimation cannot succeed without code for method invocations 214 if contract != nil { 215 if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil { 216 return nil, err 217 } else if len(code) == 0 { 218 return nil, ErrNoCode 219 } 220 } 221 // If the contract surely has code (or code is not needed), estimate the transaction 222 msg := ethereum.CallMsg{From: opts.From, To: contract, GasPrice: gasPrice, Value: value, Data: input} 223 gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg) 224 if err != nil { 225 return nil, fmt.Errorf("failed to estimate gas needed: %v", err) 226 } 227 } 228 // Create the transaction, sign it and schedule it for execution 229 var rawTx *types.Transaction 230 if contract == nil { 231 rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input) 232 } else { 233 rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input) 234 } 235 if opts.Signer == nil { 236 return nil, errors.New("no signer to authorize the transaction with") 237 } 238 signedTx, err := opts.Signer(types.HomesteadSigner{}, opts.From, rawTx) 239 if err != nil { 240 return nil, err 241 } 242 if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil { 243 return nil, err 244 } 245 return signedTx, nil 246 } 247 248 // FilterLogs filters contract logs for past blocks, returning the necessary 249 // channels to construct a strongly typed bound iterator on top of them. 250 func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) { 251 // Don't crash on a lazy user 252 if opts == nil { 253 opts = new(FilterOpts) 254 } 255 // Append the event selector to the query parameters and construct the topic set 256 query = append([][]interface{}{{c.abi.Events[name].ID()}}, query...) 257 258 topics, err := makeTopics(query...) 259 if err != nil { 260 return nil, nil, err 261 } 262 // Start the background filtering 263 logs := make(chan types.Log, 128) 264 265 config := ethereum.FilterQuery{ 266 Addresses: []common.Address{c.address}, 267 Topics: topics, 268 FromBlock: new(big.Int).SetUint64(opts.Start), 269 } 270 if opts.End != nil { 271 config.ToBlock = new(big.Int).SetUint64(*opts.End) 272 } 273 /* TODO(karalabe): Replace the rest of the method below with this when supported 274 sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs) 275 */ 276 buff, err := c.filterer.FilterLogs(ensureContext(opts.Context), config) 277 if err != nil { 278 return nil, nil, err 279 } 280 sub, err := event.NewSubscription(func(quit <-chan struct{}) error { 281 for _, log := range buff { 282 select { 283 case logs <- log: 284 case <-quit: 285 return nil 286 } 287 } 288 return nil 289 }), nil 290 291 if err != nil { 292 return nil, nil, err 293 } 294 return logs, sub, nil 295 } 296 297 // WatchLogs filters subscribes to contract logs for future blocks, returning a 298 // subscription object that can be used to tear down the watcher. 299 func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) { 300 // Don't crash on a lazy user 301 if opts == nil { 302 opts = new(WatchOpts) 303 } 304 // Append the event selector to the query parameters and construct the topic set 305 query = append([][]interface{}{{c.abi.Events[name].ID()}}, query...) 306 307 topics, err := makeTopics(query...) 308 if err != nil { 309 return nil, nil, err 310 } 311 // Start the background filtering 312 logs := make(chan types.Log, 128) 313 314 config := ethereum.FilterQuery{ 315 Addresses: []common.Address{c.address}, 316 Topics: topics, 317 } 318 if opts.Start != nil { 319 config.FromBlock = new(big.Int).SetUint64(*opts.Start) 320 } 321 sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs) 322 if err != nil { 323 return nil, nil, err 324 } 325 return logs, sub, nil 326 } 327 328 // UnpackLog unpacks a retrieved log into the provided output structure. 329 func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error { 330 if len(log.Data) > 0 { 331 if err := c.abi.Unpack(out, event, log.Data); err != nil { 332 return err 333 } 334 } 335 var indexed abi.Arguments 336 for _, arg := range c.abi.Events[event].Inputs { 337 if arg.Indexed { 338 indexed = append(indexed, arg) 339 } 340 } 341 return parseTopics(out, indexed, log.Topics[1:]) 342 } 343 344 // UnpackLogIntoMap unpacks a retrieved log into the provided map. 345 func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error { 346 if len(log.Data) > 0 { 347 if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil { 348 return err 349 } 350 } 351 var indexed abi.Arguments 352 for _, arg := range c.abi.Events[event].Inputs { 353 if arg.Indexed { 354 indexed = append(indexed, arg) 355 } 356 } 357 return parseTopicsIntoMap(out, indexed, log.Topics[1:]) 358 } 359 360 // ensureContext is a helper method to ensure a context is not nil, even if the 361 // user specified it as such. 362 func ensureContext(ctx context.Context) context.Context { 363 if ctx == nil { 364 return context.TODO() 365 } 366 return ctx 367 }