Resolve Nonce Holes - Self-Custody MPC (Simple)
Identify and resolve nonce holes that cause stuck transactions on ETH-like coins using the BitGo JavaScript SDK. This flow is for self-custody MPC wallets using the simple SDK approach. For other wallet types, see the cURL cookbook. 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 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 other wallet types (Custody MPC, Self-Custody MPC Manual, Go Account Simple, Go Account Manual), see the cURL cookbook.
// 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"
// Use the JavaScript SDK for this step (see JavaScript tab)
// 1. Identify Stuck Transactions
// Use the REST API for this step (see cURL tab)
// 2. 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));
// 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
}
]
// 2. Resolve Nonce Holes Response
{
"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
}