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 }