Skip to main content
All articles

Can You Send a Transfer Again After a Timeout?

· 14 min read
Bankware Global Engineering

You press the transfer button. After a long wait, the screen says, "Request timed out." You cannot tell whether the recipient received the tokens. Should you press it again?

The wait for a response has certainly ended. How far the transaction progressed during that wait is still unknown. The request may never have reached the server, or the transaction may have been submitted to the blockchain or already executed. Creating a new transfer without checking this difference can turn one intended payment into two movements of assets.

Middleware such as BXB, which connects business servers to blockchains, needs to handle this gap. We will follow a transfer of 10 tokens to distinguish querying the existing transaction again, forwarding the same signed transaction again, and creating a new transaction.

We sent 10 tokens but received no response​

Suppose A holds 100 units of token T and B holds 0 on a network using the EVM, the Ethereum-compatible execution environment. A business server intends to send 10 T from A to B. One successful transfer leaves A with 90 T and B with 10 T.

Assume T has zero decimal places, with no minting, burning, or token transfer fees. There are no concurrent transactions, and A separately holds the native currency needed for network fees. This is a standalone illustrative example, not a reproduction of a production transaction.

The business server assigns request ID R17 to this transfer intent. Suppose the first EVM transaction created to carry it out has nonce 42, and the signed transaction has hash H1. R17 and H1 are illustrative names, not literal fields from BXB's API or actual transaction hashes.

If we later established everything that happened, the timeline might look like this:

Request R17: A → B, 10 T
↓
Sign tx (nonce 42) → H1
↓
Submit to a node
↓
Server stops waiting
↓
H1 succeeds on-chain
↓
Later query finds the result

The timeout in the middle does not rewind this sequence or cancel a transaction the node has already received. This timeline is one of several possible cases. The timeout screen alone does not tell us which stage has been reached.

Whether one person signed or several participants signed jointly through MPC does not remove this problem. As the article on MPC relays and sessions distinguishes, completing a signature and confirming on-chain execution each have their own completion conditions. Here, we will focus on handling a transaction after its signature has been produced.

One transfer needs several identifiers​

To find the existing work after a timeout, we first need to decide what counts as the same thing. A customer's transfer intent, a signed transaction, and its order on-chain refer to different objects.

IdentifierWhat it identifies in the example
Business request ID R17A's intent to send 10 T to B once
Transaction hash H1One EVM transaction with specific contents and a signature
A's nonce 42A value that determines the order of A's transactions on this network

An EVM transaction nonce is a sequence value associated with an address. It is a public field, distinct from the secret one-time values used in MPC computation. Nodes and the chain use it to check transaction ordering, but they do not automatically know what the server's R17 means.

The transaction hash is also needed. For an EIP-1559 transaction such as the one in this example, the hash can be calculated from the signed transaction data. A successful response from a node is therefore not a prerequisite for identifying the transaction. Keeping the same signed bytes produces the same hash. Changing the nonce or fee terms and signing again, by contrast, may create a different transaction to track. The EIP-1559 transaction format shows how the nonce, fees, call data, and signature form one transaction.

The business request ID must therefore be stored together with its transaction hash. The network must also be identified so that transactions on different chains, or other transactions from the same address, are not confused. If a replacement transaction with adjusted fees is created later, more than one candidate hash may be associated with R17.

Using this relationship for recovery requires a design that durably records the necessary identifiers and progress before external submission. Even after a server restart, status queries need to resume from a record of which transfer was attempted. This is a design requirement for business integration, not a claim that every BXB path implements the same storage order.

Three different actions can all be called a retry​

The first is querying again: reading H1's transaction information and execution result. This produces no new signature and adds no transfer. After a timeout, start by connecting the existing request record to these query results.

The second is rebroadcasting the same signed transaction: sending the stored transaction bytes to a node again without changing them. Its nonce remains 42, and its hash remains H1. Within one valid EVM chain history, the same sender's same nonce cannot be consumed twice. Forwarding this identical transaction again therefore does not itself move 10 T twice.

That does not guarantee a successful response every time. A node may treat the transaction as already known or report that its nonce has already been used. Even then, the actual outcome of H1 must be checked. Rebroadcasting an already signed transaction does not require a new wallet signature or another MPC computation.

The third is creating a new transaction: calling the "send 10 T from A to B" API from the beginning, obtaining a new nonce, and signing again. Even when the business details are the same, the chain may see a separate valid transaction.

For example, suppose H1 succeeds and the server then creates the same transfer with the next nonce, 43. If this new transaction, H2, also succeeds, the balances change as follows:

Executed transactionA's TB's T
Before execution1000
H1 succeeds: 10 T moved9010
H2 also succeeds: another 10 T moved8020

The total remains 100 tokens. The problem is that an intended one-time transfer was executed twice, even though the token total was conserved. Both transactions correctly used different nonces, so nonce checks alone cannot prevent this duplication.

A replacement transaction that keeps nonce 42 but raises the fee and is signed again is another case to distinguish. It competes for the same position in the sequence as the original transaction, with a different hash. Geth's transaction pool documentation explains that multiple candidate transactions can share an address and nonce. Submitting a replacement does not establish that the original has been canceled; which candidate executes must be tracked. The scope of authorization for fee changes and the conditions for accepting a replacement must also be applied separately.

No result is still an observation​

In the EVM, a receipt records the execution result of a transaction included in a block. eth_getTransactionReceipt queries it by transaction hash and returns null if no receipt is found. A transaction that has not yet been included in a block has no receipt.

Read null as "this node's query has not found a receipt yet." The transaction may not have been submitted, may be known only to another node, or may be pending. The absence of a result does not establish that the transaction will never execute. For the same reason, balances that still appear to be 100 T for A and 0 T for B do not provide sufficient grounds to treat it as canceled.

Once a receipt is available, distinguish successful execution from failed execution. For the ordinary EVM transaction in this example, a status of 0 means execution failed and the token transfer was reverted. A retains 100 T and B retains 0 T. Because the transaction was included in a block and execution was attempted, however, A pays gas in the native currency and the nonce is consumed. This differs from a transaction that was never submitted.

A status of 1 means transaction execution succeeded. The next step is to verify the intended movement of 10 T. Match sender A, recipient B, and amount 10 in token T's events, and connect this to any necessary state queries. The ERC-20 standard requires callers to handle a false return value as well. Some implementations return failure without raising an exception, so a successful execution receipt cannot be equated with business success for every contract. A function's return value is not simply included in the receipt either.

Finally, distinguish finding an execution result from deciding that the business operation is complete. Apply the network's confirmation criteria, taking into account reorganizations, or reorgs, in which blocks are replaced during consensus. Some networks have separate consensus conditions, such as Ethereum's proof-of-stake finality. One receipt query cannot therefore be treated as final confirmation across every EVM network.

BXB separates submission records from result confirmation​

In BXB's ordinary EVM execution path, signing and submitting a transaction to a node is separate from waiting for its receipt and recording its execution result. The submission record is associated with the transaction hash, nonce, and other values needed for tracking. Subsequent confirmation fills in the gas used and execution result.

This distinction connects to the meaning of a transaction hash response in the article on exposing contracts through REST APIs. The hash returned by the API is a starting point for later queries. Determining that the customer's "send 10 tokens" operation is complete requires a separate check.

The EVM implementation examined also includes a collector that periodically queries receipts for stored transactions whose results remain unconfirmed. Its targets are stored records that have transaction hashes and match the query conditions. It fills in results within configured time and processing limits. It cannot be interpreted as automatically recovering every timed-out request, regardless of whether a record exists. This collection task reads existing results; it does not create new transfers.

Filling in transaction logs and recovering business state are also different tasks. For example, even if an on-chain transfer is found to have succeeded, the business server's payment request may still say "processing." The request needs to be connected to that same transaction result and its status updated. The work needed here is to apply the result, not to start another transfer to the customer.

The conditions for waiting on results also differ across chains. BXB's Solana path includes a flow that does not immediately record a timeout while waiting for a result as execution failure, but instead queries the later status using the transaction identifier. The XRPL submission path includes handling that records an uncertain outcome and the transaction hash. The common requirement is to preserve an "unknown for now" state and continue checking afterward.

Specific retry conditions follow each chain's rules. For ordinary Solana transactions using a recent blockhash, consider the validity period and last valid block height together. XRPL uses results from validated ledgers and LastLedgerSequence. Even after a validity period has expired, whether the transaction executed before expiry must be checked; expiry alone does not justify a new transfer. Tracking results across chains starts by recognizing that these conditions do not follow the same rules as an EVM nonce. What to unify in a multichain API and which differences to preserve compares asset identification, costs, and recipient requirements through independent transfers on EVM, Cardano, Solana, and XRPL.

What makes a business request ID prevent duplicates?​

Writing R17 in a log does not itself block duplicate execution. A correlation ID is useful for connecting multiple logs to one request, but it does not replace a check that prevents new transaction creation when the same request arrives again.

Idempotency is the property that repeating the same request produces no additional effect. Applying it to this transfer operation requires integration rules such as the following. These are design criteria for the example, not a list of guarantees shared by all BXB APIs.

  • Before accepting requests, define the business principal and the scope of request IDs, and prevent changes to the recipient, token, or amount under the same ID.
  • Enforce uniqueness of the acceptance record so that only one new execution starts even when the same request arrives concurrently.
  • If an existing request is found, retrieve its candidate transactions and confirmation state, then return that information or direct the caller to query it.
  • Provide a recovery procedure between business records and chain submission so that existing submission attempts can be tracked even after the server stops.

A concrete example found in BXB is duplicate-key handling in the XRPL submission path. It checks for an existing request using the combination of network, wallet, and idempotencyKey, and also uses a uniqueness constraint on the record stored before submission. When the same key arrives again, it returns a conflict response and the stored transaction hash instead of starting a new submission.

This is a mechanism for recognizing the same request again. Assigning a fresh key on every call loses the connection that identifies the same transfer intent. Nor can this XRPL contract be extended to a guarantee for every API, including EVM APIs. Duplicate-key handling, on-chain nonce or sequence checks, and execution result queries address duplicates and uncertainty at different stages.

What state should a timed-out request end in?​

Return to the original R17. If H1 has already transferred 10 T successfully and the required confirmation conditions are satisfied, mark the associated business operation complete. The final balances are 90 T for A and 10 T for B. Losing the response is no reason to create an additional transfer.

If the result is still unknown, keep the operation in a state that indicates confirmation is in progress. If the transaction remains valid and rebroadcasting is deemed necessary, use the existing signed data. If its fees need to change, track the replacement transaction separately. In each case, distinguish this from executing the same business operation again with a new nonce.

If execution failure is established at the required confirmation level, a new attempt can be considered after its cause is addressed. Even when it is certain that the transaction was never submitted, first ensure that the components handling the old attempt cannot submit it later. A new attempt must retain its relationship to the original business request and the previous attempt records, allowing an operator to account for the complete outcome.

In this example, ending without submission or with execution failure leaves A with 100 T and B with 0 T. One successful transfer leaves A with 90 T and B with 10 T. Native-currency fees for execution attempted in a block are accounted for separately. While the result is unknown, neither set of balances can be declared the final outcome.

After a timeout, a transfer system needs to preserve the connection between the original transfer intent and its execution result. That connection makes it possible to decide whether to query again, rebroadcast the same transaction, or allow a new attempt. Making the confirmed result available through familiar database queries also requires reconciling ledger events with stored records. From blockchain transactions to database queries follows event decoding and ingestion to explain the gap between on-chain execution and the point when a query reflects its result.