Resolve Nonce Holes
Identify and resolve nonce holes that cause stuck transactions on ETH-like coins. Includes flows for Custody MPC, Self-Custody MPC (Simple and Manual), and Go Account (Simple and Manual) wallets. See the Guide.
-
Query the wallet for potentially stuck transactions on ETH-like coins. Stuck transactions caused by nonce holes return
"cause": "nonceHole"and"userActionDisabled": false. Save thenoncevalue from the response for use in the next step.You can subscribe to the
stuckTxwallet webhook to receive notifications when a transaction becomes stuck. For more information, see Create Wallet Webhooks.Prerequisites: You must have completed the Get Started guide and initiated a withdrawal (see Withdraw Overview).
-
For Custody MPC wallets, create a new transaction that includes the
noncevalue from the stuck transaction identified in the prior step. SetintentTypeto"fillNonce"and pass thenoncevalue to fill the gap, using the Create Transaction Request endpoint.Withdrawal flows differ by wallet type. View the end-to-end transaction flow and integration guides for your wallet type at Withdraw Overview.
-
For self-custody MPC wallets using the simple SDK flow, create a new transaction that includes the
noncevalue from the stuck transaction identified in the prior step. Settypeto"fillNonce"and pass thenoncevalue to fill the gap, using the BitGo SDKsendManymethod.The SDK authenticates, unlocks the session, then sends a transaction with
type: "fillNonce"and the nonce from the stuck transaction. -
For self-custody MPC wallets using the manual REST flow, create a new transaction that includes the
noncevalue from the stuck transaction identified in the prior step. SetintentTypeto"fillNonce"and pass thenoncevalue to fill the gap, using the Create Transaction Request endpoint. -
For Go Account wallets using the simple flow, create a new transaction that includes the
noncevalue from the stuck transaction identified in the prior step. Settypeto"fillNonce"and pass thenoncevalue to fill the gap, using the Send Transaction endpoint through BitGo Express. If withdrawing to another Go Account, passwalletIdinstead ofaddress. -
For Go Account wallets using the manual flow, create a new transaction that includes the
noncevalue from the stuck transaction identified in the prior step. Settypeto"fillNonce"and pass thenoncevalue to fill the gap, using the Build Transaction endpoint. If sending to another Go Account, use thewalletIdparameter instead ofaddress.
// 1. Identify Stuck Transactions
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"
// 2. Custody MPC: Resolve Nonce Holes
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",
"nonce": "'"$NONCE"'",
"sequenceId": "4dEeycHsDSsCAG1zYPKGSxyPkHQ",
"feeOptions": {
"unit": "baseUnit",
"formula": "fixed",
"feeType": "base",
"gasLimit": 0,
"gasPrice": 0
},
"receiveAddress": "string",
"senderAddressIndex": 0
},
"videoApprovers": [
"585951a5df8380e0e3063e9f",
"585951a5df8380e0e304a553"
],
"apiVersion": "lite",
"preview": false
}'
// 3. Self-Custody MPC (Simple): Resolve Nonce Holes
// Use the JavaScript SDK for this step (see JavaScript tab)
// 4. Self-Custody MPC (Manual): Resolve Nonce Holes
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",
"nonce": "'"$NONCE"'",
"sequenceId": "4dEeycHsDSsCAG1zYPKGSxyPkHQ",
"feeOptions": {
"unit": "baseUnit",
"formula": "fixed",
"feeType": "base",
"gasLimit": 0,
"gasPrice": 0
},
"receiveAddress": "string",
"senderAddressIndex": 0
},
"videoApprovers": [
"585951a5df8380e0e3063e9f",
"585951a5df8380e0e304a553"
],
"apiVersion": "lite",
"preview": false
}'
// 5. Go Account (Simple): Resolve Nonce Holes
export BITGO_EXPRESS_HOST="<YOUR_LOCAL_HOST>"
export COIN="<OFC_ASSET_ID>"
export ADDRESS="<DESTINATION_ADDRESS_OR_ID_HASH>"
export GO_ACCOUNT="<DESTINATION_WALLET_ID>"
export AMOUNT="<AMOUNT_IN_BASE_UNITS>"
export WALLET_PASSPHRASE="<YOUR_WALLET_PASSPHRASE>"
export NONCE="<NONCE_VALUE_OF_STUCK_TRANSACTION>"
curl -X POST \
http://$BITGO_EXPRESS_HOST/api/v2/$COIN/wallet/$WALLET_ID/sendcoins \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"address": "'"$ADDRESS"'",
"walletId": "'"$GO_ACCOUNT"'",
"amount": "'"$AMOUNT"'",
"walletPassphrase": "'"$WALLET_PASSPHRASE"'",
"type": "fillNonce",
"nonce": "'"$NONCE"'"
}'
// 6. Go Account (Manual): Resolve Nonce Holes
export AMOUNT="<AMOUNT_IN_BASE_UNITS>"
export ADDRESS="<DESTINATION_ADDRESS>"
export NONCE="<NONCE_VALUE_OF_STUCK_TRANSACTION>"
curl -X POST \
https://app.bitgo-test.com/api/v2/$COIN/wallet/$WALLET_ID/tx/build \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"type": "fillNonce",
"nonce": "'"$NONCE"'",
"idfSignedTimestamp": "2025-01-28T10:55:38.732Z",
"idfUserId": "628ca09d1b78a6022750254f0777561a",
"idfVersion": 1,
"instant": false,
"preview": false,
"recipients": [
{
"amount": "'"$AMOUNT"'",
"address": "'"$ADDRESS"'"
}
],
"sequenceId": "4dEeycHsDSsCAG1zYPKGSxyPkHQ"
}'
// 1. Identify Stuck Transactions
// Use the REST API for this step (see cURL tab)
// 2. Custody MPC: Resolve Nonce Holes
// Use the REST API for this step (see cURL tab)
// 3. Self-Custody MPC (Simple): Resolve Nonce Holes
import * as dotenv from "dotenv";
import {EnvironmentName} from "bitgo";
import {BitGoAPI} from "@bitgo/sdk-api";
import {Hteth} from "@bitgo/sdk-coin-eth";
dotenv.config();
const config = {
USERNAME: process.env.USERNAME as string,
PASSWORD: process.env.PASSWORD as string,
ENV: process.env.ENV as EnvironmentName,
OTP: process.env.OTP as string,
ENTERPRISE_ID: process.env.ENTERPRISE_ID as string,
WALLET_ID: process.env.WALLET_ID as string,
WALLET_PASS_PHRASE: process.env.WALLET_PASS_PHRASE as string
};
const bitgo = new BitGoAPI({env: config.ENV});
bitgo.register("hteth", Hteth.createInstance);
const coin = bitgo.coin("hteth");
async function auth() {
await bitgo.authenticate({
username: config.USERNAME,
password: config.PASSWORD,
otp: config.OTP,
});
await bitgo.lock();
await bitgo.unlock({otp: "000000", duration: 3600});
}
async function main() {
await auth();
const destinatonAddress = "0x1037c88b10fbd0754b9fbd3ba7be41fe7cb61f59";
const wallet = await coin.wallets().get({id: config.WALLET_ID});
const sendAmount = wallet.balanceString() ?? 0;
const res = await wallet.sendMany({
walletPassphrase: config.WALLET_PASS_PHRASE,
recipients: [{address: destinatonAddress, amount: sendAmount}],
type: "fillNonce",
nonce: 200000,
});
console.log(res);
}
main().catch((err) => console.error(err));
// 4. Self-Custody MPC (Manual): Resolve Nonce Holes
// Use the REST API for this step (see cURL tab)
// 5. Go Account (Simple): Resolve Nonce Holes
// Use the REST API for this step (see cURL tab)
// 6. Go Account (Manual): Resolve Nonce Holes
// Use the REST API for this step (see cURL tab)
// 1. Identify Stuck Transactions Response
[
{
"nonce": 200000,
"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",
"gasAccelerationFee": {
"gasPrice": "200000",
"maxFeePerGas": "200000",
"maxPriorityFeePerGas": "200000"
},
"userActionDisabled": false
}
]
// Resolve Nonce Holes Response (applies to all wallet types)
{
"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
}