Skip to content

Fluid Dex: Liquidity source integration ​

Guide on integrating Fluid Dex swaps as a Liquidity Source.

Integration benefits ​

Adding Fluid Dex swaps as a liquidity source offers several benefits:

  1. Best liquidity: Fluid Dex has the deepest liquidity for almost any token pair with a launched pool (e.g. wstETH-ETH, cbBTC-WBTC, ...). More pools will be launched continuously.
  2. Easy Integration: Simple contract calls and payload building process through our resolver contracts.
  3. Gas Efficiency: Swaps cost approximately 90k to 145k gas.
  4. Arbitrage Opportunities: Can be leveraged with other DEXes for arbitrage setups.
  5. Mathematical Consistency: Fluid Dex utilizes the x * y = k constant product formula (similar to Uniswap v2).

Guide ​

Liquidity Fetching ​

DexReservesResolver provides each pool's reserves amounts.

Triggers for liquidity fetching should be:

  1. events: either any event at a pool OR the LogOperate event at the Liquidity Layer (0x52Aa899454998Be5b000Ad077a46Bbe360F4e497) filtered by user == pool addresses.
  2. in addition also trigger if no event happened in the last 5 to max 10 minutes at a pool (otherwise there is some important internal pricing logic not accounted for)

Price estimation ​

DexReservesResolver provides estimate() methods for all types of swaps. Alternatively, we have Fluid Dex math logic replication libraries for JS (see public repo), TS (see Paraswap reference implementation) and Golang (see Kyberswap reference implementation), to be used in combination with the DexReservesResolver for keeping updated pool state data. Note you must pass in the reserves fetched from the "Adjusted" suffix-named methods at the Resolver, which are scaled to 1e12 decimals.

On-chain settlement ​

Execute the swapIn() (or swapOut) method at the respective Dex pool.

Deployment addresses ​

See deployment addresses in public repo.

Important: Avoid reverts ​

  • Due to the innovative architecture, the swappable amount depends on the actual token balance at the liquidity layer, on limits for borrowable and withdrawable amounts (which can expand over time), on an utilization limit and other similar limits. The DexReservesResolver returns all limits as limits (available, expandTo, expandDuration) in token decimals. Just pass in this limits object to the provided swap() methods in the DexMath code in JS or Golang alongside the syncTime of when those limits were fetched. The expansion of limits will be considered and all potential reverts will be caught accordingly. The estimate() methods on the resolver already include those checks.
  • The provided DexMath code also includes revert checks for other potential reverts such as much price movement within 1 swap, minimum pool ratio etc.
  • The pool is pausable, trackable via event LogPauseSwapAndArbitrage() and event LogUnpauseSwapAndArbitrage(). Alternatively this is available through the DexState in the DexResolver or can be directly read from storage (check code here).

Provide the best pricing: Optimize gas usage ​

To provide the best swaps to users, Fluid Dex provides a simple way to optimize gas cost by skipping token transfer steps through swapInWithCallback().

Fluid Dex transfers callback

The integration contract must implement (see code):

interface IDexCallback {
    function dexCallback(address token_, uint256 amount_) external;
}

contract MockSampleDexCallback is IDexCallback {
    using SafeERC20 for IERC20;

    address constant DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD;
    address public immutable FLUID_LIQUIDITY;

    address public senderTransient;

    constructor(address fluidLiquidity_) {
        FLUID_LIQUIDITY = fluidLiquidity_;
        senderTransient = DEAD_ADDRESS; // so to DEAD_ADDRESS to optimize gas refunds
    }

    // [...] Integration logic for swap, which triggers FluidDex.swapInWithCallback()
    // senderTransient = msg.sender; // tmp store (use transient on newer Solidity versions)

    // @INTEGRATOR: MUST IMPLEMENT:
    function dexCallback(address token_, uint256 amount_) external override {
        // ideally, transfer tokens from User -> Fluid liquidity layer:
        IERC20(token_).safeTransferFrom(senderTransient, FLUID_LIQUIDITY, amount_);

        senderTransient = DEAD_ADDRESS; // reset for ~5k gas refund

        // Alternative if tokens must be transferred from user -> integration contract first
        // IERC20(token_).safeTransfer(liquidityContract, amount_);
    }
}

Additional references ​