Enable Bulk ERC20 Withdrawals - JavaScript
Approve the batcher smart contract to enable bulk ERC20 token withdrawals using the BitGo JavaScript SDK. Includes flows for Custody MPC, Custody Multisig, Self-Custody MPC (Hot and Cold), and Self-Custody Multisig (Hot and Cold) wallets. See the Guide.
-
Before approving tokens, you can check if your wallet has already approved the batcher contract using the REST API (see the cURL cookbook). The large number shown in the response (
2^256-1) is the maximum approval amount (unlimited).Approving the batcher smart contract is a one-time operation, but each token requires a separate approval in each wallet.
Prerequisites: Complete Get Started, Create Wallets, and Deposit Assets (you must have the ERC20 token in the wallet).
-
Build an approval transaction for the ERC20 token to authorize the batcher smart contract to transfer tokens on your behalf. The SDK method depends on your wallet type:
- Custody MPC: Use
sendManywithisTss: trueandtype: 'tokenApproval'. - Custody Multisig: Use
sendManywithtype: 'tokenApproval'(noisTssflag). - Self-Custody MPC: Use
sendManywithisTss: true,type: 'tokenApproval',walletPassphrase, andfeeOptions. - BitGo Offline Vault MPC: Use
sendManywithisTss: true,type: 'tokenApproval', andfeeOptions(no passphrase — sign offline). - Self-Custody Multisig: Use
buildErc20TokenApprovalwithtokenNameandwalletPassphraseto build, sign, and send in one operation. - BitGo Offline Vault Multisig: Use
buildErc20TokenApprovalto get the unsigned transaction, sign offline using the OVC, then send the half-signed transaction withsendTransaction.
Transactions for approving the batcher smart contract follow the transaction flow of the wallet type, and must receive all required signatures and approvals before broadcasting to the blockchain. Once you sign and broadcast your approval transaction, you can Bulk Withdraw ERC20 Tokens.
- Custody MPC: Use
// 1. (Optional) Check Token Allowance
// Use the REST API for this step (see cURL cookbook)
// 2. Approve Batcher Smart Contract
async function approveToken() {
const walletInstance = await bitgo
.coin('opeth')
.wallets()
.get({ id: '<YOUR_WALLET_ID>' });
// For custodial MPC wallets, use sendMany with type: 'tokenApproval'
const result = await walletInstance.sendMany({
isTss: true,
type: 'tokenApproval',
tokenName: 'opeth:usdc'
});
return result;
}
// 1. (Optional) Check Token Allowance
// Use the REST API for this step (see cURL cookbook)
// 2. Approve Batcher Smart Contract
async function approveToken() {
const walletInstance = await bitgo
.coin('opeth')
.wallets()
.get({ id: '<YOUR_WALLET_ID>' });
// For custodial multisig wallets, use sendMany with type: 'tokenApproval'
const result = await walletInstance.sendMany({
type: 'tokenApproval',
tokenName: 'opeth:usdc'
});
return result;
}
// 1. (Optional) Check Token Allowance
// Use the REST API for this step (see cURL cookbook)
// 2. Approve Batcher Smart Contract
async function approveToken() {
const walletInstance = await bitgo
.coin('opeth')
.wallets()
.get({ id: '<YOUR_WALLET_ID>' });
// For self-custody MPC wallets, use sendMany with isTss flag
const result = await walletInstance.sendMany({
isTss: true,
type: 'tokenApproval',
tokenName: 'opeth:usdc',
walletPassphrase: 'VerySecurePassword1234',
feeOptions: {
maxFeePerGas: '30000000001',
maxPriorityFeePerGas: '30000000000'
}
});
return result;
}
// 1. (Optional) Check Token Allowance
// Use the REST API for this step (see cURL cookbook)
// 2. Approve Batcher Smart Contract
async function approveToken() {
const walletInstance = await bitgo
.coin('opeth')
.wallets()
.get({ id: '<YOUR_WALLET_ID>' });
// For BitGo Offline Vault MPC wallets, similar to Self-Custody MPC but without passphrase
// User will need to sign the transaction offline
const result = await walletInstance.sendMany({
isTss: true,
type: 'tokenApproval',
tokenName: 'opeth:usdc',
feeOptions: {
maxFeePerGas: '30000000001',
maxPriorityFeePerGas: '30000000000'
}
});
return result;
}
// 1. (Optional) Check Token Allowance
// Use the REST API for this step (see cURL cookbook)
// 2. Approve Batcher Smart Contract
async function approveToken() {
const walletInstance = await bitgo
.coin('opeth')
.wallets()
.get({ id: '<YOUR_WALLET_ID>' });
// self-custody multisignature wallets can use buildErc20TokenApproval with walletPassphrase
// to build, sign, and send in one operation
const result = await walletInstance.buildErc20TokenApproval(
'opeth:opeth:usdc', // tokenName
'VerySecurePassword1234' // walletPassphrase
);
return result;
}
// 1. (Optional) Check Token Allowance
// Use the REST API for this step (see cURL cookbook)
// 2. Approve Batcher Smart Contract
async function approveToken() {
const walletInstance = await bitgo
.coin('opeth')
.wallets()
.get({ id: '<YOUR_WALLET_ID>' });
// For BitGo Offline Vault Multisig wallets, get the unsigned transaction
const tokenApprovalBuild = await walletInstance.buildErc20TokenApproval('opeth:usdc');
// Sign offline using the OVC
// ...
// Send the half-signed transaction
const sendResult = await walletInstance.sendTransaction({
txHex: halfSignedTxHex,
});
return sendResult;
}
// 1. (Optional) Check Token Allowance Response
{
"allowance": "115792089237316195423570985008687907853269984665640564039457584007913129639935"
}
// Approve Batcher Smart Contract Response (applies to all wallet types)
{
"pendingApproval": {
"id": "63727a81cdbc820007b27caa7b76016d",
"coin": "eth",
"wallet": "63726fde0a3c94000758f2790536041d",
"state": "pendingFinalApproval",
"scope": "wallet",
"info": {
"type": "transactionRequest",
"transactionRequest": {
"coinSpecific": {
"eth": {
"eip1559": {
"maxPriorityFeePerGas": 1500000000,
"maxFeePerGas": 133302263906
},
"recipients": [
{
"amount": "0",
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"data": "0x095ea7b3000000000000000000000000..."
}
]
}
}
}
}
}
}