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
Define Price Condition
Set the price target your order should wait for
Build Swap Instruction
Create the swap that executes when condition is met
Submit Order
Sign once—MEE monitors and executes when ready
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
Type Operator Trading Use Case LTE≤ Buy when price drops to target GTE≥ Sell 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
});
Duration Seconds Use Case 1 hour 3600 Short-term scalping 24 hours 86400 Day trading 7 days 604800 Swing 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)
Asset Chainlink Feed Address Decimals ETH/USD 0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb708 BTC/USD 0x64c911996D3c6aC71E9b8e0D8d8e81E3C6E0E5b38 USDC/USD 0x7e860098F58bBFC8648a4311b374B1D669a2bc6B8
Next Steps