How to Troubleshoot "!BAL20" Error When Buying NFTs on Marketplace v3
Overview
When purchasing NFTs from a thirdweb Marketplace v3 contract, you might encounter a TransactionError: Error - !BAL20
error. This guide explains what causes this error and how to resolve it.
Understanding the Error
The !BAL20
error occurs during transaction validation when either:
- The buyer's wallet doesn't have enough token balance, OR
- The buyer hasn't provided sufficient allowance for the marketplace contract to spend their tokens
Root Cause
The error is thrown from this contract function:
solidity
function _validateERC20BalAndAllowance(address _tokenOwner, address _currency, uint256 _amount) internal view {
require(
IERC20(_currency).balanceOf(_tokenOwner) >= _amount &&
IERC20(_currency).allowance(_tokenOwner, address(this)) >= _amount,
"!BAL20"
);
}
Common Pitfalls
- Token Decimal Mismatch: The most common cause is incorrectly calculating the token amount, especially when converting between human-readable values and on-chain values.
- TransactionButton Timing: When using the
TransactionButton
component, approval transactions and purchase transactions may have timing issues. - Contract Addresses: Using incorrect spender addresses for approvals.
Step-by-Step Troubleshooting
1. Verify Token Decimals
Always explicitly get the token decimals and use them for calculations:
javascript
// Get token decimals
const tokenDecimals = await decimals({ contract: tokenContract });
console.log(`Token decimals: ${tokenDecimals}`);
// Convert price to raw amount
const rawAmount = BigInt(Math.floor(price * 10**tokenDecimals));
2. Check Balance and Allowance
Explicitly check both balance and allowance before proceeding:
javascript
// Check wallet balance
const walletBalance = await balanceOf({
contract: tokenContract,
address: walletAddress,
});
// Check current allowance
const currentAllowance = await allowance({
contract: tokenContract,
owner: walletAddress,
spender: marketplaceAddress,
});
// Log values for debugging
console.log(`Price: ${rawAmount} raw units`);
console.log(`Wallet balance: ${walletBalance} raw units`);
console.log(`Current allowance: ${currentAllowance} raw units`);
// Verify both conditions
if (walletBalance < rawAmount) {
throw new Error(`Insufficient token balance. Need ${rawAmount} but have ${walletBalance}`);
}
if (currentAllowance < rawAmount) {
// Need to increase allowance
}
3. Separate Approval and Purchase
For more reliable transactions, separate the approval and purchase steps:
javascript
// 1. First handle approval if needed
if (currentAllowance < rawAmount) {
const approveTx = await approve({
contract: tokenContract,
spender: marketplaceAddress,
amount: rawAmount,
});
// Wait for approval to be confirmed
const approvalReceipt = await sendAndConfirmTransaction({
transaction: approveTx,
account: activeAccount,
});
console.log('Approval confirmed in block', approvalReceipt.blockNumber);
}
// 2. Then handle the purchase separately
console.log('Creating purchase transaction');
const purchaseTx = await buyFromListing({
contract: marketplaceContract,
listingId: BigInt(listingId),
quantity: quantity,
recipient: walletAddress,
});
const purchaseReceipt = await sendAndConfirmTransaction({
transaction: purchaseTx,
account: activeAccount,
});
4. Verify Listing Details
Ensure you're working with valid listing data:
javascript
// Get listing details
const listing = await getListing({
contract: marketplaceContract,
listingId: BigInt(listingId),
});
console.log('Listing details:', {
currencyAddress: listing.currencyValuePerToken.currency,
pricePerToken: listing.currencyValuePerToken.value,
quantity: listing.quantity,
});
Additional Tips
- Use Larger Allowances: Consider approving more tokens than needed to prevent future approval transactions.
- Check Listing Status: Ensure the listing is still active and has available quantity.
- Network Consistency: Verify all operations are on the same network.
- Test with Manual Transactions: If
TransactionButton
causes issues, try manual transactions first to isolate the problem.
Conclusion
The !BAL20
error is most commonly caused by insufficient balance or allowance issues. By carefully verifying token amounts, ensuring proper approvals, and separating approval from purchase transactions, you can resolve this error and successfully complete NFT purchases on thirdweb Marketplace v3.