@@ -55,6 +55,7 @@ import (
5555const estimateGasErrorRatio = 0.015
5656
5757var errBlobTxNotSupported = errors .New ("signing blob transactions not supported" )
58+ var errSubClosed = errors .New ("chain subscription closed" )
5859
5960// EthereumAPI provides an API to access Ethereum related information.
6061type EthereumAPI struct {
@@ -1666,6 +1667,92 @@ func (api *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil
16661667 return SubmitTransaction (ctx , api .b , tx )
16671668}
16681669
1670+ // SendRawTransactionSync will add the signed transaction to the transaction pool
1671+ // and wait until the transaction has been included in a block and return the receipt, or the timeout.
1672+ func (api * TransactionAPI ) SendRawTransactionSync (ctx context.Context , input hexutil.Bytes , timeoutMs * hexutil.Uint64 ) (map [string ]interface {}, error ) {
1673+ tx := new (types.Transaction )
1674+ if err := tx .UnmarshalBinary (input ); err != nil {
1675+ return nil , err
1676+ }
1677+
1678+ ch := make (chan core.ChainEvent , 128 )
1679+ sub := api .b .SubscribeChainEvent (ch )
1680+ subErrCh := sub .Err ()
1681+ defer sub .Unsubscribe ()
1682+
1683+ hash , err := SubmitTransaction (ctx , api .b , tx )
1684+ if err != nil {
1685+ return nil , err
1686+ }
1687+
1688+ maxTimeout := api .b .RPCTxSyncMaxTimeout ()
1689+ defaultTimeout := api .b .RPCTxSyncDefaultTimeout ()
1690+
1691+ timeout := defaultTimeout
1692+ if timeoutMs != nil && * timeoutMs > 0 {
1693+ req := time .Duration (* timeoutMs ) * time .Millisecond
1694+ if req > maxTimeout {
1695+ timeout = maxTimeout
1696+ } else {
1697+ timeout = req
1698+ }
1699+ }
1700+
1701+ receiptCtx , cancel := context .WithTimeout (ctx , timeout )
1702+ defer cancel ()
1703+
1704+ // Fast path.
1705+ if r , err := api .GetTransactionReceipt (receiptCtx , hash ); err == nil && r != nil {
1706+ return r , nil
1707+ }
1708+
1709+ for {
1710+ select {
1711+ case <- receiptCtx .Done ():
1712+ // If server-side wait window elapsed, return the structured timeout.
1713+ if errors .Is (receiptCtx .Err (), context .DeadlineExceeded ) {
1714+ return nil , & txSyncTimeoutError {
1715+ msg : fmt .Sprintf ("The transaction was added to the transaction pool but wasn't processed in %v." , timeout ),
1716+ hash : hash ,
1717+ }
1718+ }
1719+ return nil , receiptCtx .Err ()
1720+
1721+ case err , ok := <- subErrCh :
1722+ if ! ok {
1723+ return nil , errSubClosed
1724+ }
1725+ return nil , err
1726+
1727+ case ev , ok := <- ch :
1728+ if ! ok {
1729+ return nil , errSubClosed
1730+ }
1731+ rs := ev .Receipts
1732+ txs := ev .Transactions
1733+ if len (rs ) == 0 || len (rs ) != len (txs ) {
1734+ continue
1735+ }
1736+ for i := range rs {
1737+ if rs [i ].TxHash == hash {
1738+ if rs [i ].BlockNumber != nil && rs [i ].BlockHash != (common.Hash {}) {
1739+ signer := types .LatestSigner (api .b .ChainConfig ())
1740+ return MarshalReceipt (
1741+ rs [i ],
1742+ rs [i ].BlockHash ,
1743+ rs [i ].BlockNumber .Uint64 (),
1744+ signer ,
1745+ txs [i ],
1746+ int (rs [i ].TransactionIndex ),
1747+ ), nil
1748+ }
1749+ return api .GetTransactionReceipt (receiptCtx , hash )
1750+ }
1751+ }
1752+ }
1753+ }
1754+ }
1755+
16691756// Sign calculates an ECDSA signature for:
16701757// keccak256("\x19Ethereum Signed Message:\n" + len(message) + message).
16711758//
0 commit comments