github.com/status-im/status-go@v1.1.0/transactions/rpc_wrapper.go (about)

     1  package transactions
     2  
     3  import (
     4  	"context"
     5  	"math/big"
     6  
     7  	ethereum "github.com/ethereum/go-ethereum"
     8  	"github.com/ethereum/go-ethereum/common"
     9  	"github.com/ethereum/go-ethereum/common/hexutil"
    10  	gethtypes "github.com/ethereum/go-ethereum/core/types"
    11  
    12  	"github.com/status-im/status-go/eth-node/types"
    13  
    14  	"github.com/status-im/status-go/rpc"
    15  )
    16  
    17  // rpcWrapper wraps provides convenient interface for ethereum RPC APIs we need for sending transactions
    18  type rpcWrapper struct {
    19  	RPCClient rpc.ClientInterface
    20  	chainID   uint64
    21  }
    22  
    23  func newRPCWrapper(client rpc.ClientInterface, chainID uint64) *rpcWrapper {
    24  	return &rpcWrapper{RPCClient: client, chainID: chainID}
    25  }
    26  
    27  // PendingNonceAt returns the account nonce of the given account in the pending state.
    28  // This is the nonce that should be used for the next transaction.
    29  func (w *rpcWrapper) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
    30  	var result hexutil.Uint64
    31  	err := w.RPCClient.CallContext(ctx, &result, w.chainID, "eth_getTransactionCount", account, "pending")
    32  	return uint64(result), err
    33  }
    34  
    35  // SuggestGasPrice retrieves the currently suggested gas price to allow a timely
    36  // execution of a transaction.
    37  func (w *rpcWrapper) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
    38  	var hex hexutil.Big
    39  	if err := w.RPCClient.CallContext(ctx, &hex, w.chainID, "eth_gasPrice"); err != nil {
    40  		return nil, err
    41  	}
    42  	return (*big.Int)(&hex), nil
    43  }
    44  
    45  // EstimateGas tries to estimate the gas needed to execute a specific transaction based on
    46  // the current pending state of the backend blockchain. There is no guarantee that this is
    47  // the true gas limit requirement as other transactions may be added or removed by miners,
    48  // but it should provide a basis for setting a reasonable default.
    49  func (w *rpcWrapper) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
    50  	var hex hexutil.Uint64
    51  	err := w.RPCClient.CallContext(ctx, &hex, w.chainID, "eth_estimateGas", toCallArg(msg))
    52  	if err != nil {
    53  		return 0, err
    54  	}
    55  	return uint64(hex), nil
    56  }
    57  
    58  // Does the `eth_sendRawTransaction` call with the given raw transaction hex string.
    59  func (w *rpcWrapper) SendRawTransaction(ctx context.Context, rawTx string) error {
    60  	return w.RPCClient.CallContext(ctx, nil, w.chainID, "eth_sendRawTransaction", rawTx)
    61  }
    62  
    63  // SendTransaction injects a signed transaction into the pending pool for execution.
    64  //
    65  // If the transaction was a contract creation use the TransactionReceipt method to get the
    66  // contract address after the transaction has been mined.
    67  func (w *rpcWrapper) SendTransaction(ctx context.Context, tx *gethtypes.Transaction) error {
    68  	data, err := tx.MarshalBinary()
    69  	if err != nil {
    70  		return err
    71  	}
    72  	return w.SendRawTransaction(ctx, types.EncodeHex(data))
    73  }
    74  
    75  func toCallArg(msg ethereum.CallMsg) interface{} {
    76  	arg := map[string]interface{}{
    77  		"from": msg.From,
    78  		"to":   msg.To,
    79  	}
    80  	if len(msg.Data) > 0 {
    81  		arg["data"] = types.HexBytes(msg.Data)
    82  	}
    83  	if msg.Value != nil {
    84  		arg["value"] = (*hexutil.Big)(msg.Value)
    85  	}
    86  	if msg.Gas != 0 {
    87  		arg["gas"] = hexutil.Uint64(msg.Gas)
    88  	}
    89  	if msg.GasPrice != nil {
    90  		arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
    91  	}
    92  	return arg
    93  }