Skip to main content

Smart Account Execution Mode

Smart Account mode is the simplest execution mode, ideal when funds are already in your Nexus smart account. It eliminates approval steps and provides native support for gasless transactions.

Key Advantages

Simplified Flow

No token approvals needed - works directly with Nexus balance

Gasless Transactions

Native sponsorship support for better UX

Always Simple Signature

API always returns simple quote type

How It Works

When using Smart Account mode:
  1. Prerequisite: Funds must already be in the Nexus account (from any source)
  2. Quote Request: Create quote without fundingTokens
  3. Sign Payload: Simple message signature
  4. Execute: Operations execute using existing Nexus balance
Funds can be in Nexus from:
  • Previous operations
  • Manual transfers to Nexus address
  • Direct deposits
  • Any other source

Requirements

  • Must NOT provide: fundingTokens field (not needed)
  • Must NOT provide: authorizations field (not applicable)
  • Signature type: API always returns simple
  • Prerequisites: Sufficient balance must exist in Nexus account

Complete Flow Example

Here’s a complete Smart Account workflow:
import { createWalletClient, http, parseUnits } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';

const API_BASE_URL = 'https://api.biconomy.io';
const account = privateKeyToAccount('0x...');

// Setup wallet client
const walletClient = createWalletClient({
  account,
  chain: base,
  transport: http()
});

async function smartAccountSwap() {
  // Note: Nexus must already have USDC balance
  // Calculate Nexus address from ownerAddress using appropriate SDK

  // Step 1: Build quote request (NO fundingTokens needed)
  const quoteRequest = {
    mode: 'smart-account',
    ownerAddress: account.address,
    // NO fundingTokens - using existing Nexus balance
    feeToken: {
      address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
      chainId: 8453
    },
    composeFlows: [
      {
        type: '/instructions/intent-simple',
        data: {
          srcChainId: 8453,
          dstChainId: 10,
          srcToken: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', // USDC on Base
          dstToken: '0x94b008aa00579c1307b0ef2c499ad98a8ce58e58', // USDT on OP
          amount: parseUnits('50', 6).toString(),
          slippage: 0.01
        }
      }
    ]
  };

  // Step 2: Get quote
  const quoteResponse = await fetch(`${API_BASE_URL}/v1/quote`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(quoteRequest)
  });

  const quote = await quoteResponse.json();
  console.log('Fee:', quote.fee.amount, 'wei');
  console.log('Quote type:', quote.quoteType); // Always 'simple'
  console.log('Expected output:', quote.returnedData[0].outputAmount);

  // Step 3: Sign payload (always simple message signature)
  const signature = await walletClient.signMessage({
    account,
    message: quote.payloadToSign[0].message
  });

  const signedPayload = [{ ...quote.payloadToSign[0], signature }];

  // Step 4: Execute
  const executeResponse = await fetch(`${API_BASE_URL}/v1/execute`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      ...quote,
      payloadToSign: signedPayload
    })
  });

  const result = await executeResponse.json();
  console.log('Transaction hash:', result.transactionHash);
  console.log('UserOps:', result.userOps);

  return result;
}

// Execute the swap
smartAccountSwap();

Sponsorship Options

Smart Account mode supports flexible fee payment:

Advanced Example: Portfolio Rebalancing

Complete example splitting one token into multiple:
async function rebalancePortfolio() {
  const quoteRequest = {
    mode: 'smart-account',
    ownerAddress: account.address,
    // Using sponsorship (no feeToken)
    composeFlows: [
      {
        type: '/instructions/intent',
        data: {
          slippage: 0.01,
          inputPositions: [{
            chainToken: {
              chainId: 8453,
              tokenAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'
            },
            amount: parseUnits('1000', 6).toString() // 1000 USDC
          }],
          targetPositions: [
            {
              chainToken: {
                chainId: 8453,
                tokenAddress: '0x4200000000000000000000000000000000000006' // WETH
              },
              weight: 0.6 // 60%
            },
            {
              chainToken: {
                chainId: 10,
                tokenAddress: '0x94b008aa00579c1307b0ef2c499ad98a8ce58e58' // USDT on OP
              },
              weight: 0.4 // 40%
            }
          ]
        }
      }
    ]
  };

  // Get quote
  const quote = await fetch(`${API_BASE_URL}/v1/quote`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(quoteRequest)
  }).then(r => r.json());

  // Sign (simple message)
  const signature = await walletClient.signMessage({
    account,
    message: quote.payloadToSign[0].message
  });

  // Execute
  const result = await fetch(`${API_BASE_URL}/v1/execute`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      ...quote,
      payloadToSign: [{ ...quote.payloadToSign[0], signature }]
    })
  }).then(r => r.json());

  console.log('Portfolio rebalanced:', result.transactionHash);
  return result;
}

Multi-Step DeFi Strategy

Example: Swap + Aave supply in one transaction:
const quoteRequest = {
  mode: 'smart-account',
  ownerAddress: account.address,
  composeFlows: [
    // Step 1: Swap USDC → WETH
    {
      type: '/instructions/intent-simple',
      data: {
        srcChainId: 8453,
        dstChainId: 8453,
        srcToken: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
        dstToken: '0x4200000000000000000000000000000000000006',
        amount: parseUnits('100', 6).toString(),
        slippage: 0.01
      }
    },
    // Step 2: Approve Aave
    {
      type: '/instructions/build',
      data: {
        functionSignature: 'function approve(address spender, uint256 amount)',
        args: [
          '0x...aavePool',
          {
            type: 'runtimeErc20Balance',
            tokenAddress: '0x4200000000000000000000000000000000000006'
          }
        ],
        to: '0x4200000000000000000000000000000000000006',
        chainId: 8453,
        gasLimit: '80000'
      }
    },
    // Step 3: Supply to Aave
    {
      type: '/instructions/build',
      data: {
        functionSignature: 'function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode)',
        args: [
          '0x4200000000000000000000000000000000000006',
          {
            type: 'runtimeErc20Balance',
            tokenAddress: '0x4200000000000000000000000000000000000006'
          },
          account.address, // Or Nexus address
          0
        ],
        to: '0x...aavePool',
        chainId: 8453,
        gasLimit: '350000'
      }
    }
  ]
};

Best Practices

Always check Nexus has sufficient balance before creating quotes:
// Check balance using viem or your preferred library
const balance = await publicClient.readContract({
  address: tokenAddress,
  abi: erc20Abi,
  functionName: 'balanceOf',
  args: [nexusAddress]
});

if (balance < requiredAmount) {
  throw new Error('Insufficient Nexus balance');
}
Omit feeToken to use gasless execution:
// ✅ GOOD - gasless transaction
{
  mode: 'smart-account',
  ownerAddress: '0x...',
  // No feeToken
  composeFlows: [...]
}
Operations on the same chain are automatically batched (default behavior):
composeFlows: [
  {
    type: '/instructions/build',
    // batch: true is default
    data: { chainId: 8453, ... }
  },
  {
    type: '/instructions/build',
    // Automatically batched with above
    data: { chainId: 8453, ... }
  }
]
Use runtimeErc20Balance for operations that depend on previous step outputs:
{
  type: '/instructions/build',
  data: {
    functionSignature: 'function deposit(uint256 amount)',
    args: [{
      type: 'runtimeErc20Balance',
      tokenAddress: '0x...',
      // Automatically uses Nexus balance
    }],
    to: '0x...',
    chainId: 8453
  }
}
Runtime balance is currently only supported for /instructions/build flow type.
Unlike EOA mode, funds stay in Nexus which is often desired:
  • No need to add withdrawal instructions
  • Funds available for future operations
  • Can withdraw manually later if needed

Comparison with Other Modes

FeatureSmart AccountEOAEIP-7702
Funds LocationNexusEOAEOA (delegated)
Setup ComplexityLowLowMedium
Funding RequiredNoYesNo
Authorization RequiredNoNoYes (first time)
Token Approval MethodInternalExternalExternal
Signature Typesimplepermit/onchainsimple
Sponsorship SupportNativeNoVia Delegation
Batch OperationsNativeNoYes
Gas EfficiencyHighMediumHigh
Withdrawal NeededOptionalYesNo

Troubleshooting

Ensure Nexus has sufficient token balance:
// Check Nexus balance first
const nexusBalance = await getBalance(nexusAddress, tokenAddress);

if (nexusBalance < requiredAmount) {
  // Transfer tokens to Nexus or use EOA mode
}
If sponsorship is denied:
  1. Check your API key has sponsorship enabled
  2. Or provide feeToken for self-paid execution
  3. Ensure Nexus has balance of the fee token
// Fallback to self-paid
feeToken: {
  address: '0x833589...',
  chainId: 8453
}
To get funds into Nexus:
  1. Direct transfer: Send tokens directly to Nexus address
  2. From EOA mode: Previous EOA operations leave funds in Nexus
  3. Manual deposit: Use SDK to transfer from EOA to Nexus
Smart Account mode requires signing with the owner account:
  • Verify you’re signing with the correct private key
  • Ensure account matches ownerAddress in request
  • Check wallet client is properly configured

Integration with Biconomy Nexus

When building with Biconomy Nexus smart accounts, the Supertransaction API seamlessly integrates with your existing infrastructure:
import { createNexusClient } from '@biconomy/abstractjs';

// Calculate Nexus address from owner
const nexusClient = await createNexusClient({
  signer: account,
  chain: base,
  transport: http()
});

const nexusAddress = await nexusClient.account.address;

// Use in Supertransaction API
const quoteRequest = {
  mode: 'smart-account',
  ownerAddress: account.address,
  composeFlows: [...]
};