Skip to main content
Create limit orders that automatically execute when tokens reach your target price. Biconomy’s conditional execution monitors on-chain prices and triggers your swap only when conditions are satisfied.

How Limit Orders Work

1

Define Price Condition

Set the price target your order should wait for
2

Build Swap Instruction

Create the swap that executes when condition is met
3

Submit Order

Sign once—MEE monitors and executes when ready
4

Automatic Execution

When price hits target, swap executes automatically
Submit Order → MEE Monitors Price → Target Hit? Execute Swap

              Keeps checking until timeout or execution

Quick Example

Wait for WETH to drop below $3,000 USDC, then buy:
import { 
  createMeeClient, 
  createCondition, 
  ConditionType,
  toMultiplier 
} from '@biconomy/abstractjs';
import { base } from 'viem/chains';
import { parseUnits } from 'viem';

// Token addresses on Base
const USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
const WETH = '0x4200000000000000000000000000000000000006';

// Chainlink price feed for ETH/USD on Base
const ETH_USD_FEED = '0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70';

// Chainlink Aggregator ABI (minimal)
const aggregatorAbi = [
  {
    name: 'latestAnswer',
    type: 'function',
    stateMutability: 'view',
    inputs: [],
    outputs: [{ type: 'int256' }]
  }
] as const;

// Create condition: Execute when ETH price <= $3000
const priceCondition = createCondition({
  targetContract: ETH_USD_FEED,
  functionAbi: aggregatorAbi,
  functionName: 'latestAnswer',
  args: [],
  value: parseUnits('3000', 8),  // Chainlink uses 8 decimals
  type: ConditionType.LTE        // Less than or equal
});

// Build the swap instruction with condition
const limitOrder = await account.buildComposable({
  type: 'intent-simple',
  data: {
    srcChainId: base.id,
    dstChainId: base.id,
    srcToken: USDC,
    dstToken: WETH,
    amount: parseUnits('1000', 6),  // 1000 USDC
    slippage: 0.01,
    conditions: [priceCondition]    // ← Only execute at target price
  }
});

// Get quote and execute
const quote = await meeClient.getFusionQuote({
  trigger: { chainId: base.id, tokenAddress: USDC, amount: parseUnits('1000', 6) },
  instructions: [limitOrder],
  feeToken: { chainId: base.id, address: USDC },
  // Order expires in 24 hours
  upperBoundTimestamp: Math.floor(Date.now() / 1000) + 86400
});

const { hash } = await meeClient.executeFusionQuote({ fusionQuote: quote });
console.log('Limit order submitted:', hash);
console.log('Track at:', `https://meescan.biconomy.io/details/${hash}`);

Condition Types for Trading

TypeOperatorTrading Use Case
LTEBuy when price drops to target
GTESell when price rises to target
EQ=Execute at exact price (rare)

Buy Limit Order (Price Drops)

Wait for a token to become cheaper before buying:
// Buy ETH when price drops to $2,800 or below
const buyCondition = createCondition({
  targetContract: ETH_USD_FEED,
  functionAbi: aggregatorAbi,
  functionName: 'latestAnswer',
  args: [],
  value: parseUnits('2800', 8),
  type: ConditionType.LTE  // Execute when price <= $2800
});

Sell Limit Order (Price Rises)

Wait for a token to appreciate before selling:
// Sell ETH when price rises to $4,000 or above
const sellCondition = createCondition({
  targetContract: ETH_USD_FEED,
  functionAbi: aggregatorAbi,
  functionName: 'latestAnswer',
  args: [],
  value: parseUnits('4000', 8),
  type: ConditionType.GTE  // Execute when price >= $4000
});

const sellOrder = await account.buildComposable({
  type: 'intent-simple',
  data: {
    srcChainId: base.id,
    dstChainId: base.id,
    srcToken: WETH,
    dstToken: USDC,
    amount: parseUnits('0.5', 18),  // 0.5 ETH
    slippage: 0.01,
    conditions: [sellCondition]
  }
});

Using DEX Pool Prices

For tokens without Chainlink feeds, read price directly from a DEX pool:
// Uniswap V3 pool slot0 for price
const poolAbi = [
  {
    name: 'slot0',
    type: 'function',
    stateMutability: 'view',
    inputs: [],
    outputs: [
      { name: 'sqrtPriceX96', type: 'uint160' },
      { name: 'tick', type: 'int24' },
      // ... other fields
    ]
  }
] as const;

// Use tick as price proxy (higher tick = higher price for token1)
const poolPriceCondition = createCondition({
  targetContract: UNISWAP_POOL_ADDRESS,
  functionAbi: poolAbi,
  functionName: 'slot0',
  args: [],
  value: targetTick,
  type: ConditionType.LTE
});

Multiple Conditions (Safety Checks)

Combine price target with safety conditions:
const limitOrder = await account.buildComposable({
  type: 'intent-simple',
  data: {
    srcChainId: base.id,
    dstChainId: base.id,
    srcToken: USDC,
    dstToken: WETH,
    amount: parseUnits('1000', 6),
    slippage: 0.01,
    conditions: [
      // Price condition
      createCondition({
        targetContract: ETH_USD_FEED,
        functionAbi: aggregatorAbi,
        functionName: 'latestAnswer',
        args: [],
        value: parseUnits('3000', 8),
        type: ConditionType.LTE
      }),
      // Safety: Ensure we have enough balance
      createCondition({
        targetContract: USDC,
        functionAbi: erc20Abi,
        functionName: 'balanceOf',
        args: [userAddress],
        value: parseUnits('1000', 6),
        type: ConditionType.GTE
      })
    ]
  }
});
When multiple conditions are specified, ALL must be satisfied before execution (AND logic).

Setting Order Expiration

Always set a timeout so orders don’t stay pending forever:
const quote = await meeClient.getFusionQuote({
  trigger: { chainId: base.id, tokenAddress: USDC, amount },
  instructions: [limitOrder],
  feeToken: { chainId: base.id, address: USDC },
  // Order expiration options
  upperBoundTimestamp: Math.floor(Date.now() / 1000) + 86400  // 24 hours
});
DurationSecondsUse Case
1 hour3600Short-term scalping
24 hours86400Day trading
7 days604800Swing trading

Tracking Order Status

Monitor your limit order until execution:
const { hash } = await meeClient.executeFusionQuote({ fusionQuote: quote });

// Poll for status
const checkStatus = async () => {
  const status = await fetch(
    `https://api.biconomy.io/v1/status/${hash}`
  ).then(r => r.json());
  
  console.log('Status:', status.status);
  // PENDING = waiting for conditions
  // EXECUTING = conditions met, executing
  // COMPLETED = order filled
  // EXPIRED = timeout reached
  
  return status;
};

// Check every 30 seconds
const interval = setInterval(async () => {
  const status = await checkStatus();
  if (status.status !== 'PENDING') {
    clearInterval(interval);
  }
}, 30000);

Best Practices

Use Reliable Oracles

Chainlink feeds are most reliable. DEX pools can be manipulated.

Set Reasonable Timeouts

Don’t leave orders pending indefinitely—always set expiration.

Account for Decimals

Chainlink ETH/USD uses 8 decimals. Match your condition values.

Add Slippage Buffer

Price can move between condition check and execution.

Common Price Feeds (Base)

AssetChainlink Feed AddressDecimals
ETH/USD0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb708
BTC/USD0x64c911996D3c6aC71E9b8e0D8d8e81E3C6E0E5b38
USDC/USD0x7e860098F58bBFC8648a4311b374B1D669a2bc6B8
Find more Chainlink feeds at data.chain.link

Next Steps