diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 1195929f7d..d1a8aec60b 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -696,6 +696,16 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", hexutil.Encode(data)) } +// SendTransactionSync combining transaction submission and receipt retrieval into a single RPC call +// TODO: testing +func (ec *Client) SendRawTransactionSync(ctx context.Context, tx *types.Transaction) error { + data, err := tx.MarshalBinary() + if err != nil { + return err + } + return ec.c.CallContext(ctx, nil, "eth_sendRawTransactionSync", hexutil.Encode(data)) +} + // RevertErrorData returns the 'revert reason' data of a contract call. // // This can be used with CallContract and EstimateGas, and only when the server is Geth. diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 554f525290..96aa32225d 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1636,6 +1636,38 @@ func (api *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil return SubmitTransaction(ctx, api.b, tx) } +func (api *TransactionAPI) SendRawTransactionSync(ctx context.Context, input hexutil.Bytes, timeout int64) (map[string]interface{}, error) { + tx := new(types.Transaction) + if err := tx.UnmarshalBinary(input); err != nil { + return nil, err + } + hash, err := SubmitTransaction(ctx, api.b, tx) + + if err != nil { + return nil, err + } + + // waiting for receipt + timer := time.NewTimer(time.Duration(timeout) * time.Second) + defer timer.Stop() + + <-timer.C + receipt, err := api.GetTransactionReceipt(ctx, hash) + // If the timeout expires without obtaining a receipt, return code error 4 + if receipt == nil { + return nil, &timeoutError{ + message: "the timeout expires without obtaining a receipt", + } + } + + if err != nil { + return nil, err + } + + return receipt, nil + +} + // Sign calculates an ECDSA signature for: // keccak256("\x19Ethereum Signed Message:\n" + len(message) + message). // diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index c0a8fe9a58..30c0e90fc6 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -3885,3 +3885,7 @@ func (b configTimeBackend) HeaderByNumber(_ context.Context, n rpc.BlockNumber) func (b configTimeBackend) CurrentHeader() *types.Header { return &types.Header{Time: b.time} } + +func TestSendRawTransactionSync(t *testing.T) { + +} diff --git a/internal/ethapi/errors.go b/internal/ethapi/errors.go index 154938fa0e..23542a8c24 100644 --- a/internal/ethapi/errors.go +++ b/internal/ethapi/errors.go @@ -108,6 +108,7 @@ const ( errCodeInvalidParams = -32602 errCodeReverted = -32000 errCodeVMError = -32015 + errCodeTimeout = 4 ) func txValidationError(err error) *invalidTxError { @@ -168,3 +169,8 @@ type blockGasLimitReachedError struct{ message string } func (e *blockGasLimitReachedError) Error() string { return e.message } func (e *blockGasLimitReachedError) ErrorCode() int { return errCodeBlockGasLimitReached } + +type timeoutError struct{ message string } + +func (e *timeoutError) Error() string { return e.message } +func (e *timeoutError) ErrorCode() int { return errCodeTimeout }