Folio auctions use a Dutch auction mechanism where prices move from optimistic to pessimistic over time. All token pairs trade simultaneously, and bidders can participate by:
Direct bidding - Swap tokens at the current auction price
Callback bidding - Execute custom logic before transferring tokens
Trusted fills - Use aggregators like CowSwap for complex routing
Auctions have a 30-second warmup period to ensure fair competition. This is skipped for atomic swaps where start price equals end price.
Surplus tokens: Tokens above the high basket limit
Deficit tokens: Tokens below the low basket limit
// Example: Check if a pair is tradeable(uint256 sellAmount, uint256 bidAmount, uint256 price) = folio.getBid( auctionId, IERC20(weth), // Must be in surplus IERC20(usdc), // Must be in deficit type(uint256).max);if (sellAmount == 0) { // No surplus/deficit for this pair}
// Get current bid for selling WETH to buy USDC(uint256 sellAmount, uint256 bidAmount, uint256 price) = folio.getBid( auctionId, IERC20(weth), // Sell token IERC20(usdc), // Buy token 10e18 // Max WETH you want to receive);// Price is in D27 format: {buyToken/sellToken}// Example: price = 2300e27 means 1 WETH = 2300 USDC
Compare the auction price against external markets (Uniswap, etc.) to identify arbitrage opportunities.
2
Approve Tokens
The Folio needs allowance to pull buy tokens from you:
IERC20(usdc).approve(address(folio), bidAmount);
3
Execute Bid
Submit your bid:
uint256 actualBidAmount = folio.bid( auctionId, IERC20(weth), // Sell token (you receive) IERC20(usdc), // Buy token (you pay) sellAmount, // Exact amount of WETH you want bidAmount, // Max USDC you're willing to pay false, // withCallback = false for direct bid "" // No callback data needed);// You now have 'sellAmount' of WETH// Folio took 'actualBidAmount' of USDC from you
After the bid:
Your WETH balance increases by sellAmount
Your USDC balance decreases by actualBidAmount
actualBidAmount should be ≤ bidAmount (your max)
4
Arbitrage on External Markets
Immediately trade your received tokens for profit:
// Example: Sell WETH on Uniswap for more USDCISwapRouter(uniswapRouter).exactInputSingle( ISwapRouter.ExactInputSingleParams({ tokenIn: address(weth), tokenOut: address(usdc), fee: 3000, recipient: msg.sender, deadline: block.timestamp, amountIn: sellAmount, amountOutMinimum: bidAmount + minProfit, sqrtPriceLimitX96: 0 }));
For advanced strategies, use callbacks to execute custom logic within the bid transaction.
1
Implement IBidderCallee Interface
Your contract must implement the callback interface:
import { IBidderCallee } from "@interfaces/IBidderCallee.sol";contract MyArbitrageur is IBidderCallee { function bidderCallback( IERC20 sellToken, IERC20 buyToken, uint256 sellAmount, uint256 buyAmount, bytes calldata data ) external override { // 1. Receive sellToken from Folio // 2. Execute your strategy (e.g., swap on DEX) // 3. Transfer buyToken back to Folio // Example: Flash arbitrage // Sell received sellToken on Uniswap _swapOnUniswap(sellToken, buyToken, sellAmount); // Transfer buyAmount back to Folio buyToken.transfer(msg.sender, buyAmount); }}
Trusted fillers enable async execution using specialized solvers like CowSwap.
1
Create Trusted Fill
Instead of bidding directly, create a trusted fill order:
IBaseTrustedFiller filler = folio.createTrustedFill( auctionId, IERC20(weth), // Sell token IERC20(usdc), // Buy token cowSwapFillerAddr, // Target filler (e.g., CowSwapFiller) keccak256(abi.encode(msg.sender, block.timestamp)) // Unique salt);// Folio has approved the filler to spend sellToken// Filler now has entire block to execute the swap
The Folio will automatically close and claim tokens from the trusted filler at the next state-changing call.
2
Execute Fill (Solver Side)
The trusted filler contract handles the actual swap:
// CowSwap example: Create order on CoW ProtocolGPv2Order.Data memory order = GPv2Order.Data({ sellToken: weth, buyToken: usdc, sellAmount: sellAmount, buyAmount: buyAmount, // ... other CowSwap parameters});// Submit to CowSwap for async settlementcowSettlement.settle(orders, ...);
3
Monitor Fill Status
Check if the async swap is still active:
(bool syncActive, bool asyncActive) = folio.stateChangeActive();if (asyncActive) { // Trusted fill is still executing // Wait before performing state-dependent operations}
// 1. Get WETH from Folio auction (pay USDC)folio.bid(auctionId, IERC20(weth), IERC20(usdc), ...);// 2. Trade WETH for DAI on UniswapuniswapRouter.swap(weth, dai, ...);// 3. Get more USDC from Folio auction (pay DAI)folio.bid(auctionId, IERC20(usdc), IERC20(dai), ...);// Net: Started with X USDC, ended with X + profit USDC