Fluid DEX V2: Liquidity Source Integration ​
Complete integration guide for adding Fluid DEX V2 as a liquidity source in DEX aggregators and routing protocols.
Table of Contents ​
- Overview
- Quick Start
- Data Access with Resolver
- Pool Discovery & State Reading
- Router-based Integration
- Callback-based Integration (Advanced)
- Operating DEX Modules
- Token Settlement with settle()
- Best Practices
- Resources
Overview ​
What is Fluid DEX v2? ​
Fluid DEX v2 is an advanced decentralized exchange built on top of the Fluid Liquidity Layer, featuring:
- Single Contract, Multiple DEX Types: One contract handles different types of AMMs (Automated Market Makers)
- Smart Collateral & Debt: Revolutionary financial primitives for enhanced capital efficiency
- Dynamic Fees: On-chain adaptive fee mechanisms and custom hooks
- Flash Accounting: Ultra-efficient settlement for complex multi-step operations
Supported DEX Types ​
Currently, DEX v2 supports 4 major DEX types:
- DEX Type 1 (D1): DEX v1 Smart Collateral (Coming Soon)
- DEX Type 2 (D2): DEX v1 Smart Debt (Coming Soon)
- DEX Type 3 (D3): Smart Collateral Range Orders
- DEX Type 4 (D4): Smart Debt Range Orders
This guide focuses on D3 and D4 integration as they are currently available for use.
Architecture ​
- Core Contract:
FluidDexV2- Singleton contract handling all DEX operations - Resolver Contract:
FluidDexV2D3D4Resolver- Provides pool state reading, position analysis, and liquidity distribution queries - Router Contract:
FluidDexV2Router- Simplified integration with easy-to-use functions - Callback Pattern: Gas-efficient but advanced integration requiring custom callback implementation
Quick Start ​
This guide covers both integration methods. Choose the router for simplicity or callbacks for maximum control and gas efficiency.
1. Integration Methods ​
Method 1: Router-based Integration
- What it is: Use the
FluidDexV2Routercontract with simple function calls - Best for: Most integrators who want straightforward swap functionality
- Pros: Easy integration, no callback implementation needed, supports both single and multi-hop swaps, native ETH support
- Cons: Slightly higher gas costs due to additional contract layer
Method 2: Callback-based Integration (Advanced)
- What it is: You implement callback functions that the DEX calls during operations
- Best for: Advanced integrators who want maximum control and gas efficiency
- Pros: Maximum gas efficiency, full control over token transfers, can batch multiple operations
- Cons: Complex implementation, requires understanding callback mechanism and security
2. Data Access ​
Resolver Contract: FluidDexV2D3D4Resolver
- Pool state reading and analysis
- Position data queries
- Liquidity distribution analysis
- Used by both integration methods for data access
3. Core Data Structures ​
Essential structures for DEX v2 integration:
// Pool identification - every pool is uniquely identified by this structure
struct DexKey {
address token0; // Always the smaller token address (lexicographically)
address token1; // Always the larger token address (lexicographically)
uint24 fee; // Fee tier (e.g., 500 = 0.05%) or 0xFFFFFF for dynamic fees
uint24 tickSpacing; // Price granularity (1 = finest, higher = coarser)
address controller; // Optional controller contract (use address(0) if none)
}
// Parameters for exact input swaps (specify input amount, get variable output)
struct SwapInParams {
DexKey dexKey; // Pool to swap in
bool swap0To1; // Direction: true = token0 -> token1, false = token1 -> token0
uint256 amountIn; // Exact amount of input tokens
uint256 amountOutMin; // Minimum acceptable output amount (slippage protection)
bytes controllerData; // Optional data for controller contract
}
// Parameters for exact output swaps (specify output amount, pay variable input)
struct SwapOutParams {
DexKey dexKey; // Pool to swap in
bool swap0To1; // Direction: true = token0 -> token1, false = token1 -> token0
uint256 amountOut; // Exact amount of output tokens desired
uint256 amountInMax; // Maximum acceptable input amount (slippage protection)
bytes controllerData; // Optional data for controller contract
}4. Contract Addresses ​
| Network | Contract | Address |
|---|---|---|
| Ethereum | FluidDexV2 | TBD |
| Ethereum | FluidDexV2D3D4Resolver | TBD |
| Ethereum | FluidDexV2Router | TBD |
| Polygon | FluidDexV2 | 0x04e9FF525b6541Ca4a0Dca3326b79547f9057E15 |
| Polygon | FluidDexV2Resolver | 0x2Ba521a909BDBE56183e3cd5F27962466e674610 |
| Polygon | FluidDexV2Router | 0xe16aD4692B2505d21959f22Ff32D18d808D35759 |
Data Access with Resolver ​
Using the Resolver Contract ​
Use the FluidDexV2D3D4Resolver contract for all pool state reading, position analysis, and liquidity distribution queries.
Resolver Data Structures ​
// Pool state variables (packed and unpacked versions)
struct DexVariables {
int256 currentTick;
uint256 currentSqrtPriceX96;
uint256 feeGrowthGlobal0X102;
uint256 feeGrowthGlobal1X102;
}
struct DexVariables2 {
uint256 protocolFee0To1;
uint256 protocolFee1To0;
uint256 protocolCutFee;
uint256 token0Decimals;
uint256 token1Decimals;
uint256 activeLiquidity;
bool fetchDynamicFeeFlag;
bool inbuiltDynamicFeeFlag;
uint256 lpFee;
uint256 maxDecayTime;
uint256 priceImpactToFeeDivisionFactor;
uint256 minFee;
uint256 maxFee;
int256 netPriceImpact;
uint256 lastUpdateTimestamp;
uint256 decayTimeRemaining;
}
// Complete pool state (returned by resolver)
struct DexPoolState {
uint256 dexVariablesPacked;
uint256 dexVariables2Packed;
DexVariables dexVariablesUnpacked;
DexVariables2 dexVariables2Unpacked;
}
// Position-related structures
struct PositionData {
uint256 liquidity;
uint256 feeGrowthInside0X102;
uint256 feeGrowthInside1X102;
}
struct PositionInfo {
PositionData positionData;
uint256 amount0;
uint256 amount1;
uint256 feeAccruedToken0;
uint256 feeAccruedToken1;
}
// Liquidity distribution analysis
struct TickLiquidity {
int24 tick;
uint256 liquidity;
}Key Methods ​
contract FluidDexV2D3D4Resolver {
// Pool State Reading
function getDexId(DexKey memory dexKey) public view returns (bytes32 dexId);
function getDexPoolState(uint256 dexType, bytes32 dexId) public view returns (DexPoolState memory);
function getDexPoolState(uint256 dexType, DexKey memory dexKey) public view returns (DexPoolState memory);
// Position Information
function getPositionData(
uint256 dexType,
DexKey memory dexKey,
address user,
int24 tickLower,
int24 tickUpper,
bytes32 positionSalt
) public view returns (PositionData memory);
function getPositionInfo(
uint256 dexType,
DexKey memory dexKey,
address user,
int24 tickLower,
int24 tickUpper,
bytes32 positionSalt
) public view returns (PositionInfo memory);
// Liquidity Analysis
function getLiquidityAmounts(
uint256 dexType,
DexKey memory dexKey,
int24 startTick,
int24 endTick,
uint256 startLiquidity
) public view returns (TickLiquidity[] memory);
}Basic Usage Example ​
// SPDX-License-Identifier: MIT
pragma solidity 0.8.29;
import "contracts/periphery/resolvers/dexV2/d3d4/main.sol";
import "contracts/protocols/dexV2/interfaces/iDexV2.sol";
contract FluidDexV2Integrator {
FluidDexV2D3D4Resolver constant resolver = FluidDexV2D3D4Resolver(
0x... // TBD - resolver address
);
IFluidDexV2 constant dexV2 = IFluidDexV2(
0x... // TBD - main contract address
);
uint256 constant D3_DEX_TYPE = 3; // Smart Collateral Range Orders
uint256 constant D4_DEX_TYPE = 4; // Smart Debt Range Orders
function getPoolState(DexKey memory dexKey, uint256 dexType) external view returns (
bytes32 dexId,
int256 currentTick,
uint256 currentPrice,
uint256 activeLiquidity,
uint256 fee
) {
dexId = resolver.getDexId(dexKey);
DexPoolState memory state = resolver.getDexPoolState(dexType, dexId);
currentTick = state.dexVariablesUnpacked.currentTick;
currentPrice = state.dexVariablesUnpacked.currentSqrtPriceX96;
activeLiquidity = state.dexVariables2Unpacked.activeLiquidity;
fee = state.dexVariables2Unpacked.lpFee;
}
}Pool Discovery & State Reading ​
Discovering Available Pools ​
DEX v2 doesn't have a single discovery method. Pools are created through governance and can be discovered by:
- Event Monitoring: Listen for pool initialization events
- Known Pool Lists: Maintain a registry of known pools
- On-chain Queries: Check if specific DexKeys exist by querying pool state
// Listen for this event to discover new pools
event LogInitialize(
uint256 dexType,
bytes32 dexId,
address token0,
address token1,
uint24 fee,
uint24 tickSpacing,
address controller,
uint256 sqrtPriceX96
);function checkPoolExists(DexKey memory dexKey, uint256 dexType) external view returns (bool exists) {
bytes32 dexId = resolver.getDexId(dexKey);
DexPoolState memory state = resolver.getDexPoolState(dexType, dexId);
// Pool exists if it has been initialized (non-zero sqrt price)
exists = state.dexVariablesUnpacked.currentSqrtPriceX96 > 0;
}Reading Pool State ​
function readPoolDetails(DexKey memory dexKey, uint256 dexType) external view returns (
int256 currentTick,
uint256 sqrtPriceX96,
uint256 activeLiquidity,
uint256 fee,
uint256 token0Decimals,
uint256 token1Decimals,
bool isDynamicFee
) {
DexPoolState memory state = resolver.getDexPoolState(dexType, dexKey);
currentTick = state.dexVariablesUnpacked.currentTick;
sqrtPriceX96 = state.dexVariablesUnpacked.currentSqrtPriceX96;
activeLiquidity = state.dexVariables2Unpacked.activeLiquidity;
fee = state.dexVariables2Unpacked.lpFee;
token0Decimals = state.dexVariables2Unpacked.token0Decimals;
token1Decimals = state.dexVariables2Unpacked.token1Decimals;
isDynamicFee = state.dexVariables2Unpacked.inbuiltDynamicFeeFlag;
}Analyzing Liquidity Distribution ​
function analyzeLiquidity(
DexKey memory dexKey,
uint256 dexType,
int24 tick
) external view returns (TickLiquidity[] memory liquidityDistribution) {
DexPoolState memory state = resolver.getDexPoolState(dexType, dexKey);
int24 currentTick = state.dexVariablesUnpacked.currentTick;
uint256 currentLiquidity = state.dexVariables2Unpacked.activeLiquidity;
liquidityDistribution = resolver.getLiquidityAmounts(
dexType,
dexKey,
currentTick,
tick,
currentLiquidity
);
}Router-based Integration ​
Overview ​
The FluidDexV2Router provides a simple, user-friendly interface for DEX v2 swaps. It handles all the complex callback logic internally, making integration straightforward while supporting both single-hop and multi-hop swaps across D3 and D4 pools.
Key Features ​
- Simple API: Just call functions directly, no callback implementation needed
- Native Token Support: Uses
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeEto represent the native token - Multi-hop Routing: Support for complex swap paths across multiple pools
- Mixed DEX Types: Can route through both D3 and D4 pools in a single transaction
- Slippage Protection: Built-in minimum/maximum amount validation
- Gas Refunds: Automatically refunds excess ETH sent with transactions
Router Interface ​
interface IFluidDexV2Router {
// Single-hop exact input swap
function swapIn(
uint256 dexType_, // 3 for D3, 4 for D4
SwapInParams calldata swapInParams_, // Swap parameters
address to_ // Token recipient
) external payable returns (uint256 amountOut_);
// Single-hop exact output swap
function swapOut(
uint256 dexType_, // 3 for D3, 4 for D4
SwapOutParams calldata swapOutParams_, // Swap parameters
address to_ // Token recipient
) external payable returns (uint256 amountIn_);
// Multi-hop exact input swap
function swapInMulti(
address[] calldata path_, // Token path [tokenA, tokenB, tokenC]
SwapInConfig[] calldata swapInConfigs_, // Config for each hop
uint256 amountIn_, // Input amount
address to_ // Token recipient
) external payable returns (uint256 amountOut_);
// Multi-hop exact output swap
function swapOutMulti(
address[] calldata path_, // Token path [tokenA, tokenB, tokenC]
SwapOutConfig[] calldata swapOutConfigs_, // Config for each hop
uint256 amountOut_, // Desired output amount
address to_ // Token recipient
) external payable returns (uint256 amountIn_);
}
// Multi-hop configuration structs
struct SwapInConfig {
uint256 dexType; // 3 for D3, 4 for D4
DexKey dexKey; // Pool identification
uint256 amountOutMin; // Minimum output for this hop
bytes controllerData; // Optional controller data
}
struct SwapOutConfig {
uint256 dexType; // 3 for D3, 4 for D4
DexKey dexKey; // Pool identification
uint256 amountInMax; // Maximum input for this hop
bytes controllerData; // Optional controller data
}Router Usage Examples ​
1. Single-hop Exact Input Swap (swapIn) ​
Swap an exact amount of input tokens for a minimum amount of output tokens:
import "contracts/periphery/dexV2Router/main.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract MyDEXAggregator {
FluidDexV2Router constant router = FluidDexV2Router(0x...); // Router address
function swapUSDCForUSDT(
uint256 amountIn,
uint256 minAmountOut,
address recipient
) external returns (uint256 amountOut) {
// Define the pool
DexKey memory dexKey = DexKey({
token0: 0xA0b86a33E6441E4C7449e6C4c6E9E6C4c6E9E6C4, // USDC (smaller address)
token1: 0xdAC17F958D2ee523a2206206994597C13D831ec7, // USDT (larger address)
fee: 500, // 0.05% fee
tickSpacing: 10, // Tick spacing
controller: address(0) // No controller
});
// Setup swap parameters
SwapInParams memory swapParams = SwapInParams({
dexKey: dexKey,
swap0To1: true, // USDC -> USDT (token0 -> token1)
amountIn: amountIn,
amountOutMin: minAmountOut,
controllerData: ""
});
// Approve router to spend USDC
IERC20(dexKey.token0).approve(address(router), amountIn);
// Execute swap
amountOut = router.swapIn(
3, // D3 DEX type
swapParams,
recipient
);
}
}2. Single-hop Exact Output Swap (swapOut) ​
Swap a maximum amount of input tokens for an exact amount of output tokens:
function swapETHForExactUSDC(
uint256 exactAmountOut,
address recipient
) external payable returns (uint256 amountIn) {
DexKey memory dexKey = DexKey({
token0: 0xA0b86a33E6441E4C7449e6C4c6E9E6C4c6E9E6C4, // USDC
token1: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, // ETH (native token)
fee: 3000, // 0.3% fee
tickSpacing: 60,
controller: address(0)
});
SwapOutParams memory swapParams = SwapOutParams({
dexKey: dexKey,
swap0To1: false, // ETH -> USDC (token1 -> token0)
amountOut: exactAmountOut,
amountInMax: msg.value, // Maximum ETH to spend
controllerData: ""
});
// Execute swap - ETH is sent via msg.value
amountIn = router.swapOut{value: msg.value}(
3, // D3 DEX type
swapParams,
recipient
);
// Excess ETH is automatically refunded
}3. Multi-hop Exact Input Swap (swapInMulti) ​
Swap through multiple pools: ETH → USDC → USDT
function swapETHToUSDTViaUSDC(
uint256 amountIn,
uint256 minAmountOut,
address recipient
) external payable returns (uint256 amountOut) {
// Define the swap path: ETH -> USDC -> USDT
address[] memory path = new address[](3);
path[0] = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; // ETH
path[1] = 0xA0b86a33E6441E4C7449e6C4c6E9E6C4c6E9E6C4; // USDC
path[2] = 0xdAC17F958D2ee523a2206206994597C13D831ec7; // USDT
// Configure each hop
SwapInConfig[] memory configs = new SwapInConfig[](2);
// Hop 1: ETH -> USDC (D3 pool)
configs[0] = SwapInConfig({
dexType: 3,
dexKey: DexKey({
token0: path[1], // USDC (smaller address)
token1: path[0], // ETH (larger address)
fee: 3000,
tickSpacing: 60,
controller: address(0)
}),
amountOutMin: 0, // No intermediate slippage check
controllerData: ""
});
// Hop 2: USDC -> USDT (D4 pool)
configs[1] = SwapInConfig({
dexType: 4,
dexKey: DexKey({
token0: path[1], // USDC (smaller address)
token1: path[2], // USDT (larger address)
fee: 100,
tickSpacing: 1,
controller: address(0)
}),
amountOutMin: minAmountOut, // Final slippage protection
controllerData: ""
});
// Execute multi-hop swap
amountOut = router.swapInMulti{value: msg.value}(
path,
configs,
amountIn,
recipient
);
}4. Multi-hop Exact Output Swap (swapOutMulti) ​
Get exact USDT output by swapping through ETH → USDC → USDT:
function getExactUSDTViaMultiHop(
uint256 exactAmountOut,
address recipient
) external payable returns (uint256 amountIn) {
// Path: ETH -> USDC -> USDT (same as input example)
address[] memory path = new address[](3);
path[0] = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; // ETH
path[1] = 0xA0b86a33E6441E4C7449e6C4c6E9E6C4c6E9E6C4; // USDC
path[2] = 0xdAC17F958D2ee523a2206206994597C13D831ec7; // USDT
SwapOutConfig[] memory configs = new SwapOutConfig[](2);
// Hop 1: ETH -> USDC
configs[0] = SwapOutConfig({
dexType: 3,
dexKey: DexKey({
token0: path[1], // USDC
token1: path[0], // ETH
fee: 3000,
tickSpacing: 60,
controller: address(0)
}),
amountInMax: msg.value, // Max ETH to spend
controllerData: ""
});
// Hop 2: USDC -> USDT
configs[1] = SwapOutConfig({
dexType: 4,
dexKey: DexKey({
token0: path[1], // USDC
token1: path[2], // USDT
fee: 100,
tickSpacing: 1,
controller: address(0)
}),
amountInMax: type(uint256).max, // No intermediate limit
controllerData: ""
});
// Execute exact output multi-hop swap
amountIn = router.swapOutMulti{value: msg.value}(
path,
configs,
exactAmountOut,
recipient
);
}Callback-based Integration (Advanced) ​
💡 New to DEX v2? Consider starting with the Router-based Integration above for a simpler approach. This section covers the advanced callback method for maximum control and gas efficiency.
The Callback Architecture ​
Why Callbacks? DEX v2 uses callbacks to give you maximum flexibility. You can perform multiple operations, complex routing, or custom logic all in one transaction.
The Flow:
Your Contract → startOperation() → DEX calls your callback → Your Logic → Settlement → CompleteStep by Step:
- You initiate: Call
dexV2.startOperation(encodedData)with your operation data - DEX calls back: DEX immediately calls
startOperationCallback(encodedData)on your contract - You execute: Inside the callback, perform swaps using
operate()and handle tokens usingsettle() - DEX verifies: DEX checks that all token transfers balance out correctly
- Transaction completes: If everything balances, the transaction succeeds
Key Benefits:
- Atomic operations: Multiple swaps and operations in one transaction
- Gas efficiency: No intermediate token transfers between operations
- Flexibility: Implement any trading strategy or routing logic you want
Core Interfaces ​
interface IFluidDexV2 {
// Initiates any operation - calls back to your contract
function startOperation(bytes calldata data) external returns (bytes memory result);
// Executes specific DEX operations (swap, deposit, etc.)
function operate(
uint256 dexType, // 3 for D3, 4 for D4
uint256 implementationId, // 1 for swap, 2 for user ops
bytes memory data
) external returns (bytes memory returnData);
// Handles token transfers and settlement
function settle(
address token,
int256 supplyAmount, // Positive = supply to DEX
int256 borrowAmount, // Positive = borrow from DEX
int256 storeAmount, // Positive = store in DEX
address to, // Recipient address
bool isCallback // true = DEX calls dexCallback for transfer, false = approval based transfers
) external payable;
}
// Your contract must implement this
interface IDexV2Callbacks {
function startOperationCallback(bytes calldata data) external returns (bytes memory);
function dexCallback(address token, address to, uint256 amount) external;
}
// Swap function interfaces (for encoding with operate())
interface IDexV2SwapModule {
function swapIn(SwapInParams calldata params) external returns (uint256 amountOut, uint256 protocolFee, uint256 lpFee);
function swapOut(SwapOutParams calldata params) external returns (uint256 amountIn, uint256 protocolFee, uint256 lpFee);
}Basic Swap Example ​
Here's a complete example showing how to implement a simple swap:
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract FluidDexV2Integrator is IDexV2Callbacks {
using SafeERC20 for IERC20;
IFluidDexV2 public immutable dexV2;
constructor(address _dexV2) {
dexV2 = IFluidDexV2(_dexV2);
}
// Public function users call to swap
function swapTokens(
DexKey memory dexKey,
uint256 dexType, // 3 for D3, 4 for D4
bool swap0To1,
uint256 amountIn,
uint256 minAmountOut,
address recipient
) external returns (uint256 amountOut) {
// Encode parameters for callback
bytes memory callbackData = abi.encode(
dexKey,
dexType,
swap0To1,
amountIn,
minAmountOut,
recipient
);
// Start the operation - DEX will call startOperationCallback
bytes memory result = dexV2.startOperation(callbackData);
// Decode the result
(amountOut,,) = abi.decode(result, (uint256, uint256, uint256));
}
// DEX calls this function
function startOperationCallback(bytes calldata data) external returns (bytes memory) {
require(msg.sender == address(dexV2), "Unauthorized");
// Decode parameters from callback data
(
DexKey memory dexKey,
uint256 dexType,
bool swap0To1,
uint256 amountIn,
uint256 minAmountOut,
address recipient
) = abi.decode(data, (DexKey, uint256, bool, uint256, uint256, address));
// 1. Execute the swap via operate()
SwapInParams memory swapParams = SwapInParams({
dexKey: dexKey,
swap0To1: swap0To1,
amountIn: amountIn,
amountOutMin: minAmountOut,
controllerData: ""
});
bytes memory swapData = abi.encodeWithSelector(
bytes4(keccak256("swapIn(SwapInParams)")),
swapParams
);
bytes memory swapResult = dexV2.operate(dexType, 1, swapData); // 1 = swap module
// Decode swap result to get actual amounts
(uint256 amountOut, uint256 protocolFee, uint256 lpFee) = abi.decode(swapResult, (uint256, uint256, uint256));
// Verify we got at least the minimum amount
require(amountOut >= minAmountOut, "Insufficient output amount");
// 2. Handle token settlement
if (swap0To1) {
// Supply token0, receive token1
dexV2.settle(dexKey.token0, int256(amountIn), 0, 0, address(this), true);
dexV2.settle(dexKey.token1, 0, 0, int256(amountOut), recipient, false);
} else {
// Supply token1, receive token0
dexV2.settle(dexKey.token1, int256(amountIn), 0, 0, address(this), true);
dexV2.settle(dexKey.token0, 0, 0, int256(amountOut), recipient, false);
}
return swapResult;
}
// DEX calls this when isCallback = true in settle()
function dexCallback(address token, address to, uint256 amount) external {
require(msg.sender == address(dexV2), "Unauthorized");
// Transfer tokens from the original caller to DEX
// Note: tx.origin is the user who initiated the transaction
// Note: You can also use transient storage to store the caller address to know whom to transfer tokens from
IERC20(token).safeTransferFrom(tx.origin, to, amount);
}
}Operating DEX Modules ​
Encoding Swap Operations ​
To perform swaps, you call operate() with properly encoded data:
// For D3 and D4 swaps
uint256 constant SWAP_MODULE_ID = 1;
// Example: Encoding a swapIn operation
SwapInParams memory params = SwapInParams({
dexKey: dexKey,
swap0To1: true,
amountIn: 1000e6, // 1000 USDC
amountOutMin: 990e6, // Minimum 990 USDT
controllerData: ""
});
bytes memory operateData = abi.encodeWithSelector(
bytes4(keccak256("swapIn(SwapInParams)")),
params
);
// Execute the swap
dexV2.operate(3, 1, operateData); // dexType=3 (D3), moduleId=1 (swap)DEX Types and Module IDs ​
// DEX Types
uint256 constant D3_DEX_TYPE = 3; // Smart Collateral Range Orders
uint256 constant D4_DEX_TYPE = 4; // Smart Debt Range Orders
// Module IDs
uint256 constant SWAP_MODULE_ID = 1; // For swapIn/swapOut
uint256 constant USER_MODULE_ID = 2; // For deposit/withdraw/borrow/payback
uint256 constant CONTROLLER_MODULE_ID = 3; // For controller operationsToken Settlement with settle() ​
The settle() function handles all token transfers. Understanding its parameters is crucial:
function settle(
address token,
int256 supplyAmount, // Positive = supply TO the DEX
int256 borrowAmount, // Positive = borrow FROM the DEX
int256 storeAmount, // Positive = store IN the DEX
address to, // Where tokens go
bool isCallback // true = DEX calls dexCallback for transfer
) external payable;Settlement Patterns ​
1. Basic Token Input (User → DEX)
// User supplies 1000 USDC to DEX
dexV2.settle(
USDC_ADDRESS,
1000e6, // supplyAmount: 1000 USDC to DEX
0, // borrowAmount: not borrowing
0, // storeAmount: not storing
address(this), // to: callback will transfer from this contract
true // isCallback: DEX will call dexCallback
);2. Using Stored Balances (Advanced)
// First, store tokens in DEX for later use (saves gas in multi-step operations)
dexV2.settle(
USDC_ADDRESS,
0, // supplyAmount: not supplying to pool
0, // borrowAmount: not borrowing
1000e6, // storeAmount: store 1000 USDC in DEX
address(this), // to: callback source
true // isCallback: DEX calls back for transfer
);
// Later, use stored tokens for swap (no external transfer needed)
dexV2.settle(
USDC_ADDRESS,
1000e6, // supplyAmount: use stored tokens for swap
0, // borrowAmount: not borrowing
-1000e6, // storeAmount: negative = withdraw from storage
address(0), // to: not needed (internal transfer)
false // isCallback: not needed (internal transfer)
);Best Practices ​
- Implement proper slippage protection
- Validate callback caller authenticity
- Use router based integration for simplicity
- Use callback based integration for gas efficiency
- Batch multiple operations in startOperationCallback and settle at once at the end
Resources ​
Documentation ​
Contract Addresses ​
| Network | Contract | Address |
|---|---|---|
| Ethereum | FluidDexV2 | TBD |
| Ethereum | FluidDexV2D3D4Resolver | TBD |
| Ethereum | FluidDexV2Router | TBD |
| Polygon | FluidDexV2 | 0x04e9FF525b6541Ca4a0Dca3326b79547f9057E15 |
| Polygon | FluidDexV2Resolver | 0x2Ba521a909BDBE56183e3cd5F27962466e674610 |
| Polygon | FluidDexV2Router | 0xe16aD4692B2505d21959f22Ff32D18d808D35759 |

