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