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