diff --git a/accounts/abi/bind/auth.go b/accounts/abi/bind/auth.go
new file mode 100644
index 0000000000..91913ec3b2
--- /dev/null
+++ b/accounts/abi/bind/auth.go
@@ -0,0 +1,179 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package bind
+
+import (
+ "context"
+ "crypto/ecdsa"
+ "errors"
+ "io"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/external"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// ErrNoChainID is returned whenever the user failed to specify a chain id.
+var ErrNoChainID = errors.New("no chain id specified")
+
+// ErrNotAuthorized is returned when an account is not properly unlocked.
+var ErrNotAuthorized = errors.New("not authorized to sign this account")
+
+// NewTransactor is a utility method to easily create a transaction signer from
+// an encrypted json key stream and the associated passphrase.
+//
+// Deprecated: Use NewTransactorWithChainID instead.
+func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
+ log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID")
+ json, err := io.ReadAll(keyin)
+ if err != nil {
+ return nil, err
+ }
+ key, err := keystore.DecryptKey(json, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ return NewKeyedTransactor(key.PrivateKey), nil
+}
+
+// NewKeyStoreTransactor is a utility method to easily create a transaction signer from
+// a decrypted key from a keystore.
+//
+// Deprecated: Use NewKeyStoreTransactorWithChainID instead.
+func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) {
+ log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID")
+ signer := types.HomesteadSigner{}
+ return &TransactOpts{
+ From: account.Address,
+ Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
+ if address != account.Address {
+ return nil, ErrNotAuthorized
+ }
+ signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes())
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(signer, signature)
+ },
+ Context: context.Background(),
+ }, nil
+}
+
+// NewKeyedTransactor is a utility method to easily create a transaction signer
+// from a single private key.
+//
+// Deprecated: Use NewKeyedTransactorWithChainID instead.
+func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
+ log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID")
+ keyAddr := crypto.PubkeyToAddress(key.PublicKey)
+ signer := types.HomesteadSigner{}
+ return &TransactOpts{
+ From: keyAddr,
+ Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
+ if address != keyAddr {
+ return nil, ErrNotAuthorized
+ }
+ signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key)
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(signer, signature)
+ },
+ Context: context.Background(),
+ }
+}
+
+// NewTransactorWithChainID is a utility method to easily create a transaction signer from
+// an encrypted json key stream and the associated passphrase.
+func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) {
+ json, err := io.ReadAll(keyin)
+ if err != nil {
+ return nil, err
+ }
+ key, err := keystore.DecryptKey(json, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ return NewKeyedTransactorWithChainID(key.PrivateKey, chainID)
+}
+
+// NewKeyStoreTransactorWithChainID is a utility method to easily create a transaction signer from
+// an decrypted key from a keystore.
+func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accounts.Account, chainID *big.Int) (*TransactOpts, error) {
+ if chainID == nil {
+ return nil, ErrNoChainID
+ }
+ signer := types.LatestSignerForChainID(chainID)
+ return &TransactOpts{
+ From: account.Address,
+ Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
+ if address != account.Address {
+ return nil, ErrNotAuthorized
+ }
+ signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes())
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(signer, signature)
+ },
+ Context: context.Background(),
+ }, nil
+}
+
+// NewKeyedTransactorWithChainID is a utility method to easily create a transaction signer
+// from a single private key.
+func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) {
+ keyAddr := crypto.PubkeyToAddress(key.PublicKey)
+ if chainID == nil {
+ return nil, ErrNoChainID
+ }
+ signer := types.LatestSignerForChainID(chainID)
+ return &TransactOpts{
+ From: keyAddr,
+ Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
+ if address != keyAddr {
+ return nil, ErrNotAuthorized
+ }
+ signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key)
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(signer, signature)
+ },
+ Context: context.Background(),
+ }, nil
+}
+
+// NewClefTransactor is a utility method to easily create a transaction signer
+// with a clef backend.
+func NewClefTransactor(clef *external.ExternalSigner, account accounts.Account) *TransactOpts {
+ return &TransactOpts{
+ From: account.Address,
+ Signer: func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) {
+ if address != account.Address {
+ return nil, ErrNotAuthorized
+ }
+ return clef.SignTx(account, transaction, nil) // Clef enforces its own chain id
+ },
+ Context: context.Background(),
+ }
+}
diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go
new file mode 100644
index 0000000000..2e45e86ae2
--- /dev/null
+++ b/accounts/abi/bind/backend.go
@@ -0,0 +1,141 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package bind
+
+import (
+ "context"
+ "errors"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+)
+
+var (
+ // ErrNoCode is returned by call and transact operations for which the requested
+ // recipient contract to operate on does not exist in the state db or does not
+ // have any code associated with it (i.e. self-destructed).
+ ErrNoCode = errors.New("no contract code at given address")
+
+ // ErrNoPendingState is raised when attempting to perform a pending state action
+ // on a backend that doesn't implement PendingContractCaller.
+ ErrNoPendingState = errors.New("backend does not support pending state")
+
+ // ErrNoBlockHashState is raised when attempting to perform a block hash action
+ // on a backend that doesn't implement BlockHashContractCaller.
+ ErrNoBlockHashState = errors.New("backend does not support block hash state")
+
+ // ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves
+ // an empty contract behind.
+ ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
+)
+
+// ContractCaller defines the methods needed to allow operating with a contract on a read
+// only basis.
+type ContractCaller interface {
+ // CodeAt returns the code of the given account. This is needed to differentiate
+ // between contract internal errors and the local chain being out of sync.
+ CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)
+
+ // CallContract executes an Ethereum contract call with the specified data as the
+ // input.
+ CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
+}
+
+// PendingContractCaller defines methods to perform contract calls on the pending state.
+// Call will try to discover this interface when access to the pending state is requested.
+// If the backend does not support the pending state, Call returns ErrNoPendingState.
+type PendingContractCaller interface {
+ // PendingCodeAt returns the code of the given account in the pending state.
+ PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error)
+
+ // PendingCallContract executes an Ethereum contract call against the pending state.
+ PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error)
+}
+
+// BlockHashContractCaller defines methods to perform contract calls on a specific block hash.
+// Call will try to discover this interface when access to a block by hash is requested.
+// If the backend does not support the block hash state, Call returns ErrNoBlockHashState.
+type BlockHashContractCaller interface {
+ // CodeAtHash returns the code of the given account in the state at the specified block hash.
+ CodeAtHash(ctx context.Context, contract common.Address, blockHash common.Hash) ([]byte, error)
+
+ // CallContractAtHash executes an Ethereum contract call against the state at the specified block hash.
+ CallContractAtHash(ctx context.Context, call ethereum.CallMsg, blockHash common.Hash) ([]byte, error)
+}
+
+// ContractTransactor defines the methods needed to allow operating with a contract
+// on a write only basis. Besides the transacting method, the remainder are helpers
+// used when the user does not provide some needed values, but rather leaves it up
+// to the transactor to decide.
+type ContractTransactor interface {
+ // HeaderByNumber returns a block header from the current canonical chain. If
+ // number is nil, the latest known header is returned.
+ HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
+
+ // PendingCodeAt returns the code of the given account in the pending state.
+ PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
+
+ // PendingNonceAt retrieves the current pending nonce associated with an account.
+ PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
+
+ // SuggestGasPrice retrieves the currently suggested gas price to allow a timely
+ // execution of a transaction.
+ SuggestGasPrice(ctx context.Context) (*big.Int, error)
+
+ // SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow
+ // a timely execution of a transaction.
+ SuggestGasTipCap(ctx context.Context) (*big.Int, error)
+
+ // EstimateGas tries to estimate the gas needed to execute a specific
+ // transaction based on the current pending state of the backend blockchain.
+ // There is no guarantee that this is the true gas limit requirement as other
+ // transactions may be added or removed by miners, but it should provide a basis
+ // for setting a reasonable default.
+ EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error)
+
+ // SendTransaction injects the transaction into the pending pool for execution.
+ SendTransaction(ctx context.Context, tx *types.Transaction) error
+}
+
+// ContractFilterer defines the methods needed to access log events using one-off
+// queries or continuous event subscriptions.
+type ContractFilterer interface {
+ // FilterLogs executes a log filter operation, blocking during execution and
+ // returning all the results in one batch.
+ //
+ // TODO(karalabe): Deprecate when the subscription one can return past data too.
+ FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error)
+
+ // SubscribeFilterLogs creates a background log filtering operation, returning
+ // a subscription immediately, which can be used to stream the found events.
+ SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error)
+}
+
+// DeployBackend wraps the operations needed by WaitMined and WaitDeployed.
+type DeployBackend interface {
+ TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
+ CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error)
+}
+
+// ContractBackend defines the methods needed to work with contracts on a read-write basis.
+type ContractBackend interface {
+ ContractCaller
+ ContractTransactor
+ ContractFilterer
+}
diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go
new file mode 100644
index 0000000000..6da15f147c
--- /dev/null
+++ b/accounts/abi/bind/base.go
@@ -0,0 +1,565 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package bind
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math/big"
+ "strings"
+ "sync"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/event"
+)
+
+const basefeeWiggleMultiplier = 2
+
+var (
+ errNoEventSignature = errors.New("no event signature")
+ errEventSignatureMismatch = errors.New("event signature mismatch")
+)
+
+// SignerFn is a signer function callback when a contract requires a method to
+// sign the transaction before submission.
+type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error)
+
+// CallOpts is the collection of options to fine tune a contract call request.
+type CallOpts struct {
+ Pending bool // Whether to operate on the pending state or the last known one
+ From common.Address // Optional the sender address, otherwise the first account is used
+ BlockNumber *big.Int // Optional the block number on which the call should be performed
+ BlockHash common.Hash // Optional the block hash on which the call should be performed
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+}
+
+// TransactOpts is the collection of authorization data required to create a
+// valid Ethereum transaction.
+type TransactOpts struct {
+ From common.Address // Ethereum account to send the transaction from
+ Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
+ Signer SignerFn // Method to use for signing the transaction (mandatory)
+
+ Value *big.Int // Funds to transfer along the transaction (nil = 0 = no funds)
+ GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
+ GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle)
+ GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle)
+ GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
+
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+
+ NoSend bool // Do all transact steps but do not send the transaction
+}
+
+// FilterOpts is the collection of options to fine tune filtering for events
+// within a bound contract.
+type FilterOpts struct {
+ Start uint64 // Start of the queried range
+ End *uint64 // End of the range (nil = latest)
+
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+}
+
+// WatchOpts is the collection of options to fine tune subscribing for events
+// within a bound contract.
+type WatchOpts struct {
+ Start *uint64 // Start of the queried range (nil = latest)
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+}
+
+// MetaData collects all metadata for a bound contract.
+type MetaData struct {
+ mu sync.Mutex
+ Sigs map[string]string
+ Bin string
+ ABI string
+ ab *abi.ABI
+}
+
+func (m *MetaData) GetAbi() (*abi.ABI, error) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if m.ab != nil {
+ return m.ab, nil
+ }
+ if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil {
+ return nil, err
+ } else {
+ m.ab = &parsed
+ }
+ return m.ab, nil
+}
+
+// BoundContract is the base wrapper object that reflects a contract on the
+// Ethereum network. It contains a collection of methods that are used by the
+// higher level contract bindings to operate.
+type BoundContract struct {
+ address common.Address // Deployment address of the contract on the Ethereum blockchain
+ abi abi.ABI // Reflect based ABI to access the correct Ethereum methods
+ caller ContractCaller // Read interface to interact with the blockchain
+ transactor ContractTransactor // Write interface to interact with the blockchain
+ filterer ContractFilterer // Event filtering to interact with the blockchain
+}
+
+// NewBoundContract creates a low level contract interface through which calls
+// and transactions may be made through.
+func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, filterer ContractFilterer) *BoundContract {
+ return &BoundContract{
+ address: address,
+ abi: abi,
+ caller: caller,
+ transactor: transactor,
+ filterer: filterer,
+ }
+}
+
+// DeployContract deploys a contract onto the Ethereum blockchain and binds the
+// deployment address with a Go wrapper.
+func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) {
+ // Otherwise try to deploy the contract
+ c := NewBoundContract(common.Address{}, abi, backend, backend, backend)
+
+ input, err := c.abi.Pack("", params...)
+ if err != nil {
+ return common.Address{}, nil, nil, err
+ }
+ tx, err := c.transact(opts, nil, append(bytecode, input...))
+ if err != nil {
+ return common.Address{}, nil, nil, err
+ }
+ c.address = crypto.CreateAddress(opts.From, tx.Nonce())
+ return c.address, tx, c, nil
+}
+
+// Call invokes the (constant) contract method with params as input values and
+// sets the output to result. The result type might be a single field for simple
+// returns, a slice of interfaces for anonymous returns and a struct for named
+// returns.
+func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error {
+ // Don't crash on a lazy user
+ if opts == nil {
+ opts = new(CallOpts)
+ }
+ if results == nil {
+ results = new([]interface{})
+ }
+ // Pack the input, call and unpack the results
+ input, err := c.abi.Pack(method, params...)
+ if err != nil {
+ return err
+ }
+ var (
+ msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
+ ctx = ensureContext(opts.Context)
+ code []byte
+ output []byte
+ )
+ if opts.Pending {
+ pb, ok := c.caller.(PendingContractCaller)
+ if !ok {
+ return ErrNoPendingState
+ }
+ output, err = pb.PendingCallContract(ctx, msg)
+ if err != nil {
+ return err
+ }
+ if len(output) == 0 {
+ // Make sure we have a contract to operate on, and bail out otherwise.
+ if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
+ return err
+ } else if len(code) == 0 {
+ return ErrNoCode
+ }
+ }
+ } else if opts.BlockHash != (common.Hash{}) {
+ bh, ok := c.caller.(BlockHashContractCaller)
+ if !ok {
+ return ErrNoBlockHashState
+ }
+ output, err = bh.CallContractAtHash(ctx, msg, opts.BlockHash)
+ if err != nil {
+ return err
+ }
+ if len(output) == 0 {
+ // Make sure we have a contract to operate on, and bail out otherwise.
+ if code, err = bh.CodeAtHash(ctx, c.address, opts.BlockHash); err != nil {
+ return err
+ } else if len(code) == 0 {
+ return ErrNoCode
+ }
+ }
+ } else {
+ output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
+ if err != nil {
+ return err
+ }
+ if len(output) == 0 {
+ // Make sure we have a contract to operate on, and bail out otherwise.
+ if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
+ return err
+ } else if len(code) == 0 {
+ return ErrNoCode
+ }
+ }
+ }
+
+ if len(*results) == 0 {
+ res, err := c.abi.Unpack(method, output)
+ *results = res
+ return err
+ }
+ res := *results
+ return c.abi.UnpackIntoInterface(res[0], method, output)
+}
+
+// Transact invokes the (paid) contract method with params as input values.
+func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ // Otherwise pack up the parameters and invoke the contract
+ input, err := c.abi.Pack(method, params...)
+ if err != nil {
+ return nil, err
+ }
+ // todo(rjl493456442) check the method is payable or not,
+ // reject invalid transaction at the first place
+ return c.transact(opts, &c.address, input)
+}
+
+// RawTransact initiates a transaction with the given raw calldata as the input.
+// It's usually used to initiate transactions for invoking **Fallback** function.
+func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) {
+ // todo(rjl493456442) check the method is payable or not,
+ // reject invalid transaction at the first place
+ return c.transact(opts, &c.address, calldata)
+}
+
+// Transfer initiates a plain transaction to move funds to the contract, calling
+// its default method if one is available.
+func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) {
+ // todo(rjl493456442) check the payable fallback or receive is defined
+ // or not, reject invalid transaction at the first place
+ return c.transact(opts, &c.address, nil)
+}
+
+func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Address, input []byte, head *types.Header) (*types.Transaction, error) {
+ // Normalize value
+ value := opts.Value
+ if value == nil {
+ value = new(big.Int)
+ }
+ // Estimate TipCap
+ gasTipCap := opts.GasTipCap
+ if gasTipCap == nil {
+ tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context))
+ if err != nil {
+ return nil, err
+ }
+ gasTipCap = tip
+ }
+ // Estimate FeeCap
+ gasFeeCap := opts.GasFeeCap
+ if gasFeeCap == nil {
+ gasFeeCap = new(big.Int).Add(
+ gasTipCap,
+ new(big.Int).Mul(head.BaseFee, big.NewInt(basefeeWiggleMultiplier)),
+ )
+ }
+ if gasFeeCap.Cmp(gasTipCap) < 0 {
+ return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
+ }
+ // Estimate GasLimit
+ gasLimit := opts.GasLimit
+ if opts.GasLimit == 0 {
+ var err error
+ gasLimit, err = c.estimateGasLimit(opts, contract, input, nil, gasTipCap, gasFeeCap, value)
+ if err != nil {
+ return nil, err
+ }
+ }
+ // create the transaction
+ nonce, err := c.getNonce(opts)
+ if err != nil {
+ return nil, err
+ }
+ baseTx := &types.DynamicFeeTx{
+ To: contract,
+ Nonce: nonce,
+ GasFeeCap: gasFeeCap,
+ GasTipCap: gasTipCap,
+ Gas: gasLimit,
+ Value: value,
+ Data: input,
+ }
+ return types.NewTx(baseTx), nil
+}
+
+func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
+ if opts.GasFeeCap != nil || opts.GasTipCap != nil {
+ return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
+ }
+ // Normalize value
+ value := opts.Value
+ if value == nil {
+ value = new(big.Int)
+ }
+ // Estimate GasPrice
+ gasPrice := opts.GasPrice
+ if gasPrice == nil {
+ price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
+ if err != nil {
+ return nil, err
+ }
+ gasPrice = price
+ }
+ // Estimate GasLimit
+ gasLimit := opts.GasLimit
+ if opts.GasLimit == 0 {
+ var err error
+ gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
+ if err != nil {
+ return nil, err
+ }
+ }
+ // create the transaction
+ nonce, err := c.getNonce(opts)
+ if err != nil {
+ return nil, err
+ }
+ baseTx := &types.LegacyTx{
+ To: contract,
+ Nonce: nonce,
+ GasPrice: gasPrice,
+ Gas: gasLimit,
+ Value: value,
+ Data: input,
+ }
+ return types.NewTx(baseTx), nil
+}
+
+func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Address, input []byte, gasPrice, gasTipCap, gasFeeCap, value *big.Int) (uint64, error) {
+ if contract != nil {
+ // Gas estimation cannot succeed without code for method invocations.
+ if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
+ return 0, err
+ } else if len(code) == 0 {
+ return 0, ErrNoCode
+ }
+ }
+ msg := ethereum.CallMsg{
+ From: opts.From,
+ To: contract,
+ GasPrice: gasPrice,
+ GasTipCap: gasTipCap,
+ GasFeeCap: gasFeeCap,
+ Value: value,
+ Data: input,
+ }
+ return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
+}
+
+func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) {
+ if opts.Nonce == nil {
+ return c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
+ } else {
+ return opts.Nonce.Uint64(), nil
+ }
+}
+
+// transact executes an actual transaction invocation, first deriving any missing
+// authorization fields, and then scheduling the transaction for execution.
+func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
+ if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
+ return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
+ }
+ // Create the transaction
+ var (
+ rawTx *types.Transaction
+ err error
+ )
+ if opts.GasPrice != nil {
+ rawTx, err = c.createLegacyTx(opts, contract, input)
+ } else if opts.GasFeeCap != nil && opts.GasTipCap != nil {
+ rawTx, err = c.createDynamicTx(opts, contract, input, nil)
+ } else {
+ // Only query for basefee if gasPrice not specified
+ if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); errHead != nil {
+ return nil, errHead
+ } else if head.BaseFee != nil {
+ rawTx, err = c.createDynamicTx(opts, contract, input, head)
+ } else {
+ // Chain is not London ready -> use legacy transaction
+ rawTx, err = c.createLegacyTx(opts, contract, input)
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ // Sign the transaction and schedule it for execution
+ if opts.Signer == nil {
+ return nil, errors.New("no signer to authorize the transaction with")
+ }
+ signedTx, err := opts.Signer(opts.From, rawTx)
+ if err != nil {
+ return nil, err
+ }
+ if opts.NoSend {
+ return signedTx, nil
+ }
+ if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
+ return nil, err
+ }
+ return signedTx, nil
+}
+
+// FilterLogs filters contract logs for past blocks, returning the necessary
+// channels to construct a strongly typed bound iterator on top of them.
+func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
+ // Don't crash on a lazy user
+ if opts == nil {
+ opts = new(FilterOpts)
+ }
+ // Append the event selector to the query parameters and construct the topic set
+ query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
+
+ topics, err := abi.MakeTopics(query...)
+ if err != nil {
+ return nil, nil, err
+ }
+ // Start the background filtering
+ logs := make(chan types.Log, 128)
+
+ config := ethereum.FilterQuery{
+ Addresses: []common.Address{c.address},
+ Topics: topics,
+ FromBlock: new(big.Int).SetUint64(opts.Start),
+ }
+ if opts.End != nil {
+ config.ToBlock = new(big.Int).SetUint64(*opts.End)
+ }
+ /* TODO(karalabe): Replace the rest of the method below with this when supported
+ sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
+ */
+ buff, err := c.filterer.FilterLogs(ensureContext(opts.Context), config)
+ if err != nil {
+ return nil, nil, err
+ }
+ sub, err := event.NewSubscription(func(quit <-chan struct{}) error {
+ for _, log := range buff {
+ select {
+ case logs <- log:
+ case <-quit:
+ return nil
+ }
+ }
+ return nil
+ }), nil
+
+ if err != nil {
+ return nil, nil, err
+ }
+ return logs, sub, nil
+}
+
+// WatchLogs filters subscribes to contract logs for future blocks, returning a
+// subscription object that can be used to tear down the watcher.
+func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
+ // Don't crash on a lazy user
+ if opts == nil {
+ opts = new(WatchOpts)
+ }
+ // Append the event selector to the query parameters and construct the topic set
+ query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
+
+ topics, err := abi.MakeTopics(query...)
+ if err != nil {
+ return nil, nil, err
+ }
+ // Start the background filtering
+ logs := make(chan types.Log, 128)
+
+ config := ethereum.FilterQuery{
+ Addresses: []common.Address{c.address},
+ Topics: topics,
+ }
+ if opts.Start != nil {
+ config.FromBlock = new(big.Int).SetUint64(*opts.Start)
+ }
+ sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
+ if err != nil {
+ return nil, nil, err
+ }
+ return logs, sub, nil
+}
+
+// UnpackLog unpacks a retrieved log into the provided output structure.
+func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
+ // Anonymous events are not supported.
+ if len(log.Topics) == 0 {
+ return errNoEventSignature
+ }
+ if log.Topics[0] != c.abi.Events[event].ID {
+ return errEventSignatureMismatch
+ }
+ if len(log.Data) > 0 {
+ if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
+ return err
+ }
+ }
+ var indexed abi.Arguments
+ for _, arg := range c.abi.Events[event].Inputs {
+ if arg.Indexed {
+ indexed = append(indexed, arg)
+ }
+ }
+ return abi.ParseTopics(out, indexed, log.Topics[1:])
+}
+
+// UnpackLogIntoMap unpacks a retrieved log into the provided map.
+func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error {
+ // Anonymous events are not supported.
+ if len(log.Topics) == 0 {
+ return errNoEventSignature
+ }
+ if log.Topics[0] != c.abi.Events[event].ID {
+ return errEventSignatureMismatch
+ }
+ if len(log.Data) > 0 {
+ if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
+ return err
+ }
+ }
+ var indexed abi.Arguments
+ for _, arg := range c.abi.Events[event].Inputs {
+ if arg.Indexed {
+ indexed = append(indexed, arg)
+ }
+ }
+ return abi.ParseTopicsIntoMap(out, indexed, log.Topics[1:])
+}
+
+// ensureContext is a helper method to ensure a context is not nil, even if the
+// user specified it as such.
+func ensureContext(ctx context.Context) context.Context {
+ if ctx == nil {
+ return context.Background()
+ }
+ return ctx
+}
diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go
new file mode 100644
index 0000000000..f7eb7d14d3
--- /dev/null
+++ b/accounts/abi/bind/base_test.go
@@ -0,0 +1,589 @@
+// Copyright 2019 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package bind_test
+
+import (
+ "context"
+ "errors"
+ "math/big"
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/stretchr/testify/assert"
+)
+
+func mockSign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { return tx, nil }
+
+type mockTransactor struct {
+ baseFee *big.Int
+ gasTipCap *big.Int
+ gasPrice *big.Int
+ suggestGasTipCapCalled bool
+ suggestGasPriceCalled bool
+}
+
+func (mt *mockTransactor) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
+ return &types.Header{BaseFee: mt.baseFee}, nil
+}
+
+func (mt *mockTransactor) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
+ return []byte{1}, nil
+}
+
+func (mt *mockTransactor) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
+ return 0, nil
+}
+
+func (mt *mockTransactor) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
+ mt.suggestGasPriceCalled = true
+ return mt.gasPrice, nil
+}
+
+func (mt *mockTransactor) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
+ mt.suggestGasTipCapCalled = true
+ return mt.gasTipCap, nil
+}
+
+func (mt *mockTransactor) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) {
+ return 0, nil
+}
+
+func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transaction) error {
+ return nil
+}
+
+type mockCaller struct {
+ codeAtBlockNumber *big.Int
+ callContractBlockNumber *big.Int
+ callContractBytes []byte
+ callContractErr error
+ codeAtBytes []byte
+ codeAtErr error
+}
+
+func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
+ mc.codeAtBlockNumber = blockNumber
+ return mc.codeAtBytes, mc.codeAtErr
+}
+
+func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
+ mc.callContractBlockNumber = blockNumber
+ return mc.callContractBytes, mc.callContractErr
+}
+
+type mockPendingCaller struct {
+ *mockCaller
+ pendingCodeAtBytes []byte
+ pendingCodeAtErr error
+ pendingCodeAtCalled bool
+ pendingCallContractCalled bool
+ pendingCallContractBytes []byte
+ pendingCallContractErr error
+}
+
+func (mc *mockPendingCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
+ mc.pendingCodeAtCalled = true
+ return mc.pendingCodeAtBytes, mc.pendingCodeAtErr
+}
+
+func (mc *mockPendingCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
+ mc.pendingCallContractCalled = true
+ return mc.pendingCallContractBytes, mc.pendingCallContractErr
+}
+
+type mockBlockHashCaller struct {
+ *mockCaller
+ codeAtHashBytes []byte
+ codeAtHashErr error
+ codeAtHashCalled bool
+ callContractAtHashCalled bool
+ callContractAtHashBytes []byte
+ callContractAtHashErr error
+}
+
+func (mc *mockBlockHashCaller) CodeAtHash(ctx context.Context, contract common.Address, hash common.Hash) ([]byte, error) {
+ mc.codeAtHashCalled = true
+ return mc.codeAtHashBytes, mc.codeAtHashErr
+}
+
+func (mc *mockBlockHashCaller) CallContractAtHash(ctx context.Context, call ethereum.CallMsg, hash common.Hash) ([]byte, error) {
+ mc.callContractAtHashCalled = true
+ return mc.callContractAtHashBytes, mc.callContractAtHashErr
+}
+
+func TestPassingBlockNumber(t *testing.T) {
+ t.Parallel()
+ mc := &mockPendingCaller{
+ mockCaller: &mockCaller{
+ codeAtBytes: []byte{1, 2, 3},
+ },
+ }
+
+ bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
+ Methods: map[string]abi.Method{
+ "something": {
+ Name: "something",
+ Outputs: abi.Arguments{},
+ },
+ },
+ }, mc, nil, nil)
+
+ blockNumber := big.NewInt(42)
+
+ bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, nil, "something")
+
+ if mc.callContractBlockNumber != blockNumber {
+ t.Fatalf("CallContract() was not passed the block number")
+ }
+
+ if mc.codeAtBlockNumber != blockNumber {
+ t.Fatalf("CodeAt() was not passed the block number")
+ }
+
+ bc.Call(&bind.CallOpts{}, nil, "something")
+
+ if mc.callContractBlockNumber != nil {
+ t.Fatalf("CallContract() was passed a block number when it should not have been")
+ }
+
+ if mc.codeAtBlockNumber != nil {
+ t.Fatalf("CodeAt() was passed a block number when it should not have been")
+ }
+
+ bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, nil, "something")
+
+ if !mc.pendingCallContractCalled {
+ t.Fatalf("CallContract() was not passed the block number")
+ }
+
+ if !mc.pendingCodeAtCalled {
+ t.Fatalf("CodeAt() was not passed the block number")
+ }
+}
+
+const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158"
+
+func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
+ t.Parallel()
+ hash := crypto.Keccak256Hash([]byte("testName"))
+ topics := []common.Hash{
+ crypto.Keccak256Hash([]byte("received(string,address,uint256,bytes)")),
+ hash,
+ }
+ mockLog := newMockLog(topics, common.HexToHash("0x0"))
+
+ abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
+ parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
+ bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
+
+ expectedReceivedMap := map[string]interface{}{
+ "name": hash,
+ "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
+ "amount": big.NewInt(1),
+ "memo": []byte{88},
+ }
+ unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
+}
+
+func TestUnpackAnonymousLogIntoMap(t *testing.T) {
+ t.Parallel()
+ mockLog := newMockLog(nil, common.HexToHash("0x0"))
+
+ abiString := `[{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"received","type":"event"}]`
+ parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
+ bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
+
+ var received map[string]interface{}
+ err := bc.UnpackLogIntoMap(received, "received", mockLog)
+ if err == nil {
+ t.Error("unpacking anonymous event is not supported")
+ }
+ if err.Error() != "no event signature" {
+ t.Errorf("expected error 'no event signature', got '%s'", err)
+ }
+}
+
+func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
+ t.Parallel()
+ sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"})
+ if err != nil {
+ t.Fatal(err)
+ }
+ hash := crypto.Keccak256Hash(sliceBytes)
+ topics := []common.Hash{
+ crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")),
+ hash,
+ }
+ mockLog := newMockLog(topics, common.HexToHash("0x0"))
+
+ abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"names","type":"string[]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
+ parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
+ bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
+
+ expectedReceivedMap := map[string]interface{}{
+ "names": hash,
+ "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
+ "amount": big.NewInt(1),
+ "memo": []byte{88},
+ }
+ unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
+}
+
+func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
+ t.Parallel()
+ arrBytes, err := rlp.EncodeToBytes([2]common.Address{common.HexToAddress("0x0"), common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")})
+ if err != nil {
+ t.Fatal(err)
+ }
+ hash := crypto.Keccak256Hash(arrBytes)
+ topics := []common.Hash{
+ crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")),
+ hash,
+ }
+ mockLog := newMockLog(topics, common.HexToHash("0x0"))
+
+ abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"addresses","type":"address[2]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
+ parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
+ bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
+
+ expectedReceivedMap := map[string]interface{}{
+ "addresses": hash,
+ "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
+ "amount": big.NewInt(1),
+ "memo": []byte{88},
+ }
+ unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
+}
+
+func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
+ t.Parallel()
+ mockAddress := common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")
+ addrBytes := mockAddress.Bytes()
+ hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)"))
+ functionSelector := hash[:4]
+ functionTyBytes := append(addrBytes, functionSelector...)
+ var functionTy [24]byte
+ copy(functionTy[:], functionTyBytes[0:24])
+ topics := []common.Hash{
+ crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")),
+ common.BytesToHash(functionTyBytes),
+ }
+ mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
+ abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"function","type":"function"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
+ parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
+ bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
+
+ expectedReceivedMap := map[string]interface{}{
+ "function": functionTy,
+ "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
+ "amount": big.NewInt(1),
+ "memo": []byte{88},
+ }
+ unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
+}
+
+func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
+ t.Parallel()
+ bytes := []byte{1, 2, 3, 4, 5}
+ hash := crypto.Keccak256Hash(bytes)
+ topics := []common.Hash{
+ crypto.Keccak256Hash([]byte("received(bytes,address,uint256,bytes)")),
+ hash,
+ }
+ mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
+
+ abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"content","type":"bytes"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
+ parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
+ bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
+
+ expectedReceivedMap := map[string]interface{}{
+ "content": hash,
+ "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
+ "amount": big.NewInt(1),
+ "memo": []byte{88},
+ }
+ unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
+}
+
+func TestTransactGasFee(t *testing.T) {
+ t.Parallel()
+ assert := assert.New(t)
+
+ // GasTipCap and GasFeeCap
+ // When opts.GasTipCap and opts.GasFeeCap are nil
+ mt := &mockTransactor{baseFee: big.NewInt(100), gasTipCap: big.NewInt(5)}
+ bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
+ opts := &bind.TransactOpts{Signer: mockSign}
+ tx, err := bc.Transact(opts, "")
+ assert.Nil(err)
+ assert.Equal(big.NewInt(5), tx.GasTipCap())
+ assert.Equal(big.NewInt(205), tx.GasFeeCap())
+ assert.Nil(opts.GasTipCap)
+ assert.Nil(opts.GasFeeCap)
+ assert.True(mt.suggestGasTipCapCalled)
+
+ // Second call to Transact should use latest suggested GasTipCap
+ mt.gasTipCap = big.NewInt(6)
+ mt.suggestGasTipCapCalled = false
+ tx, err = bc.Transact(opts, "")
+ assert.Nil(err)
+ assert.Equal(big.NewInt(6), tx.GasTipCap())
+ assert.Equal(big.NewInt(206), tx.GasFeeCap())
+ assert.True(mt.suggestGasTipCapCalled)
+
+ // GasPrice
+ // When opts.GasPrice is nil
+ mt = &mockTransactor{gasPrice: big.NewInt(5)}
+ bc = bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
+ opts = &bind.TransactOpts{Signer: mockSign}
+ tx, err = bc.Transact(opts, "")
+ assert.Nil(err)
+ assert.Equal(big.NewInt(5), tx.GasPrice())
+ assert.Nil(opts.GasPrice)
+ assert.True(mt.suggestGasPriceCalled)
+
+ // Second call to Transact should use latest suggested GasPrice
+ mt.gasPrice = big.NewInt(6)
+ mt.suggestGasPriceCalled = false
+ tx, err = bc.Transact(opts, "")
+ assert.Nil(err)
+ assert.Equal(big.NewInt(6), tx.GasPrice())
+ assert.True(mt.suggestGasPriceCalled)
+}
+
+func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) {
+ received := make(map[string]interface{})
+ if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil {
+ t.Error(err)
+ }
+
+ if len(received) != len(expected) {
+ t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected))
+ }
+ for name, elem := range expected {
+ if !reflect.DeepEqual(elem, received[name]) {
+ t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name])
+ }
+ }
+}
+
+func newMockLog(topics []common.Hash, txHash common.Hash) types.Log {
+ return types.Log{
+ Address: common.HexToAddress("0x0"),
+ Topics: topics,
+ Data: hexutil.MustDecode(hexData),
+ BlockNumber: uint64(26),
+ TxHash: txHash,
+ TxIndex: 111,
+ BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
+ Index: 7,
+ Removed: false,
+ }
+}
+
+func TestCall(t *testing.T) {
+ t.Parallel()
+ var method, methodWithArg = "something", "somethingArrrrg"
+ tests := []struct {
+ name, method string
+ opts *bind.CallOpts
+ mc bind.ContractCaller
+ results *[]interface{}
+ wantErr bool
+ wantErrExact error
+ }{{
+ name: "ok not pending",
+ mc: &mockCaller{
+ codeAtBytes: []byte{0},
+ },
+ method: method,
+ }, {
+ name: "ok pending",
+ mc: &mockPendingCaller{
+ pendingCodeAtBytes: []byte{0},
+ },
+ opts: &bind.CallOpts{
+ Pending: true,
+ },
+ method: method,
+ }, {
+ name: "ok hash",
+ mc: &mockBlockHashCaller{
+ codeAtHashBytes: []byte{0},
+ },
+ opts: &bind.CallOpts{
+ BlockHash: common.Hash{0xaa},
+ },
+ method: method,
+ }, {
+ name: "pack error, no method",
+ mc: new(mockCaller),
+ method: "else",
+ wantErr: true,
+ }, {
+ name: "interface error, pending but not a PendingContractCaller",
+ mc: new(mockCaller),
+ opts: &bind.CallOpts{
+ Pending: true,
+ },
+ method: method,
+ wantErrExact: bind.ErrNoPendingState,
+ }, {
+ name: "interface error, blockHash but not a BlockHashContractCaller",
+ mc: new(mockCaller),
+ opts: &bind.CallOpts{
+ BlockHash: common.Hash{0xaa},
+ },
+ method: method,
+ wantErrExact: bind.ErrNoBlockHashState,
+ }, {
+ name: "pending call canceled",
+ mc: &mockPendingCaller{
+ pendingCallContractErr: context.DeadlineExceeded,
+ },
+ opts: &bind.CallOpts{
+ Pending: true,
+ },
+ method: method,
+ wantErrExact: context.DeadlineExceeded,
+ }, {
+ name: "pending code at error",
+ mc: &mockPendingCaller{
+ pendingCodeAtErr: errors.New(""),
+ },
+ opts: &bind.CallOpts{
+ Pending: true,
+ },
+ method: method,
+ wantErr: true,
+ }, {
+ name: "no pending code at",
+ mc: new(mockPendingCaller),
+ opts: &bind.CallOpts{
+ Pending: true,
+ },
+ method: method,
+ wantErrExact: bind.ErrNoCode,
+ }, {
+ name: "call contract error",
+ mc: &mockCaller{
+ callContractErr: context.DeadlineExceeded,
+ },
+ method: method,
+ wantErrExact: context.DeadlineExceeded,
+ }, {
+ name: "code at error",
+ mc: &mockCaller{
+ codeAtErr: errors.New(""),
+ },
+ method: method,
+ wantErr: true,
+ }, {
+ name: "no code at",
+ mc: new(mockCaller),
+ method: method,
+ wantErrExact: bind.ErrNoCode,
+ }, {
+ name: "call contract at hash error",
+ mc: &mockBlockHashCaller{
+ callContractAtHashErr: context.DeadlineExceeded,
+ },
+ opts: &bind.CallOpts{
+ BlockHash: common.Hash{0xaa},
+ },
+ method: method,
+ wantErrExact: context.DeadlineExceeded,
+ }, {
+ name: "code at error",
+ mc: &mockBlockHashCaller{
+ codeAtHashErr: errors.New(""),
+ },
+ opts: &bind.CallOpts{
+ BlockHash: common.Hash{0xaa},
+ },
+ method: method,
+ wantErr: true,
+ }, {
+ name: "no code at hash",
+ mc: new(mockBlockHashCaller),
+ opts: &bind.CallOpts{
+ BlockHash: common.Hash{0xaa},
+ },
+ method: method,
+ wantErrExact: bind.ErrNoCode,
+ }, {
+ name: "unpack error missing arg",
+ mc: &mockCaller{
+ codeAtBytes: []byte{0},
+ },
+ method: methodWithArg,
+ wantErr: true,
+ }, {
+ name: "interface unpack error",
+ mc: &mockCaller{
+ codeAtBytes: []byte{0},
+ },
+ method: method,
+ results: &[]interface{}{0},
+ wantErr: true,
+ }}
+ for _, test := range tests {
+ bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
+ Methods: map[string]abi.Method{
+ method: {
+ Name: method,
+ Outputs: abi.Arguments{},
+ },
+ methodWithArg: {
+ Name: methodWithArg,
+ Outputs: abi.Arguments{abi.Argument{}},
+ },
+ },
+ }, test.mc, nil, nil)
+ err := bc.Call(test.opts, test.results, test.method)
+ if test.wantErr || test.wantErrExact != nil {
+ if err == nil {
+ t.Fatalf("%q expected error", test.name)
+ }
+ if test.wantErrExact != nil && !errors.Is(err, test.wantErrExact) {
+ t.Fatalf("%q expected error %q but got %q", test.name, test.wantErrExact, err)
+ }
+ continue
+ }
+ if err != nil {
+ t.Fatalf("%q unexpected error: %v", test.name, err)
+ }
+ }
+}
+
+// TestCrashers contains some strings which previously caused the abi codec to crash.
+func TestCrashers(t *testing.T) {
+ t.Parallel()
+ abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"_1"}]}]}]`))
+ abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"&"}]}]}]`))
+ abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"----"}]}]}]`))
+ abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"foo.Bar"}]}]}]`))
+}
diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go
new file mode 100644
index 0000000000..ec28013463
--- /dev/null
+++ b/accounts/abi/bind/bind.go
@@ -0,0 +1,496 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Package bind generates Ethereum contract Go bindings.
+//
+// Detailed usage document and tutorial available on the go-ethereum Wiki page:
+// https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts
+package bind
+
+import (
+ "bytes"
+ "fmt"
+ "go/format"
+ "regexp"
+ "strings"
+ "text/template"
+ "unicode"
+
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// Lang is a target programming language selector to generate bindings for.
+type Lang int
+
+const (
+ LangGo Lang = iota
+)
+
+func isKeyWord(arg string) bool {
+ switch arg {
+ case "break":
+ case "case":
+ case "chan":
+ case "const":
+ case "continue":
+ case "default":
+ case "defer":
+ case "else":
+ case "fallthrough":
+ case "for":
+ case "func":
+ case "go":
+ case "goto":
+ case "if":
+ case "import":
+ case "interface":
+ case "iota":
+ case "map":
+ case "make":
+ case "new":
+ case "package":
+ case "range":
+ case "return":
+ case "select":
+ case "struct":
+ case "switch":
+ case "type":
+ case "var":
+ default:
+ return false
+ }
+
+ return true
+}
+
+// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
+// to be used as is in client code, but rather as an intermediate struct which
+// enforces compile time type safety and naming convention opposed to having to
+// manually maintain hard coded strings that break on runtime.
+func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
+ var (
+ // contracts is the map of each individual contract requested binding
+ contracts = make(map[string]*tmplContract)
+
+ // structs is the map of all redeclared structs shared by passed contracts.
+ structs = make(map[string]*tmplStruct)
+
+ // isLib is the map used to flag each encountered library as such
+ isLib = make(map[string]struct{})
+ )
+ for i := 0; i < len(types); i++ {
+ // Parse the actual ABI to generate the binding for
+ evmABI, err := abi.JSON(strings.NewReader(abis[i]))
+ if err != nil {
+ return "", err
+ }
+ // Strip any whitespace from the JSON ABI
+ strippedABI := strings.Map(func(r rune) rune {
+ if unicode.IsSpace(r) {
+ return -1
+ }
+ return r
+ }, abis[i])
+
+ // Extract the call and transact methods; events, struct definitions; and sort them alphabetically
+ var (
+ calls = make(map[string]*tmplMethod)
+ transacts = make(map[string]*tmplMethod)
+ events = make(map[string]*tmplEvent)
+ fallback *tmplMethod
+ receive *tmplMethod
+
+ // identifiers are used to detect duplicated identifiers of functions
+ // and events. For all calls, transacts and events, abigen will generate
+ // corresponding bindings. However we have to ensure there is no
+ // identifier collisions in the bindings of these categories.
+ callIdentifiers = make(map[string]bool)
+ transactIdentifiers = make(map[string]bool)
+ eventIdentifiers = make(map[string]bool)
+ )
+
+ for _, input := range evmABI.Constructor.Inputs {
+ if hasStruct(input.Type) {
+ bindStructType[lang](input.Type, structs)
+ }
+ }
+
+ for _, original := range evmABI.Methods {
+ // Normalize the method for capital cases and non-anonymous inputs/outputs
+ normalized := original
+ normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
+ // Ensure there is no duplicated identifier
+ var identifiers = callIdentifiers
+ if !original.IsConstant() {
+ identifiers = transactIdentifiers
+ }
+ // Name shouldn't start with a digit. It will make the generated code invalid.
+ if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) {
+ normalizedName = fmt.Sprintf("M%s", normalizedName)
+ normalizedName = abi.ResolveNameConflict(normalizedName, func(name string) bool {
+ _, ok := identifiers[name]
+ return ok
+ })
+ }
+ if identifiers[normalizedName] {
+ return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
+ }
+ identifiers[normalizedName] = true
+
+ normalized.Name = normalizedName
+ normalized.Inputs = make([]abi.Argument, len(original.Inputs))
+ copy(normalized.Inputs, original.Inputs)
+ for j, input := range normalized.Inputs {
+ if input.Name == "" || isKeyWord(input.Name) {
+ normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
+ }
+ if hasStruct(input.Type) {
+ bindStructType[lang](input.Type, structs)
+ }
+ }
+ normalized.Outputs = make([]abi.Argument, len(original.Outputs))
+ copy(normalized.Outputs, original.Outputs)
+ for j, output := range normalized.Outputs {
+ if output.Name != "" {
+ normalized.Outputs[j].Name = capitalise(output.Name)
+ }
+ if hasStruct(output.Type) {
+ bindStructType[lang](output.Type, structs)
+ }
+ }
+ // Append the methods to the call or transact lists
+ if original.IsConstant() {
+ calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
+ } else {
+ transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
+ }
+ }
+ for _, original := range evmABI.Events {
+ // Skip anonymous events as they don't support explicit filtering
+ if original.Anonymous {
+ continue
+ }
+ // Normalize the event for capital cases and non-anonymous outputs
+ normalized := original
+
+ // Ensure there is no duplicated identifier
+ normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
+ // Name shouldn't start with a digit. It will make the generated code invalid.
+ if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) {
+ normalizedName = fmt.Sprintf("E%s", normalizedName)
+ normalizedName = abi.ResolveNameConflict(normalizedName, func(name string) bool {
+ _, ok := eventIdentifiers[name]
+ return ok
+ })
+ }
+ if eventIdentifiers[normalizedName] {
+ return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
+ }
+ eventIdentifiers[normalizedName] = true
+ normalized.Name = normalizedName
+
+ used := make(map[string]bool)
+ normalized.Inputs = make([]abi.Argument, len(original.Inputs))
+ copy(normalized.Inputs, original.Inputs)
+ for j, input := range normalized.Inputs {
+ if input.Name == "" || isKeyWord(input.Name) {
+ normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
+ }
+ // Event is a bit special, we need to define event struct in binding,
+ // ensure there is no camel-case-style name conflict.
+ for index := 0; ; index++ {
+ if !used[capitalise(normalized.Inputs[j].Name)] {
+ used[capitalise(normalized.Inputs[j].Name)] = true
+ break
+ }
+ normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index)
+ }
+ if hasStruct(input.Type) {
+ bindStructType[lang](input.Type, structs)
+ }
+ }
+ // Append the event to the accumulator list
+ events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
+ }
+ // Add two special fallback functions if they exist
+ if evmABI.HasFallback() {
+ fallback = &tmplMethod{Original: evmABI.Fallback}
+ }
+ if evmABI.HasReceive() {
+ receive = &tmplMethod{Original: evmABI.Receive}
+ }
+ contracts[types[i]] = &tmplContract{
+ Type: capitalise(types[i]),
+ InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
+ InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
+ Constructor: evmABI.Constructor,
+ Calls: calls,
+ Transacts: transacts,
+ Fallback: fallback,
+ Receive: receive,
+ Events: events,
+ Libraries: make(map[string]string),
+ }
+ // Function 4-byte signatures are stored in the same sequence
+ // as types, if available.
+ if len(fsigs) > i {
+ contracts[types[i]].FuncSigs = fsigs[i]
+ }
+ // Parse library references.
+ for pattern, name := range libs {
+ matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin))
+ if err != nil {
+ log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
+ }
+ if matched {
+ contracts[types[i]].Libraries[pattern] = name
+ // keep track that this type is a library
+ if _, ok := isLib[name]; !ok {
+ isLib[name] = struct{}{}
+ }
+ }
+ }
+ }
+ // Check if that type has already been identified as a library
+ for i := 0; i < len(types); i++ {
+ _, ok := isLib[types[i]]
+ contracts[types[i]].Library = ok
+ }
+ // Generate the contract template data content and render it
+ data := &tmplData{
+ Package: pkg,
+ Contracts: contracts,
+ Libraries: libs,
+ Structs: structs,
+ }
+ buffer := new(bytes.Buffer)
+
+ funcs := map[string]interface{}{
+ "bindtype": bindType[lang],
+ "bindtopictype": bindTopicType[lang],
+ "namedtype": namedType[lang],
+ "capitalise": capitalise,
+ "decapitalise": decapitalise,
+ }
+ tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
+ if err := tmpl.Execute(buffer, data); err != nil {
+ return "", err
+ }
+ // For Go bindings pass the code through gofmt to clean it up
+ if lang == LangGo {
+ code, err := format.Source(buffer.Bytes())
+ if err != nil {
+ return "", fmt.Errorf("%v\n%s", err, buffer)
+ }
+ return string(code), nil
+ }
+ // For all others just return as is for now
+ return buffer.String(), nil
+}
+
+// bindType is a set of type binders that convert Solidity types to some supported
+// programming language types.
+var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
+ LangGo: bindTypeGo,
+}
+
+// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones.
+func bindBasicTypeGo(kind abi.Type) string {
+ switch kind.T {
+ case abi.AddressTy:
+ return "common.Address"
+ case abi.IntTy, abi.UintTy:
+ parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
+ switch parts[2] {
+ case "8", "16", "32", "64":
+ return fmt.Sprintf("%sint%s", parts[1], parts[2])
+ }
+ return "*big.Int"
+ case abi.FixedBytesTy:
+ return fmt.Sprintf("[%d]byte", kind.Size)
+ case abi.BytesTy:
+ return "[]byte"
+ case abi.FunctionTy:
+ return "[24]byte"
+ default:
+ // string, bool types
+ return kind.String()
+ }
+}
+
+// bindTypeGo converts solidity types to Go ones. Since there is no clear mapping
+// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
+// mapped will use an upscaled type (e.g. BigDecimal).
+func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ return structs[kind.TupleRawName+kind.String()].Name
+ case abi.ArrayTy:
+ return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs)
+ case abi.SliceTy:
+ return "[]" + bindTypeGo(*kind.Elem, structs)
+ default:
+ return bindBasicTypeGo(kind)
+ }
+}
+
+// bindTopicType is a set of type binders that convert Solidity types to some
+// supported programming language topic types.
+var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
+ LangGo: bindTopicTypeGo,
+}
+
+// bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
+// functionality as for simple types, but dynamic types get converted to hashes.
+func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
+ bound := bindTypeGo(kind, structs)
+
+ // todo(rjl493456442) according solidity documentation, indexed event
+ // parameters that are not value types i.e. arrays and structs are not
+ // stored directly but instead a keccak256-hash of an encoding is stored.
+ //
+ // We only convert strings and bytes to hash, still need to deal with
+ // array(both fixed-size and dynamic-size) and struct.
+ if bound == "string" || bound == "[]byte" {
+ bound = "common.Hash"
+ }
+ return bound
+}
+
+// bindStructType is a set of type binders that convert Solidity tuple types to some supported
+// programming language struct definition.
+var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
+ LangGo: bindStructTypeGo,
+}
+
+// bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
+// in the given map.
+// Notably, this function will resolve and record nested struct recursively.
+func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ // We compose a raw struct name and a canonical parameter expression
+ // together here. The reason is before solidity v0.5.11, kind.TupleRawName
+ // is empty, so we use canonical parameter expression to distinguish
+ // different struct definition. From the consideration of backward
+ // compatibility, we concat these two together so that if kind.TupleRawName
+ // is not empty, it can have unique id.
+ id := kind.TupleRawName + kind.String()
+ if s, exist := structs[id]; exist {
+ return s.Name
+ }
+ var (
+ names = make(map[string]bool)
+ fields []*tmplField
+ )
+ for i, elem := range kind.TupleElems {
+ name := capitalise(kind.TupleRawNames[i])
+ name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] })
+ names[name] = true
+ fields = append(fields, &tmplField{Type: bindStructTypeGo(*elem, structs), Name: name, SolKind: *elem})
+ }
+ name := kind.TupleRawName
+ if name == "" {
+ name = fmt.Sprintf("Struct%d", len(structs))
+ }
+ name = capitalise(name)
+
+ structs[id] = &tmplStruct{
+ Name: name,
+ Fields: fields,
+ }
+ return name
+ case abi.ArrayTy:
+ return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs)
+ case abi.SliceTy:
+ return "[]" + bindStructTypeGo(*kind.Elem, structs)
+ default:
+ return bindBasicTypeGo(kind)
+ }
+}
+
+// namedType is a set of functions that transform language specific types to
+// named versions that may be used inside method names.
+var namedType = map[Lang]func(string, abi.Type) string{
+ LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
+}
+
+// alias returns an alias of the given string based on the aliasing rules
+// or returns itself if no rule is matched.
+func alias(aliases map[string]string, n string) string {
+ if alias, exist := aliases[n]; exist {
+ return alias
+ }
+ return n
+}
+
+// methodNormalizer is a name transformer that modifies Solidity method names to
+// conform to target language naming conventions.
+var methodNormalizer = map[Lang]func(string) string{
+ LangGo: abi.ToCamelCase,
+}
+
+// capitalise makes a camel-case string which starts with an upper case character.
+var capitalise = abi.ToCamelCase
+
+// decapitalise makes a camel-case string which starts with a lower case character.
+func decapitalise(input string) string {
+ if len(input) == 0 {
+ return input
+ }
+
+ goForm := abi.ToCamelCase(input)
+ return strings.ToLower(goForm[:1]) + goForm[1:]
+}
+
+// structured checks whether a list of ABI data types has enough information to
+// operate through a proper Go struct or if flat returns are needed.
+func structured(args abi.Arguments) bool {
+ if len(args) < 2 {
+ return false
+ }
+ exists := make(map[string]bool)
+ for _, out := range args {
+ // If the name is anonymous, we can't organize into a struct
+ if out.Name == "" {
+ return false
+ }
+ // If the field name is empty when normalized or collides (var, Var, _var, _Var),
+ // we can't organize into a struct
+ field := capitalise(out.Name)
+ if field == "" || exists[field] {
+ return false
+ }
+ exists[field] = true
+ }
+ return true
+}
+
+// hasStruct returns an indicator whether the given type is struct, struct slice
+// or struct array.
+func hasStruct(t abi.Type) bool {
+ switch t.T {
+ case abi.SliceTy:
+ return hasStruct(*t.Elem)
+ case abi.ArrayTy:
+ return hasStruct(*t.Elem)
+ case abi.TupleTy:
+ return true
+ default:
+ return false
+ }
+}
diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go
new file mode 100644
index 0000000000..95dc13cc18
--- /dev/null
+++ b/accounts/abi/bind/template.go
@@ -0,0 +1,571 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package bind
+
+import "github.com/ethereum/go-ethereum/accounts/abi"
+
+// tmplData is the data structure required to fill the binding template.
+type tmplData struct {
+ Package string // Name of the package to place the generated file in
+ Contracts map[string]*tmplContract // List of contracts to generate into this file
+ Libraries map[string]string // Map the bytecode's link pattern to the library name
+ Structs map[string]*tmplStruct // Contract struct type definitions
+}
+
+// tmplContract contains the data needed to generate an individual contract binding.
+type tmplContract struct {
+ Type string // Type name of the main contract binding
+ InputABI string // JSON ABI used as the input to generate the binding from
+ InputBin string // Optional EVM bytecode used to generate deploy code from
+ FuncSigs map[string]string // Optional map: string signature -> 4-byte signature
+ Constructor abi.Method // Contract constructor for deploy parametrization
+ Calls map[string]*tmplMethod // Contract calls that only read state data
+ Transacts map[string]*tmplMethod // Contract calls that write state data
+ Fallback *tmplMethod // Additional special fallback function
+ Receive *tmplMethod // Additional special receive function
+ Events map[string]*tmplEvent // Contract events accessors
+ Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
+ Library bool // Indicator whether the contract is a library
+}
+
+// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
+// and cached data fields.
+type tmplMethod struct {
+ Original abi.Method // Original method as parsed by the abi package
+ Normalized abi.Method // Normalized version of the parsed method (capitalized names, non-anonymous args/returns)
+ Structured bool // Whether the returns should be accumulated into a struct
+}
+
+// tmplEvent is a wrapper around an abi.Event that contains a few preprocessed
+// and cached data fields.
+type tmplEvent struct {
+ Original abi.Event // Original event as parsed by the abi package
+ Normalized abi.Event // Normalized version of the parsed fields
+}
+
+// tmplField is a wrapper around a struct field with binding language
+// struct type definition and relative filed name.
+type tmplField struct {
+ Type string // Field type representation depends on target binding language
+ Name string // Field name converted from the raw user-defined field name
+ SolKind abi.Type // Raw abi type information
+}
+
+// tmplStruct is a wrapper around an abi.tuple and contains an auto-generated
+// struct name.
+type tmplStruct struct {
+ Name string // Auto-generated struct name(before solidity v0.5.11) or raw name.
+ Fields []*tmplField // Struct fields definition depends on the binding language.
+}
+
+// tmplSource is language to template mapping containing all the supported
+// programming languages the package can generate to.
+var tmplSource = map[Lang]string{
+ LangGo: tmplSourceGo,
+}
+
+// tmplSourceGo is the Go source template that the generated Go contract binding
+// is based on.
+const tmplSourceGo = `
+// Code generated - DO NOT EDIT.
+// This file is a generated binding and any manual changes will be lost.
+
+package {{.Package}}
+
+import (
+ "math/big"
+ "strings"
+ "errors"
+
+ ethereum "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/event"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var (
+ _ = errors.New
+ _ = big.NewInt
+ _ = strings.NewReader
+ _ = ethereum.NotFound
+ _ = bind.Bind
+ _ = common.Big1
+ _ = types.BloomLookup
+ _ = event.NewSubscription
+ _ = abi.ConvertType
+)
+
+{{$structs := .Structs}}
+{{range $structs}}
+ // {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
+ type {{.Name}} struct {
+ {{range $field := .Fields}}
+ {{$field.Name}} {{$field.Type}}{{end}}
+ }
+{{end}}
+
+{{range $contract := .Contracts}}
+ // {{.Type}}MetaData contains all meta data concerning the {{.Type}} contract.
+ var {{.Type}}MetaData = &bind.MetaData{
+ ABI: "{{.InputABI}}",
+ {{if $contract.FuncSigs -}}
+ Sigs: map[string]string{
+ {{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
+ {{end}}
+ },
+ {{end -}}
+ {{if .InputBin -}}
+ Bin: "0x{{.InputBin}}",
+ {{end}}
+ }
+ // {{.Type}}ABI is the input ABI used to generate the binding from.
+ // Deprecated: Use {{.Type}}MetaData.ABI instead.
+ var {{.Type}}ABI = {{.Type}}MetaData.ABI
+
+ {{if $contract.FuncSigs}}
+ // Deprecated: Use {{.Type}}MetaData.Sigs instead.
+ // {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
+ var {{.Type}}FuncSigs = {{.Type}}MetaData.Sigs
+ {{end}}
+
+ {{if .InputBin}}
+ // {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
+ // Deprecated: Use {{.Type}}MetaData.Bin instead.
+ var {{.Type}}Bin = {{.Type}}MetaData.Bin
+
+ // Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
+ func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
+ parsed, err := {{.Type}}MetaData.GetAbi()
+ if err != nil {
+ return common.Address{}, nil, nil, err
+ }
+ if parsed == nil {
+ return common.Address{}, nil, nil, errors.New("GetABI returned nil")
+ }
+ {{range $pattern, $name := .Libraries}}
+ {{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
+ {{$contract.Type}}Bin = strings.ReplaceAll({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:])
+ {{end}}
+ address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
+ if err != nil {
+ return common.Address{}, nil, nil, err
+ }
+ return address, tx, &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
+ }
+ {{end}}
+
+ // {{.Type}} is an auto generated Go binding around an Ethereum contract.
+ type {{.Type}} struct {
+ {{.Type}}Caller // Read-only binding to the contract
+ {{.Type}}Transactor // Write-only binding to the contract
+ {{.Type}}Filterer // Log filterer for contract events
+ }
+
+ // {{.Type}}Caller is an auto generated read-only Go binding around an Ethereum contract.
+ type {{.Type}}Caller struct {
+ contract *bind.BoundContract // Generic contract wrapper for the low level calls
+ }
+
+ // {{.Type}}Transactor is an auto generated write-only Go binding around an Ethereum contract.
+ type {{.Type}}Transactor struct {
+ contract *bind.BoundContract // Generic contract wrapper for the low level calls
+ }
+
+ // {{.Type}}Filterer is an auto generated log filtering Go binding around an Ethereum contract events.
+ type {{.Type}}Filterer struct {
+ contract *bind.BoundContract // Generic contract wrapper for the low level calls
+ }
+
+ // {{.Type}}Session is an auto generated Go binding around an Ethereum contract,
+ // with pre-set call and transact options.
+ type {{.Type}}Session struct {
+ Contract *{{.Type}} // Generic contract binding to set the session for
+ CallOpts bind.CallOpts // Call options to use throughout this session
+ TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
+ }
+
+ // {{.Type}}CallerSession is an auto generated read-only Go binding around an Ethereum contract,
+ // with pre-set call options.
+ type {{.Type}}CallerSession struct {
+ Contract *{{.Type}}Caller // Generic contract caller binding to set the session for
+ CallOpts bind.CallOpts // Call options to use throughout this session
+ }
+
+ // {{.Type}}TransactorSession is an auto generated write-only Go binding around an Ethereum contract,
+ // with pre-set transact options.
+ type {{.Type}}TransactorSession struct {
+ Contract *{{.Type}}Transactor // Generic contract transactor binding to set the session for
+ TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
+ }
+
+ // {{.Type}}Raw is an auto generated low-level Go binding around an Ethereum contract.
+ type {{.Type}}Raw struct {
+ Contract *{{.Type}} // Generic contract binding to access the raw methods on
+ }
+
+ // {{.Type}}CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
+ type {{.Type}}CallerRaw struct {
+ Contract *{{.Type}}Caller // Generic read-only contract binding to access the raw methods on
+ }
+
+ // {{.Type}}TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
+ type {{.Type}}TransactorRaw struct {
+ Contract *{{.Type}}Transactor // Generic write-only contract binding to access the raw methods on
+ }
+
+ // New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract.
+ func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) {
+ contract, err := bind{{.Type}}(address, backend, backend, backend)
+ if err != nil {
+ return nil, err
+ }
+ return &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
+ }
+
+ // New{{.Type}}Caller creates a new read-only instance of {{.Type}}, bound to a specific deployed contract.
+ func New{{.Type}}Caller(address common.Address, caller bind.ContractCaller) (*{{.Type}}Caller, error) {
+ contract, err := bind{{.Type}}(address, caller, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &{{.Type}}Caller{contract: contract}, nil
+ }
+
+ // New{{.Type}}Transactor creates a new write-only instance of {{.Type}}, bound to a specific deployed contract.
+ func New{{.Type}}Transactor(address common.Address, transactor bind.ContractTransactor) (*{{.Type}}Transactor, error) {
+ contract, err := bind{{.Type}}(address, nil, transactor, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &{{.Type}}Transactor{contract: contract}, nil
+ }
+
+ // New{{.Type}}Filterer creates a new log filterer instance of {{.Type}}, bound to a specific deployed contract.
+ func New{{.Type}}Filterer(address common.Address, filterer bind.ContractFilterer) (*{{.Type}}Filterer, error) {
+ contract, err := bind{{.Type}}(address, nil, nil, filterer)
+ if err != nil {
+ return nil, err
+ }
+ return &{{.Type}}Filterer{contract: contract}, nil
+ }
+
+ // bind{{.Type}} binds a generic wrapper to an already deployed contract.
+ func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
+ parsed, err := {{.Type}}MetaData.GetAbi()
+ if err != nil {
+ return nil, err
+ }
+ return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
+ }
+
+ // Call invokes the (constant) contract method with params as input values and
+ // sets the output to result. The result type might be a single field for simple
+ // returns, a slice of interfaces for anonymous returns and a struct for named
+ // returns.
+ func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+ return _{{$contract.Type}}.Contract.{{$contract.Type}}Caller.contract.Call(opts, result, method, params...)
+ }
+
+ // Transfer initiates a plain transaction to move funds to the contract, calling
+ // its default method if one is available.
+ func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transfer(opts)
+ }
+
+ // Transact invokes the (paid) contract method with params as input values.
+ func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transact(opts, method, params...)
+ }
+
+ // Call invokes the (constant) contract method with params as input values and
+ // sets the output to result. The result type might be a single field for simple
+ // returns, a slice of interfaces for anonymous returns and a struct for named
+ // returns.
+ func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+ return _{{$contract.Type}}.Contract.contract.Call(opts, result, method, params...)
+ }
+
+ // Transfer initiates a plain transaction to move funds to the contract, calling
+ // its default method if one is available.
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.contract.Transfer(opts)
+ }
+
+ // Transact invokes the (paid) contract method with params as input values.
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...)
+ }
+
+ {{range .Calls}}
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) {
+ var out []interface{}
+ err := _{{$contract.Type}}.contract.Call(opts, &out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ {{if .Structured}}
+ outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} })
+ if err != nil {
+ return *outstruct, err
+ }
+ {{range $i, $t := .Normalized.Outputs}}
+ outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
+
+ return *outstruct, err
+ {{else}}
+ if err != nil {
+ return {{range $i, $_ := .Normalized.Outputs}}*new({{bindtype .Type $structs}}), {{end}} err
+ }
+ {{range $i, $t := .Normalized.Outputs}}
+ out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
+
+ return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err
+ {{end}}
+ }
+
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+ {{end}}
+
+ {{range .Transacts}}
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+ {{end}}
+
+ {{if .Fallback}}
+ // Fallback is a paid mutator transaction binding the contract fallback function.
+ //
+ // Solidity: {{.Fallback.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) {
+ return _{{$contract.Type}}.contract.RawTransact(opts, calldata)
+ }
+
+ // Fallback is a paid mutator transaction binding the contract fallback function.
+ //
+ // Solidity: {{.Fallback.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Session) Fallback(calldata []byte) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
+ }
+
+ // Fallback is a paid mutator transaction binding the contract fallback function.
+ //
+ // Solidity: {{.Fallback.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Fallback(calldata []byte) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
+ }
+ {{end}}
+
+ {{if .Receive}}
+ // Receive is a paid mutator transaction binding the contract receive function.
+ //
+ // Solidity: {{.Receive.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _{{$contract.Type}}.contract.RawTransact(opts, nil) // calldata is disallowed for receive function
+ }
+
+ // Receive is a paid mutator transaction binding the contract receive function.
+ //
+ // Solidity: {{.Receive.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Session) Receive() (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
+ }
+
+ // Receive is a paid mutator transaction binding the contract receive function.
+ //
+ // Solidity: {{.Receive.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Receive() (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
+ }
+ {{end}}
+
+ {{range .Events}}
+ // {{$contract.Type}}{{.Normalized.Name}}Iterator is returned from Filter{{.Normalized.Name}} and is used to iterate over the raw logs and unpacked data for {{.Normalized.Name}} events raised by the {{$contract.Type}} contract.
+ type {{$contract.Type}}{{.Normalized.Name}}Iterator struct {
+ Event *{{$contract.Type}}{{.Normalized.Name}} // Event containing the contract specifics and raw log
+
+ contract *bind.BoundContract // Generic contract to use for unpacking event data
+ event string // Event name to use for unpacking event data
+
+ logs chan types.Log // Log channel receiving the found contract events
+ sub ethereum.Subscription // Subscription for errors, completion and termination
+ done bool // Whether the subscription completed delivering logs
+ fail error // Occurred error to stop iteration
+ }
+ // Next advances the iterator to the subsequent event, returning whether there
+ // are any more events found. In case of a retrieval or parsing error, false is
+ // returned and Error() can be queried for the exact failure.
+ func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Next() bool {
+ // If the iterator failed, stop iterating
+ if (it.fail != nil) {
+ return false
+ }
+ // If the iterator completed, deliver directly whatever's available
+ if (it.done) {
+ select {
+ case log := <-it.logs:
+ it.Event = new({{$contract.Type}}{{.Normalized.Name}})
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+ // Iterator still in progress, wait for either a data or an error event
+ select {
+ case log := <-it.logs:
+ it.Event = new({{$contract.Type}}{{.Normalized.Name}})
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+ }
+ // Error returns any retrieval or parsing error occurred during filtering.
+ func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Error() error {
+ return it.fail
+ }
+ // Close terminates the iteration process, releasing any pending underlying
+ // resources.
+ func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+ }
+
+ // {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract.
+ type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}}
+ {{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type $structs}}{{else}}{{bindtype .Type $structs}}{{end}}; {{end}}
+ Raw types.Log // Blockchain specific contextual infos
+ }
+
+ // Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
+ {{range .Normalized.Inputs}}
+ {{if .Indexed}}var {{.Name}}Rule []interface{}
+ for _, {{.Name}}Item := range {{.Name}} {
+ {{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
+ }{{end}}{{end}}
+
+ logs, sub, err := _{{$contract.Type}}.contract.FilterLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
+ if err != nil {
+ return nil, err
+ }
+ return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
+ }
+
+ // Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (event.Subscription, error) {
+ {{range .Normalized.Inputs}}
+ {{if .Indexed}}var {{.Name}}Rule []interface{}
+ for _, {{.Name}}Item := range {{.Name}} {
+ {{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
+ }{{end}}{{end}}
+
+ logs, sub, err := _{{$contract.Type}}.contract.WatchLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case log := <-logs:
+ // New log arrived, parse the event and forward to the user
+ event := new({{$contract.Type}}{{.Normalized.Name}})
+ if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+ }
+
+ // Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
+ event := new({{$contract.Type}}{{.Normalized.Name}})
+ if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+ }
+
+ {{end}}
+{{end}}
+`
diff --git a/accounts/abi/bind/util.go b/accounts/abi/bind/util.go
new file mode 100644
index 0000000000..b931fbb04d
--- /dev/null
+++ b/accounts/abi/bind/util.go
@@ -0,0 +1,79 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package bind
+
+import (
+ "context"
+ "errors"
+ "time"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// WaitMined waits for tx to be mined on the blockchain.
+// It stops waiting when the context is canceled.
+func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) {
+ queryTicker := time.NewTicker(time.Second)
+ defer queryTicker.Stop()
+
+ logger := log.New("hash", tx.Hash())
+ for {
+ receipt, err := b.TransactionReceipt(ctx, tx.Hash())
+ if err == nil {
+ return receipt, nil
+ }
+
+ if errors.Is(err, ethereum.NotFound) {
+ logger.Trace("Transaction not yet mined")
+ } else {
+ logger.Trace("Receipt retrieval failed", "err", err)
+ }
+
+ // Wait for the next round.
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ case <-queryTicker.C:
+ }
+ }
+}
+
+// WaitDeployed waits for a contract deployment transaction and returns the on-chain
+// contract address when it is mined. It stops waiting when ctx is canceled.
+func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (common.Address, error) {
+ if tx.To() != nil {
+ return common.Address{}, errors.New("tx is not contract creation")
+ }
+ receipt, err := WaitMined(ctx, b, tx)
+ if err != nil {
+ return common.Address{}, err
+ }
+ if receipt.ContractAddress == (common.Address{}) {
+ return common.Address{}, errors.New("zero address")
+ }
+ // Check that code has indeed been deployed at the address.
+ // This matters on pre-Homestead chains: OOG in the constructor
+ // could leave an empty account behind.
+ code, err := b.CodeAt(ctx, receipt.ContractAddress, nil)
+ if err == nil && len(code) == 0 {
+ err = ErrNoCodeAfterDeploy
+ }
+ return receipt.ContractAddress, err
+}