Learn how OneBalance automatically handles token approvals for DeFi operations while maintaining security best practices.

Important: Never manually call approve() functions when using OneBalance. The platform automatically manages all required approvals as part of the contract call preparation process.

How Approval Management Works

When you specify allowanceRequirements, OneBalance automatically:

  1. Checks existing allowances - Verifies current approval amounts
  2. Adds approval transactions - Only when needed, for exact amounts
  3. Executes your operations - Runs your contract calls
  4. Removes excess approvals - Cleans up remaining allowances for security

This prevents front-running attacks and ensures optimal gas usage.

OneBalance bundles approvals, your calls, and approval cleanup into a single user operation for atomic execution.

Basic Example

Here’s how to properly handle approvals for a DEX swap:

swap-with-approval.ts
import { encodeFunctionData, parseAbi, parseUnits } from 'viem';

const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
const UNISWAP_ROUTER = '0x2626664c2603336E57B271c5C0b26F421741e481';

// ✅ Correct - Let OneBalance handle approvals
const prepareRequest = {
  account: {
    sessionAddress: '0x...',
    adminAddress: '0x...',
    accountAddress: '0x...'
  },
  targetChain: 'eip155:8453', // Base
  calls: [{
    to: UNISWAP_ROUTER,
    data: encodeFunctionData({
      abi: parseAbi(['function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96))']),
      functionName: 'exactInputSingle',
      args: [{
        tokenIn: USDC_BASE,
        tokenOut: '0x...',
        fee: 3000,
        recipient: account.accountAddress,
        deadline: Math.floor(Date.now() / 1000) + 3600,
        amountIn: parseUnits('100', 6), // 100 USDC
        amountOutMinimum: 0,
        sqrtPriceLimitX96: 0
      }]
    }),
    value: '0'
  }],
  allowanceRequirements: [{
    assetType: `eip155:8453/erc20:${USDC_BASE}`,
    amount: parseUnits('100', 6).toString(), // Exact amount needed
    spender: UNISWAP_ROUTER
  }],
  tokensRequired: [{
    assetType: `eip155:8453/erc20:${USDC_BASE}`,
    amount: parseUnits('100', 6).toString()
  }]
};

What NOT to Do

Common Mistake: Including approval calls manually will cause transaction failures.

wrong-approach.ts
// ❌ WRONG - Don't do this!
const badRequest = {
  calls: [
    {
      // This will cause the transaction to fail
      to: USDC_BASE,
      data: encodeFunctionData({
        abi: parseAbi(['function approve(address spender, uint256 amount)']),
        functionName: 'approve',
        args: [UNISWAP_ROUTER, parseUnits('100', 6)]
      }),
      value: '0'
    },
    {
      to: UNISWAP_ROUTER,
      data: swapCallData,
      value: '0'
    }
  ]
  // No allowanceRequirements - this is the problem!
};

Common Patterns

DEX Swaps

dex-swap.ts
// Uniswap V3 swap requiring token approval
allowanceRequirements: [{
  assetType: `eip155:8453/erc20:${inputToken}`,
  amount: inputAmount.toString(),
  spender: UNISWAP_V3_ROUTER
}]

Lending Protocols

aave-deposit.ts
// Aave deposit requiring pool approval
allowanceRequirements: [{
  assetType: `eip155:1/erc20:${asset}`,
  amount: depositAmount.toString(),
  spender: AAVE_POOL_ADDRESS
}]

Multiple Token Operations

multi-token-defi.ts
// Complex DeFi operation requiring multiple approvals
allowanceRequirements: [
  {
    assetType: `eip155:1/erc20:${tokenA}`,
    amount: amountA.toString(),
    spender: DEX_ROUTER
  },
  {
    assetType: `eip155:1/erc20:${tokenB}`,
    amount: amountB.toString(),
    spender: LENDING_POOL
  }
]

NFT Purchases

nft-purchase.ts
// NFT marketplace purchase with USDC
allowanceRequirements: [{
  assetType: 'eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
  amount: nftPrice.toString(),
  spender: NFT_MARKETPLACE_ADDRESS
}]

Special Cases

Native Tokens (ETH, MATIC, etc.)

Native tokens don’t require approvals - they’re sent directly:

native-token.ts
// No allowanceRequirements needed for ETH
calls: [{
  to: CONTRACT_ADDRESS,
  data: contractCallData,
  value: ethAmount.toString() // Send ETH directly
}],
tokensRequired: [{
  assetType: 'eip155:1/slip44:60', // ETH
  amount: ethAmount.toString()
}]
// No allowanceRequirements array needed

Existing Allowances

OneBalance optimizes gas by checking existing allowances:

Sufficient Allowance

If current allowance ≥ required amount, no approval transaction is added

Insufficient Allowance

Only approves the exact amount needed, not unlimited approvals

Security Features

OneBalance’s approval management includes several security measures:

  • Exact Amount Approvals - Never approves more than needed
  • Automatic Cleanup - Removes remaining allowances after execution
  • Front-running Protection - Atomic bundling prevents manipulation
  • Approval Validation - Verifies spender addresses match your calls

Best Practices

1

Use allowanceRequirements

Always specify token approvals in the allowanceRequirements array, never in calls

2

Specify Exact Amounts

Use precise amounts to minimize approval exposure

3

Verify Spender Addresses

Double-check that spender addresses match your contract calls

4

Test with Small Amounts

Start with small transactions to verify your integration

TypeScript Interface

interfaces.ts
interface AllowanceRequirement {
  assetType: string;  // Format: "eip155:chainId/erc20:tokenAddress"
  amount: string;     // Amount in smallest unit (e.g., wei)
  spender: string;    // Contract address that needs approval
}

interface PrepareCallRequest {
  account: Account;
  targetChain: string;
  calls: CallData[];
  allowanceRequirements: AllowanceRequirement[];
  tokensRequired: TokenRequirement[];
}

Troubleshooting

  • Transaction fails with ‘Approval not found’ - Make sure you’re using allowanceRequirements instead of manual approval calls.
  • Gas costs seem high - OneBalance only adds approvals when needed. High gas might indicate multiple tokens or complex operations.
  • Spender address mismatch - Verify that the spender in allowanceRequirements matches the contract address in your calls.
  • Amount calculation errors - Ensure you’re using the correct decimals for the token (e.g., 6 for USDC, 18 for most ERC20s).

Need help? Check the troubleshooting guide for common issues and solutions.