Skip to content

Fluid DEX Lite: Liquidity Source Integration ​

Complete integration guide for adding Fluid DEX Lite as a liquidity source in DEX aggregators and routing protocols.

Table of Contents ​

  1. Overview
  2. Quick Start
  3. Integration Methods
  4. Pool Discovery & State Reading
  5. Price Estimation
  6. Swap Execution
  7. Gas Optimization
  8. Legacy Integration
  9. Best Practices
  10. Resources

Overview ​

What is Fluid DEX Lite? ​

Fluid DEX Lite is an ultra-gas-optimized DEX specialized for correlated asset pairs, featuring:

  • Ultra-Low Gas: ~10k gas per swap (most efficient on Ethereum)
  • Singleton Architecture: Multiple pools under one contract for efficient multi-hop routing
  • Easy Integration: Comprehensive resolver contract with clean APIs
  • Multi-hop Support: Native support for complex routing paths

Architecture ​

  • Core Contract: FluidDexLite - Handles all swaps and pool state
  • Resolver Contract: FluidDexLiteResolver - Provides discovery, state reading, and estimation APIs
  • Storage: Packed storage design for gas optimization
  • Pools: Governance-controlled pool launches for correlated pairs

Quick Start ​

1. Integration Method ​

Standard: Resolver-based Integration

  • Use FluidDexLiteResolver for discovery, state reading, and estimation
  • Clean APIs, no storage manipulation required
  • Gas-free price estimation
  • This is the recommended approach for all new integrations

Note: This guide also includes a legacy section for integrators who have already implemented direct contract integration.

2. Core Data Structures ​

solidity
struct DexKey {
    address token0;    // Lexicographically smaller token
    address token1;    // Lexicographically larger token
    bytes32 salt;      // Pool salt
}

struct DexState {
    DexVariables dexVariables;
    CenterPriceShift centerPriceShift;
    RangeShift rangeShift;
    ThresholdShift thresholdShift;
}

struct DexVariables {
    uint256 fee;
    uint256 revenueCut;
    uint256 rebalancingStatus;
    bool isCenterPriceShiftActive;
    uint256 centerPrice;
    address centerPriceAddress;
    bool isRangePercentShiftActive;
    uint256 upperRangePercent;
    uint256 lowerRangePercent;
    bool isThresholdPercentShiftActive;
    uint256 upperShiftThresholdPercent;
    uint256 lowerShiftThresholdPercent;
    uint256 token0Decimals;
    uint256 token1Decimals;
    uint256 totalToken0AdjustedAmount;
    uint256 totalToken1AdjustedAmount;
}

struct CenterPriceShift {
    uint256 lastInteractionTimestamp;
    uint256 rebalancingShiftingTime;
    uint256 maxCenterPrice;
    uint256 minCenterPrice;
    uint256 shiftPercentage;
    uint256 centerPriceShiftingTime;
    uint256 startTimestamp;
}

struct RangeShift {
    uint256 oldUpperRangePercent;
    uint256 oldLowerRangePercent;
    uint256 shiftingTime;
    uint256 startTimestamp;
}

struct ThresholdShift {
    uint256 oldUpperThresholdPercent;
    uint256 oldLowerThresholdPercent;
    uint256 shiftingTime;
    uint256 startTimestamp;
}

struct Prices {
    uint256 poolPrice;
    uint256 centerPrice;
    uint256 upperRangePrice;
    uint256 lowerRangePrice;
    uint256 upperThresholdPrice;
    uint256 lowerThresholdPrice;
}

struct Reserves {
    uint256 token0RealReserves;
    uint256 token1RealReserves;
    uint256 token0ImaginaryReserves;
    uint256 token1ImaginaryReserves;
}

3. Contract Addresses ​

NetworkContractAddress
Ethereum MainnetFluidDexLite0xBbcb91440523216e2b87052A99F69c604A7b6e00
Ethereum MainnetFluidDexLiteResolver (v1.0.0)0x26b696D0dfDAB6c894Aa9a6575fCD07BB25BbD2C

Integration Methods ​

Use the resolver contract for all discovery, state reading, and estimation operations.

Key Methods ​

solidity
contract FluidDexLiteResolver {
    // Pool Discovery
    function getAllDexes() public view returns (DexKey[] memory);
    
    // State Reading
    function getDexState(DexKey memory dexKey) public view returns (DexState memory);
    function getPricesAndReserves(DexKey memory dexKey) public returns (Prices memory, Reserves memory);
    function getDexEntireData(DexKey memory dexKey) public returns (DexEntireData memory);
    function getAllDexesEntireData() public returns (DexEntireData[] memory);
    
    // Price Estimation (Gas-free)
    function estimateSwapSingle(DexKey calldata dexKey, bool swap0To1, int256 amountSpecified) public returns (uint256);
    function estimateSwapHop(address[] calldata path, DexKey[] calldata dexKeys, int256 amountSpecified) public returns (uint256);
}

Basic Integration Example ​

solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.29;

import "contracts/periphery/resolvers/dexLite/main.sol";

contract FluidDexLiteIntegrator {
    FluidDexLiteResolver constant resolver = FluidDexLiteResolver(
        0x26b696D0dfDAB6c894Aa9a6575fCD07BB25BbD2C
    );
    
    function getPoolData() external returns (
        uint256 poolCount,
        bytes8 firstDexId,
        uint256 poolPrice,
        uint256 token0Reserves,
        uint256 token1Reserves
    ) {
        // Discover all pools
        DexKey[] memory dexes = resolver.getAllDexes();
        poolCount = dexes.length;
        
        if (poolCount > 0) {
            // Get complete data for first pool
            DexEntireData memory data = resolver.getDexEntireData(dexes[0]);
            firstDexId = data.dexId;
            poolPrice = data.prices.poolPrice;
            token0Reserves = data.reserves.token0RealReserves;
            token1Reserves = data.reserves.token1RealReserves;
        }
    }
    
    function estimateSwap(
        DexKey memory dexKey,
        bool swap0To1,
        uint256 amountIn
    ) external returns (uint256 amountOut) {
        return resolver.estimateSwapSingle(dexKey, swap0To1, int256(amountIn));
    }
}

Pool Discovery & State Reading ​

Discovering Available Pools ​

solidity
function discoverPools() external view returns (DexKey[] memory pools) {
    pools = resolver.getAllDexes();
    
    // Each pool is identified by DexKey with:
    // - token0: lexicographically smaller token address
    // - token1: lexicographically larger token address  
    // - salt: unique identifier (typically 0x00...)
}

Reading Pool State ​

solidity
function readPoolDetails(DexKey memory dexKey) external returns (
    uint256 fee,
    uint256 poolPrice,
    uint256 token0Decimals,
    uint256 token1Decimals,
    uint256 realReserves0,
    uint256 realReserves1
) {
    // Get complete pool data
    DexEntireData memory data = resolver.getDexEntireData(dexKey);
    
    fee = data.dexState.dexVariables.fee;
    poolPrice = data.prices.poolPrice;
    token0Decimals = data.dexState.dexVariables.token0Decimals;
    token1Decimals = data.dexState.dexVariables.token1Decimals;
    realReserves0 = data.reserves.token0RealReserves;
    realReserves1 = data.reserves.token1RealReserves;
}

Batch Reading All Pools ​

solidity
function readAllPoolsData() external returns (DexEntireData[] memory) {
    // Get complete data for all pools in one call
    return resolver.getAllDexesEntireData();
}

Price Estimation ​

Single Pool Estimation ​

solidity
function estimateSingleSwap(
    DexKey memory dexKey,
    bool swap0To1,
    uint256 amountIn
) external returns (uint256 amountOut) {
    // Gas-free estimation using resolver
    amountOut = resolver.estimateSwapSingle(
        dexKey,
        swap0To1,
        int256(amountIn)  // Positive for exact input
    );
}

function estimateExactOutput(
    DexKey memory dexKey,
    bool swap0To1,
    uint256 amountOut
) external returns (uint256 amountIn) {
    // Estimate required input for exact output
    amountIn = resolver.estimateSwapSingle(
        dexKey,
        swap0To1,
        -int256(amountOut)  // Negative for exact output
    );
}

Multi-hop Estimation ​

solidity
function estimateMultiHop(
    address[] memory path,
    DexKey[] memory dexKeys,
    uint256 amountIn
) external returns (uint256 amountOut) {
    // Gas-free multi-hop estimation
    amountOut = resolver.estimateSwapHop(path, dexKeys, int256(amountIn));
}

Example: USDC → USDT → DAI ​

solidity
function estimateUSDCToDAI(uint256 usdcAmount) external returns (uint256 daiAmount) {
    // Define path: USDC → USDT → DAI
    address[] memory path = new address[](3);
    path[0] = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // USDC
    path[1] = 0xdAC17F958D2ee523a2206206994597C13D831ec7; // USDT
    path[2] = 0x6B175474E89094C44Da98b954EedeAC495271d0F; // DAI
    
    // Define pools for each hop
    DexKey[] memory dexKeys = new DexKey[](2);
    dexKeys[0] = DexKey({
        token0: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, // USDC-USDT
        token1: 0xdAC17F958D2ee523a2206206994597C13D831ec7,
        salt: 0x0000000000000000000000000000000000000000000000000000000000000000
    });
    dexKeys[1] = DexKey({
        token0: 0x6B175474E89094C44Da98b954EedeAC495271d0F, // USDT-DAI
        token1: 0xdAC17F958D2ee523a2206206994597C13D831ec7,
        salt: 0x0000000000000000000000000000000000000000000000000000000000000000
    });
    
    daiAmount = resolver.estimateSwapHop(path, dexKeys, int256(usdcAmount));
}

Swap Execution ​

After estimation, execute swaps through the core FluidDexLite contract.

Single Pool Swap ​

solidity
interface IFluidDexLite {
    function swapSingle(
        DexKey calldata dexKey,
        bool swap0To1,
        int256 amountSpecified,
        uint256 amountLimit,
        address to,
        bool isCallback,
        bytes calldata callbackData,
        bytes calldata extraData
    ) external payable returns (uint256 amountUnspecified);
}

function executeSwap(
    DexKey memory dexKey,
    bool swap0To1,
    uint256 amountIn,
    uint256 minAmountOut,
    address recipient
) external {
    IFluidDexLite dexLite = IFluidDexLite(0xBbcb91440523216e2b87052A99F69c604A7b6e00);
    
    // Execute swap
    uint256 amountOut = dexLite.swapSingle(
        dexKey,
        swap0To1,
        int256(amountIn),     // Exact input amount
        minAmountOut,         // Minimum output (slippage protection)
        recipient,            // Token recipient
        false,                // No callback
        "",                   // No callback data
        ""                    // No extra data
    );
}

Multi-hop Swap ​

solidity
struct TransferParams {
    address to;
    bool isCallback;
    bytes callbackData;
    bytes extraData;
}

function executeMultiHop(
    address[] memory path,
    DexKey[] memory dexKeys,
    uint256 amountIn,
    uint256 minAmountOut,
    address recipient
) external {
    IFluidDexLite dexLite = IFluidDexLite(0xBbcb91440523216e2b87052A99F69c604A7b6e00);
    
    // Set amount limits for each hop (0 = no limit except final)
    uint256[] memory amountLimits = new uint256[](dexKeys.length);
    amountLimits[amountLimits.length - 1] = minAmountOut; // Final output limit
    
    TransferParams memory transferParams = TransferParams({
        to: recipient,
        isCallback: false,
        callbackData: "",
        extraData: ""
    });
    
    uint256 finalAmount = dexLite.swapHop(
        path,
        dexKeys,
        int256(amountIn),
        amountLimits,
        transferParams
    );
}

Gas Optimization ​

Callback Pattern ​

Implement callbacks to save ~5k gas per swap by optimizing token transfers.

solidity
interface IDexLiteCallback {
    function dexCallback(address token, uint256 amount, bytes calldata data) external;
}

contract OptimizedIntegrator is IDexLiteCallback {
    address constant FLUID_DEX_LITE = 0xBbcb91440523216e2b87052A99F69c604A7b6e00;
    
    function executeOptimizedSwap(
        DexKey memory dexKey,
        bool swap0To1,
        uint256 amountIn,
        uint256 minAmountOut
    ) external {
        IFluidDexLite(FLUID_DEX_LITE).swapSingle(
            dexKey,
            swap0To1,
            int256(amountIn),
            minAmountOut,
            msg.sender,
            true,        // Enable callback
            "",
            ""
        );
    }
    
    function dexCallback(address token, uint256 amount, bytes calldata) external override {
        require(msg.sender == FLUID_DEX_LITE, "Unauthorized");
        
        // Transfer tokens to DEX contract
        IERC20(token).transferFrom(tx.origin, FLUID_DEX_LITE, amount);
    }
}

Multi-hop Optimization ​

Use swapHop instead of multiple separate swaps to save gas on intermediate token transfers.


Legacy Integration ​

Direct Contract Integration ​

For existing integrations using direct contract interaction:

Storage-based Pool Discovery ​

solidity
// Read pool count
uint256 poolCount = uint256(fluidDexLite.readFromStorage(bytes32(1)));

// Read DexKey at index
function readDexKeyAtIndex(uint256 index) view returns (DexKey memory) {
    bytes32 baseSlot = keccak256(abi.encode(1));
    
    address token0 = address(uint160(uint256(
        fluidDexLite.readFromStorage(bytes32(uint256(baseSlot) + index * 3))
    )));
    address token1 = address(uint160(uint256(
        fluidDexLite.readFromStorage(bytes32(uint256(baseSlot) + index * 3 + 1))
    )));
    bytes32 salt = fluidDexLite.readFromStorage(
        bytes32(uint256(baseSlot) + index * 3 + 2)
    );
    
    return DexKey(token0, token1, salt);
}

Manual Estimation Pattern ​

solidity
bytes32 constant ESTIMATE_SWAP = keccak256("ESTIMATE_SWAP");

try fluidDexLite.swapSingle(
    dexKey,
    swap0To1,
    int256(amountIn),
    0,
    address(0),
    false,
    "",
    abi.encode(ESTIMATE_SWAP)
) {
    revert("Should not succeed");
} catch (bytes memory reason) {
    if (reason.length >= 36) {
        bytes4 selector = bytes4(reason);
        if (selector == bytes4(keccak256("EstimateSwap(uint256)"))) {
            assembly {
                amountOut := mload(add(reason, 36))
            }
        }
    }
}

Best Practices ​

Gas Optimization ​

  1. Implement callback pattern for swap execution
  2. Batch multi-hop swaps using swapHop

Error Handling ​

  1. Always use try-catch for estimation
  2. Implement proper slippage protection
  3. Handle edge cases (zero liquidity, large swaps)

Security ​

  1. Validate all DexKey parameters
  2. Use proper slippage calculations
  3. Implement deadline checks
  4. Verify callback caller authenticity

Resources ​

Documentation ​

Production Implementations ​

  1. ParaSwap Integration (TypeScript)

  2. KyberSwap Integration (Golang)

Contract Addresses ​

NetworkContractAddress
Ethereum MainnetFluidDexLite0xBbcb91440523216e2b87052A99F69c604A7b6e00
Ethereum MainnetFluidDexLiteResolver (v1.0.0)0x26b696D0dfDAB6c894Aa9a6575fCD07BB25BbD2C