This guide covers the most common issues developers encounter when implementing OneBalance contract calls and their solutions.

Quick Debug Tip: Most contract call failures happen during the prepare phase. Always validate your inputs before calling the API.

Common Errors

Including Manual Approval Calls

Most Common Error: Adding approve() calls to your transaction array will cause failures.

OneBalance automatically handles all token approvals through the allowanceRequirements field. Including manual approve() calls in your transaction array conflicts with this system and will cause the request to fail.

wrong-approval-approach.ts
// ❌ WRONG - This will fail
const badRequest = {
  calls: [
    {
      to: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC
      data: encodeFunctionData({
        abi: parseAbi(['function approve(address spender, uint256 amount)']),
        functionName: 'approve',
        args: [spenderAddress, amount]
      }),
      value: '0'
    },
    {
      to: spenderAddress,
      data: actualCallData,
      value: '0'
    }
  ]
  // This causes "Invalid call data" errors
};
correct-approval-approach.ts
// ✅ CORRECT - Use allowanceRequirements
const correctRequest = {
  calls: [{
    to: spenderAddress,
    data: actualCallData,
    value: '0'
  }],
  allowanceRequirements: [{
    assetType: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
    amount: amount.toString(),
    spender: spenderAddress
  }]
};

Invalid Tamper Proof Signature

This error occurs when the quote structure is modified after preparation. The tamper-proof signature validates the exact quote structure, so any changes will invalidate it.

Common Causes:

  • JSON key ordering changed during serialization
  • Extra fields added to the quote object
  • Quote passed through JSON.stringify/parse incorrectly
preserve-quote-structure.ts
// ❌ Wrong - modifies structure
const modifiedQuote = {
  ...preparedQuote,
  extraField: 'value'
};

// ❌ Wrong - changes key order
const reordered = JSON.parse(JSON.stringify(preparedQuote));

// ✅ Correct - preserve exact structure
preparedQuote.chainOperation.userOp.signature = signature;

Account Configuration Errors

Account validation ensures all required addresses are present and properly formatted. Missing or invalid addresses will cause authentication failures during quote preparation.

account-validation.ts
// ✅ Validate account before using
function validateAccount(account: Account) {
  if (!account.sessionAddress || !account.adminAddress || !account.accountAddress) {
    throw new Error('Missing required account addresses');
  }
  
  if (!isAddress(account.sessionAddress)) {
    throw new Error('Invalid session address format');
  }
  
  if (account.sessionAddress === account.adminAddress) {
    throw new Error('Session and admin addresses must be different');
  }
  
  return true;
}

API Response Errors

400 Bad Request

These errors occur when request data doesn’t match expected formats. Always validate input data before sending API requests.

Asset ID format errors:

asset-id-validation.ts
// ❌ Wrong formats
const wrong = [
  'usdc',
  'USDC', 
  'erc20:0x...',
  'ds:USDC'
];

// ✅ Correct format
const correct = 'ds:usdc';

Chain ID format errors:

chain-validation.ts
// ❌ Wrong formats
const wrongChains = ['base', '8453', 'Base'];

// ✅ Correct format
const correctChain = 'eip155:8453';

401 Unauthorized

This error indicates missing or invalid API key configuration. Verify your environment variables are set correctly.

api-key-check.ts
// Check your API key configuration
const headers = {
  'x-api-key': process.env.ONEBALANCE_API_KEY
};

if (!headers['x-api-key']) {
  throw new Error('OneBalance API key not configured');
}

422 Unprocessable Entity

Usually indicates business logic errors:

  • Insufficient balance
  • Amount below minimum ($0.50)
  • Unsupported token/chain combination

Balance & Amount Issues

Insufficient Balance

Always verify the account has sufficient balance before attempting operations. This prevents failed transactions and provides better user feedback.

balance-validation.ts
// Always check balance before operations
async function validateSufficientBalance(
  accountAddress: string,
  assetId: string,
  requiredAmount: bigint
) {
  const response = await apiGet('/v2/balances/aggregated-balance', {
    address: accountAddress,
    assetId
  });
  
  const assetBalance = response.balanceByAggregatedAsset.find(
    asset => asset.aggregatedAssetId === assetId
  );
  
  if (!assetBalance) {
    throw new Error(`No balance found for asset ${assetId}`);
  }
  
  const availableBalance = BigInt(assetBalance.balance);
  
  if (availableBalance < requiredAmount) {
    throw new Error(
      `Insufficient balance. Required: ${requiredAmount}, Available: ${availableBalance}`
    );
  }
  
  return true;
}

Decimal Calculation Errors

Token amounts must be calculated using the correct decimal places. Using wrong decimals is a common cause of “insufficient balance” or “amount too small” errors.

Common Token Decimals

  • USDC: 6 decimals
  • ETH/WETH: 18 decimals
  • WBTC: 8 decimals
  • DAI: 18 decimals

Amount Validation

Always use parseUnits() and verify the token’s actual decimals before calculations.

decimal-handling.ts
import { parseUnits, formatUnits } from 'viem';

// ✅ Correct decimal handling
function formatTokenAmount(amount: string, decimals: number) {
  // Convert user input to smallest unit
  const parsedAmount = parseUnits(amount, decimals);
  
  // Validate minimum amount ($0.50 equivalent)
  const minimumUSD = parseUnits('0.50', 6); // $0.50 in USDC terms
  
  return {
    amount: parsedAmount.toString(),
    formatted: formatUnits(parsedAmount, decimals)
  };
}

Signing & Execution Issues

Signature Validation

Verify signatures locally before sending to prevent failed transactions. This helps debug signing issues early in the development process.

signature-validation.ts
import { verifyTypedData } from 'viem';

async function validateSignature(
  account: Account,
  typedData: any,
  signature: string
) {
  const isValid = await verifyTypedData({
    address: account.sessionAddress as `0x${string}`,
    domain: typedData.domain,
    types: typedData.types,
    primaryType: typedData.primaryType,
    message: typedData.message,
    signature: signature as `0x${string}`
  });
  
  if (!isValid) {
    throw new Error('Invalid signature');
  }
  
  return true;
}

Transaction Status Monitoring

Implement proper status polling to track transaction progress and handle different completion states. This provides users with real-time feedback on their transactions.

status-monitoring.ts
async function waitForTransactionCompletion(
  quoteId: string,
  timeoutMs = 60000
): Promise<QuoteStatus> {
  const startTime = Date.now();
  
  while (Date.now() - startTime < timeoutMs) {
    const status = await apiGet('/status/get-execution-status', { quoteId });
    
    switch (status.status.status) {
      case 'COMPLETED':
        return status;
        
      case 'FAILED':
        throw new Error(`Transaction failed: ${status.quoteId}`);
        
      case 'REFUNDED':
        throw new Error(`Transaction refunded - likely amount too small or gas costs too high`);
        
      case 'PENDING':
        // Continue polling
        await new Promise(resolve => setTimeout(resolve, 2000));
        break;
        
      default:
        throw new Error(`Unknown status: ${status.status.status}`);
    }
  }
  
  throw new Error('Transaction timeout');
}

Contract-Specific Issues

DEX Integration Problems

DEX protocols have specific parameter requirements that must be validated before execution. Common issues include expired deadlines and invalid fee tiers.

dex-troubleshooting.ts
// Common Uniswap V3 issues
function validateUniswapV3Call(params: any) {
  // Check deadline is in future
  const now = Math.floor(Date.now() / 1000);
  if (params.deadline <= now) {
    throw new Error('Deadline must be in the future');
  }
  
  // Validate fee tier
  const validFees = [100, 500, 3000, 10000];
  if (!validFees.includes(params.fee)) {
    throw new Error(`Invalid fee tier: ${params.fee}`);
  }
  
  // Check recipient matches account
  if (params.recipient !== account.accountAddress) {
    console.warn('Recipient does not match account address');
  }
}

NFT Marketplace Issues

NFT purchases require additional validation for contract addresses and reasonable price limits. This helps prevent accidental overpayments or invalid transactions.

nft-troubleshooting.ts
// Common NFT purchase validation
function validateNFTPurchase(contractAddress: string, tokenId: string, price: bigint) {
  if (!isAddress(contractAddress)) {
    throw new Error('Invalid NFT contract address');
  }
  
  // Check if price seems reasonable (basic sanity check)
  const maxReasonablePrice = parseUnits('1000', 6); // $1000 USDC
  if (price > maxReasonablePrice) {
    console.warn('Price seems unusually high - please verify');
  }
  
  return true;
}

Debugging Tools

Enhanced Logging

Comprehensive logging helps identify issues quickly during development. This debug function provides structured output for all request parameters.

debug-helpers.ts
// Comprehensive request logging
function debugPrepareRequest(request: PrepareCallRequest) {
  console.group('🔍 OneBalance Prepare Request Debug');
  
  console.log('📋 Account:', {
    session: request.account.sessionAddress,
    admin: request.account.adminAddress,
    smart: request.account.accountAddress
  });
  
  console.log('🌐 Target Chain:', request.targetChain);
  
  console.log(`📞 Calls (${request.calls.length}):`);
  request.calls.forEach((call, i) => {
    console.log(`  Call ${i + 1}:`, {
      to: call.to,
      value: call.value,
      dataLength: call.data.length,
      dataPreview: call.data.slice(0, 10) + '...'
    });
  });
  
  console.log(`💰 Tokens Required (${request.tokensRequired.length}):`);
  request.tokensRequired.forEach((token, i) => {
    console.log(`  Token ${i + 1}:`, token);
  });
  
  console.log(`🔐 Allowances Required (${request.allowanceRequirements.length}):`);
  request.allowanceRequirements.forEach((allowance, i) => {
    console.log(`  Allowance ${i + 1}:`, allowance);
  });
  
  console.groupEnd();
}

Health Check Function

Run this function before implementing your main logic to verify all components are working correctly. It tests API connectivity, account prediction, and balance retrieval.

health-check.ts
async function performHealthCheck(account: Account) {
  const checks = [];
  
  try {
    // 1. Check API connectivity
    await apiGet('/chains/supported-list', {});
    checks.push('✅ API connectivity');
  } catch {
    checks.push('❌ API connectivity failed');
  }
  
  try {
    // 2. Check account prediction
    const prediction = await apiPost('/account/predict-address', {
      sessionAddress: account.sessionAddress,
      adminAddress: account.adminAddress
    });
    checks.push(`✅ Account prediction: ${prediction.predictedAddress}`);
  } catch {
    checks.push('❌ Account prediction failed');
  }
  
  try {
    // 3. Check balance retrieval
    const balance = await apiGet('/v2/balances/aggregated-balance', {
      address: account.accountAddress
    });
    checks.push(`✅ Balance check: ${balance.balanceByAggregatedAsset.length} assets`);
  } catch {
    checks.push('❌ Balance check failed');
  }
  
  console.log('🏥 OneBalance Health Check Results:');
  checks.forEach(check => console.log(check));
  
  return checks.every(check => check.startsWith('✅'));
}

Common Pitfalls

Top 5 Mistakes to Avoid:

  1. Manual Approvals - Never include approve() in your calls array
  2. Wrong Decimals - Always verify token decimals (USDC = 6, not 18)
  3. Invalid Chain Format - Use eip155:chainId, not just the chain ID
  4. Modifying Quotes - Never alter the quote structure after preparation
  5. Insufficient Balance - Always check balance before attempting operations

Error Code Reference

Error CodeMeaningSolution
400Bad RequestCheck input format and validation
401UnauthorizedVerify API key configuration
422Unprocessable EntityCheck business logic (balance, amounts)
503Service UnavailableImplement retry logic

Getting Help

1

Check This Guide

Review the specific error section above for your issue

2

Use Debug Tools

Run the health check and debug logging functions

3

Review Examples

Check working examples for reference implementations

4

Contact Support

Provide your quote ID, error message, and debug logs for faster resolution

Pro Tip: Most issues can be prevented by running the health check function before implementing your main logic.