Resolve Nonce Holes
Overview
Nonces are single-use numbers that prevent replay attacks by ensuring each transaction is unique. A nonce hole occurs when a transaction broadcasts with a nonce that skips the expected sequential value, creating a gap. This gap often results from network failures, crashes, or out-of-order execution. Therefore, resolving nonce holes is critical to ensure that transactions with higher nonces don't remain stuck.
Common causes of nonce holes include:
- Dropped Messages - If messages containing nonces are lost, other parties may expect a missing nonce.
- Asynchronous Execution - Some protocols assume a sequential nonce usage, but real-world execution may be out of order.
- Crashes and Restarts - A participant may lose track of the last used nonce if no persistence mechanism exists.
Prerequisites
- Get Started
- Initiate a withdrawal (see Transact Overview).
1. Identify Stuck Transactions
Endpoint: Get list of potentially stuck transactions and their nonces for eth-like coins
1 2 3 4 5 6 7 8
export COIN="<ASSET_ID>" export WALLET_ID="<YOUR_WALLET_ID>" export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>" curl -X GET \ "https://app.bitgo-test.com/api/v2/$COIN/wallet/$WALLET_ID/potentialStuckTxs" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ACCESS_TOKEN"
Step Result
Stuck transactions return:
"cause": "nonceHole"
"userActionDisabled": false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
[ { "nonce": 200000, # save this nonce value for use in the following step "txHex": "01000000000101d...a53aec8b11400", "txId": "b8a828b98dbf32d9fd1875cbace9640ceb8c82626716b4a64203fdc79bb46d26", "sendTransfer": { "baseValueString": "2000000", "bitgoOrg": "BitGo Trust", "coin": "hteth", "date": "2025-01-24T14:15:22Z", "enterprise": "59cd72485007a239fb00282ed480da1f", "history": [ { "action": "created", "date": "2025-01-24T14:15:22Z", "txid": "b8a828b98dbf32d9fd1875cbace9640ceb8c82626716b4a64203fdc79bb46d26", "user": "59cd72485007a239fb00282ed480da1f" } ], "id": "59cd72485007a239fb00282ed480da1f", "organization": "59cd72485007a239fb00282ed480da1f", "pendingApproval": "664ed267aad92c62a183ac5f28883495", "state": "initialized", "txid": "b8a828b98dbf32d9fd1875cbace9640ceb8c82626716b4a64203fdc79bb46d26", "type": "send", "valueString": "2000000", "wallet": "59cd72485007a239fb00282ed480da1f", "walletType": "custodial" }, "cause": "nonceHole", # identifies the cause of the stuck transaction is a nonce hole "gasAccelerationFee": { "gasPrice": "200000", "maxFeePerGas": "200000", "maxPriorityFeePerGas": "200000" }, "userActionDisabled": false # identifies that the transaction is stuck } ]
2. Resolve Nonce Holes
Create a new transaction that includes the nonce
value returned in the prior step.
Note: Transaction flows differ by wallet type. View the end-to-end transaction flow and integration guides for your wallet type at Transact Overview.
Endpoints:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
export WALLET_ID="<WALLET_ID>" export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>" export NONCE="<NONCE_VALUE_OF_STUCK_TRANSACTION>" curl -X POST "https://app.bitgo-test.com/api/v2/wallet/$WALLET_ID/txrequests" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -d '{ "idempotencyKey": "string", "intent": { "intentType": "fillNonce", # Required to fill the nonce hole "nonce": "'"$NONCE"'", # Required to fill the nonce hole "sequenceId": "4dEeycHsDSsCAG1zYPKGSxyPkHQ", "feeOptions": { "unit": "baseUnit", "formula": "fixed", "feeType": "base", "gasLimit": 0, "gasPrice": 0 }, "receiveAddress": "string", "senderAddressIndex": 0 }, "videoApprovers": [ "585951a5df8380e0e3063e9f", "585951a5df8380e0e304a553" ], "apiVersion": "lite", "preview": false }'
Step Result
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
{ "txRequestId": "string", "version": 0, "latest": true, "walletId": "string", "walletType": "cold", "enterpriseId": "string", "state": "initialized", "date": {}, "createdDate": {}, "userId": "string", "initiatedBy": "string", "updatedBy": "string", "intent": { "intentType": "fillNonce", "nonce": "string", "sequenceId": "string", "comment": "string", "feeOptions": { "unit": "baseUnit", "formula": "fixed", "feeType": "base", "gasLimit": 0, "gasPrice": 0 }, "receiveAddress": "string", "senderAddressIndex": 0 }, "pendingApprovalId": "string", "isCanceled": true,}
Next
Continue the transaction flow for your wallet. For more details, see Transact Overview.