Creating Market Orders
Learn how to execute market orders (swaps) on the Vif order book using vifdk.
What is a Market Order?
A market order is a "take" operation that matches against existing limit orders on the book. When you create a market order, you:
- Specify the maximum amount you want to give (spend)
- Receive tokens by taking from existing offers at the best available prices
- Execute immediately as a taker (no waiting for fills)
Basic Market Order
Step 1: Setup
First, ensure you have:
- Tokens defined
- Market created
- VifRouter initialized
- User authorized the router
import { Token, Market, VifRouter, execute } from "vifdk";
import { createWalletClient, erc20Abi, http, publicActions } from "viem";
import { mainnet } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
// Define tokens
const WETH = Token.from(WETH_ADDRESS, 18, "WETH", 1n);
const USDC = Token.from(USDC_ADDRESS, 6, "USDC", 1n);
// Create market
const market = Market.create({
base: WETH,
quote: USDC,
tickSpacing: 1n,
});
// Initialize router
const router = new VifRouter(VIF_ROUTER_ADDRESS, VIF_CORE_ADDRESS, chainId);
const client = createWalletClient({
chain: mainnet,
transport: http(),
account: privateKeyToAccount(privateKey),
}).extend(publicActions);Step 2: Authorize Router
const authData = router.authorizationData(
client.account.address,
0n,
new Date(Date.now() + 1000 * 60 * 60 * 24 * 30)
);
const typedData = router.singatureDataForAuthorization(authData);
const signature = await client.signTypedData(typedData);Step 3: Approve Tokens
await client.writeContractSync({
address: USDC_ADDRESS,
abi: erc20Abi,
functionName: "approve",
args: [VIF_CORE_ADDRESS, USDC.amount("3500").amount],
});Step 4: Create Market Order
// Buy WETH with 3500 USDC
const actions = router
.createTypedActions()
.authorize({ authorization: authData, signature })
.orderSingle({
market: market.bids,
fillVolume: USDC.amount("3500"),
maxTick: market.bids.price(4000),
})
.build({
addRecommendedActions: true,
receiver: client.account.address,
});
// Get transaction data
const { commands, args } = actions.txData();
// Simulate first
const { result, request } = await client.simulateContract({
address: VIF_ROUTER_ADDRESS,
...execute(commands, args),
});
const [, { data: simulationResult }] = actions.parseSimulationResult(result);
console.log("Will receive:", simulationResult.got.amountString, "WETH");
console.log("Will spend:", simulationResult.gave.amountString, "USDC");
console.log("Fees:", simulationResult.fee.amountString, "USDC");
// Execute
const receipt = await client.writeContractSync(request);
const [, { data: orderResult }] = actions.parseLogs(receipt.logs);
console.log("Received:", orderResult?.got.amountString, "WETH");
console.log("Spent:", orderResult?.gave.amountString, "USDC");
console.log("Fees:", orderResult?.fee.amountString, "USDC");Sell Order (Ask Side)
Selling base token (WETH) for quote token (USDC):
// Sell base token (WETH) for quote token (USDC)
const sellActions = router
.createTypedActions()
.orderSingle({
market: market.asks, // Take from asks to sell WETH
fillVolume: WETH.amount("1"), // Sell 1 WETH
maxTick: market.asks.price(3400), // At least 3400 USDC/WETH price
})
.build({
addRecommendedActions: true, // Automatically adds settleAll(WETH) and takeAll(USDC)
receiver: client.account.address,
});Using Native Token (ETH)
To swap ETH directly without wrapping first:
// Swap ETH directly without wrapping first
const wrapActions = router
.createTypedActions()
.wrapNative(Token.NATIVE_TOKEN.amount("1")) // Wrap ETH to WETH
.orderSingle({
market: market.asks,
fillVolume: WETH.amount("1"),
maxTick: market.asks.price(3400),
})
.build({
addRecommendedActions: true, // Automatically adds settleAll(NATIVE_TOKEN/WETH) and takeAll(USDC)
receiver: client.account.address,
});
const value = wrapActions.expectedValue({
globalProvision: Token.PROVISION_TOKEN.amount("0"),
});
// Send ETH with transaction
const { commands: wrapCommands, args: wrapArgs } = wrapActions.txData();
const receiptWrap = await client.writeContractSync({
address: VIF_ROUTER_ADDRESS,
...execute(wrapCommands, wrapArgs),
value: value.amount,
});Receiving Native Token (ETH)
To receive ETH instead of WETH:
// Receive ETH instead of WETH
const unwrapActions = router
.createTypedActions()
.orderSingle({
market: market.asks,
fillVolume: USDC.amount("3500"),
maxTick: market.asks.price(4000),
})
.unwrapNative(WETH.amount("1")) // Unwrap WETH to ETH
.build({
addRecommendedActions: true, // Automatically adds settleAll(USDC) and takeAll(NATIVE_TOKEN)
receiver: client.account.address,
});Slippage Protection
Control the trade outcome with fillVolume and maxTick:
Exact Input - Spend exact amount:
// Exact Input - Spend exact amount with slippage protection
router.createTypedActions().orderSingle({
market: market.bids,
fillVolume: USDC.amount("3500"), // Spend exactly 3500 USDC
maxTick: market.bids.price(3850), // Max 3850 USDC per WETH (10% slippage)
});Exact Output - Receive exact amount:
// Exact Output - Receive exact amount with slippage protection
router.createTypedActions().orderSingle({
market: market.asks,
fillVolume: WETH.amount("1"), // Buy exactly 1 WETH
maxTick: market.asks.price(3850), // Pay up to 3850 USDC per WETH
});Error Handling
Common errors to handle:
- Insufficient Liquidity - Not enough offers in the order book
- Slippage Exceeded - Received less than
minReceives - Insufficient Balance - Not enough input tokens
Next Steps
- Create Limit Orders - Provide liquidity as a maker
- Cancel Limit Orders - Manage your orders
- Router Actions - All available actions
- Reading State - Query order book data