package transactions import ( "context" "math/big" ethereum "github.com/ethereum/go-ethereum" "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/rlp" "github.com/status-im/status-go/geth/rpc" ) // EthTransactor provides methods to create transactions for ethereum network. type EthTransactor interface { PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) ethereum.GasEstimator ethereum.GasPricer ethereum.TransactionSender } // EthTxClient wraps common API methods that are used to send transaction. type EthTxClient struct { c *rpc.Client } // NewEthTxClient returns a new EthTxClient for client func NewEthTxClient(client *rpc.Client) *EthTxClient { return &EthTxClient{c: client} } // PendingNonceAt returns the account nonce of the given account in the pending state. // This is the nonce that should be used for the next transaction. func (ec *EthTxClient) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { var result hexutil.Uint64 err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, "pending") return uint64(result), err } // SuggestGasPrice retrieves the currently suggested gas price to allow a timely // execution of a transaction. func (ec *EthTxClient) SuggestGasPrice(ctx context.Context) (*big.Int, error) { var hex hexutil.Big if err := ec.c.CallContext(ctx, &hex, "eth_gasPrice"); err != nil { return nil, err } return (*big.Int)(&hex), nil } // 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. func (ec *EthTxClient) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) { var hex hexutil.Uint64 err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg)) if err != nil { return 0, err } return uint64(hex), nil } // SendTransaction injects a signed transaction into the pending pool for execution. // // If the transaction was a contract creation use the TransactionReceipt method to get the // contract address after the transaction has been mined. func (ec *EthTxClient) SendTransaction(ctx context.Context, tx *types.Transaction) error { data, err := rlp.EncodeToBytes(tx) if err != nil { return err } return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data)) } func toCallArg(msg ethereum.CallMsg) interface{} { arg := map[string]interface{}{ "from": msg.From, "to": msg.To, } if len(msg.Data) > 0 { arg["data"] = hexutil.Bytes(msg.Data) } if msg.Value != nil { arg["value"] = (*hexutil.Big)(msg.Value) } if msg.Gas != 0 { arg["gas"] = hexutil.Uint64(msg.Gas) } if msg.GasPrice != nil { arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) } return arg }