Skip to content

Fluid Dex Lite Integration#25

Open
Vaibhav20201 wants to merge 1 commit intogluexprotocol:mainfrom
Vaibhav20201:main
Open

Fluid Dex Lite Integration#25
Vaibhav20201 wants to merge 1 commit intogluexprotocol:mainfrom
Vaibhav20201:main

Conversation

@Vaibhav20201
Copy link
Copy Markdown

Pull Request: Fluid Dex Lite Liquidity Module Integration

Protocol Information

  • Protocol Name: Fluid Dex Lite
  • Protocol Website: https://fluid.io/
  • Indexing Smart Contract Addresses:
    • Ethereum Mainnet:
      • 0xBbcb91440523216e2b87052A99F69c604A7b6e00 (FluidDexLite Core Contract)

Summary of Integration

Fluid Dex Lite is a sophisticated automated market maker (AMM) protocol that provides:

  • Concentrated liquidity: Efficient capital utilization through dynamic price ranges
  • Dynamic rebalancing: Automatic price adjustments based on market conditions
  • Real-time simulation: Accurate pricing with time-based rebalancing mechanisms

Unique Features for GlueX:

  • Complex bit-packed state variables requiring precise unpacking
  • BigNumber format for center price compression/expansion
  • Time-dependent rebalancing logic for accurate simulation
  • Multi-layer price shifting mechanisms (center, range, threshold)
  • Advanced mathematical formulas matching Solidity contract exactly

Implementation Details

Execution Functions Required

All functions interact with the FluidDexLite core contract:

  • Swap Functions: swapSingle() and swapHop() for token exchanges
  • State Reading: readFromStorage() for accessing packed dex variables
  • Price Calculation: Internal _getPricesAndReserves() for current pricing

Functions Implemented

  • get_amount_out(): Implements exact constant product formula with:

    • Decimal adjustment for token precision differences
    • Fee calculation with revenue cut logic
    • Supply updates after swaps for state consistency
    • All 5 contract revert conditions for validation
  • get_amount_in(): Reverse calculation using:

    • Inverse constant product formula: amountIn = (amountOut * reserveIn) / (reserveOut - amountOut)
    • Fee adjustment: fee = ((amountIn * SIX_DECIMALS) / (SIX_DECIMALS - feeBps)) - amountIn
    • Same validation and supply update logic as get_amount_out
  • get_apy(): Returns fixed 2% APY (hardcoded as external API unavailable)

  • get_tvl(): Calculates total value locked by:

    • Summing adjusted token supplies from dex variables
    • Converting to USD assuming ~$1 values for both tokens

Dynamic States Required for AMM Calculations

  • dex_variables: 256-bit packed state containing all pool parameters
  • center_price_shift: Time-based price shifting data for rebalancing
  • range_shift: Dynamic range adjustment parameters (if active)
  • threshold_shift: Threshold shifting data for rebalancing triggers

Dependencies

N/A - All calculations are self-contained using:

  • SafeMath utility for overflow protection
  • Standard Python libraries (time, typing, decimal)
  • GlueX framework templates

Other Requirements

  • API Keys: N/A
  • Special Access Requirements: N/A
  • Python Version: 3.10+ (for union type syntax)

Test Results

Comprehensive Test Suite: 15/15 Tests Passing ✅

Test Coverage:

  • Small amount swaps ($1 USDC)
  • Large amount swaps ($1000 USDC)
  • Bidirectional swaps (USDC↔USDT)
  • Inverse calculations (get_amount_in)
  • Edge cases (tiny amounts, excessive amounts)
  • Error conditions (uninitialized pools)
  • Mathematical precision across different amounts
  • Fee calculation consistency
  • TVL and APY functions

Performance Verification (1000 USDC Swap):

📥 INPUT: 1000.00 USDC
💸 FEE: 0.005000 USDC (0.0005%)
📤 OUTPUT: 999.702189 USDT
📈 EXCHANGE RATE: 0.999702189 USDT per USDC
🎯 PRICE IMPACT: 0.029281% (excellent for stablecoins)

Unpacked Dex State Verification:

fee: 5 basis points
revenue_cut: 0%
rebalancing_status: 0 (inactive)
center_price: 999,999,999,725,139,423,651,692,544
upper_percent: 15 (0.15%)
lower_percent: 15 (0.15%)
token0_decimals: 6
token1_decimals: 6
token0_total_supply_adjusted: 2,394,938,887,613,916
token1_total_supply_adjusted: 1,613,626,676,171,497

Edge Cases Tested:

  • ✅ Excessive swap amounts properly rejected
  • ✅ Tiny amounts below minimum properly rejected
  • ✅ Uninitialized pools properly rejected
  • ✅ Mathematical precision maintained across all ranges
  • ✅ Bidirectional consistency verified (forward/reverse calculations)

@Ishita-GlueX
Copy link
Copy Markdown

To move forward, we’d need the factory address so we can fetch and index all the tokens and pairs (either through events or via a read method).

If the protocol has only a single pool instead of a factory setup, then we’d just need the tokens associated with that pool in order to proceed.

Could you share whichever of these applies?

@Vaibhav20201
Copy link
Copy Markdown
Author

To move forward, we’d need the factory address so we can fetch and index all the tokens and pairs (either through events or via a read method).

If the protocol has only a single pool instead of a factory setup, then we’d just need the tokens associated with that pool in order to proceed.

Could you share whichever of these applies?

Yes you can use our resolver's function getAllDexes() which will give you the list of dexKey's

FluidDexLiteResolver - 0x26b696D0dfDAB6c894Aa9a6575fCD07BB25BbD2C

DexKey is a struct which looks like this
image

@Ishita-GlueX
Copy link
Copy Markdown

Ishita-GlueX commented Aug 21, 2025

I can see everything is being decoded from dex_variables, but could you clarify where we should be fetching dex_variables from?

Also, I passed the following input to fetch the dexState from the resolver contract and got an error. Could you share an example or the correct way to query it?

Input:
(0x4c9EDD5852cd905f086C759E8383e09bff1E68B3,0xdAC17F958D2ee523a2206206994597C13D831ec7,0x0000000000000000000000000000000000000000000000000000000000000000)

Error:
tuple Error: invalid tuple value (argument="tuple", value=0, code=INVALID_ARGUMENT, version=abi/5.7.0)

# Center price shift data
'center_price_shift': 0x0000000000000000000000000000000000019d6228d9dcc28dfffffe688c1fe7,

# Range shift data (currently not active)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that range_shift and threshold_shift are currently set to inactive (0x00…00). They also don’t seem to be used anywhere in the AMM logic at the moment. Could you clarify if these variables will have any impact on the AMM once they’re activated in the future?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lower_range_percent = unpacked_vars['lower_percent']

if unpacked_vars['range_percent_shift_active']:
range_shift_data = pool_state['range_shift']
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Range shift is used here

@Vaibhav20201
Copy link
Copy Markdown
Author

I can see everything is being decoded from dex_variables, but could you clarify where we should be fetching dex_variables from?

Our dex lite has a function called readFromStorage() which will help you read the state

Dex Lite Address - 0xBbcb91440523216e2b87052A99F69c604A7b6e00

This code snippets from the resolver should give you context
image
image

Also, I passed the following input to fetch the dexState from the resolver contract and got an error. Could you share an example or the correct way to query it?

Input: (0x4c9EDD5852cd905f086C759E8383e09bff1E68B3,0xdAC17F958D2ee523a2206206994597C13D831ec7,0x0000000000000000000000000000000000000000000000000000000000000000)

Error: tuple Error: invalid tuple value (argument="tuple", value=0, code=INVALID_ARGUMENT, version=abi/5.7.0)

Which function of the resolver were you trying to use?

Btw these are our integration docs, you will find examples for everything here
https://docs.fluid.instadapp.io/integrate/dex-lite-swaps.html#resolver-based-integration-recommended

# - Fee collection from trading volume
# - Stability of stablecoin pairs
# - Historical performance of similar pools
return Decimal('0.02') # 2% APY - reasonable for stablecoin pairs
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of hardcoding APY (e.g. 0.02), we need to calculate it dynamically using a generalized formula based on fees, rewards, and liquidity. Hardcoding won’t work across different pools.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The APY depends on the volume the pool is getting, not sure how we can calculate it here without access to any historical data

@Ishita-GlueX
Copy link
Copy Markdown

We need a unique identifier to store pool info — generally, this is the pool address, but in Fluid DEX Lite there isn’t a pool address and currently the salt for both pools is the same. Could there be a possibility that, in the future, the same token pairs are added with different salts? This would help us understand what key to rely on for uniquely identifying pools in our DB.

These are the dexes now.
[[0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48,0xdAC17F958D2ee523a2206206994597C13D831ec7,0x0000000000000000000000000000000000000000000000000000000000000000]
[0x4c9EDD5852cd905f086C759E8383e09bff1E68B3,0xdAC17F958D2ee523a2206206994597C13D831ec7,0x0000000000000000000000000000000000000000000000000000000000000000]]

@Vaibhav20201
Copy link
Copy Markdown
Author

We need a unique identifier to store pool info — generally, this is the pool address, but in Fluid DEX Lite there isn’t a pool address and currently the salt for both pools is the same. Could there be a possibility that, in the future, the same token pairs are added with different salts? This would help us understand what key to rely on for uniquely identifying pools in our DB.

These are the dexes now. [[0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48,0xdAC17F958D2ee523a2206206994597C13D831ec7,0x0000000000000000000000000000000000000000000000000000000000000000] [0x4c9EDD5852cd905f086C759E8383e09bff1E68B3,0xdAC17F958D2ee523a2206206994597C13D831ec7,0x0000000000000000000000000000000000000000000000000000000000000000]]

Yes its possible to have more than one pool for a pair, they will have different salts. You can use the dexId as the identifier, it is basically the hash of the dexKey

bytes8 dexId = bytes8(keccak256(abi.encode(dexKey)));

@Ishita-GlueX
Copy link
Copy Markdown

Ishita-GlueX commented Aug 25, 2025

The quoted amounts and computed amounts are not matching.

Here is the code we’re running (based on your implementation):
https://gist.github.com/Ishita-GlueX/3c7ffa3ab9821c7aa8ecbd96147ef0ce

We are using getDexState to fetch all the states, so I’ve removed the bits calculations from the AMM since we are already getting the direct data.

Also attaching an image with a few cases where the amounts are deviating. Please review the code and outputs to help resolve the discrepancy.

get_amount_out results:

Screenshot 2025-08-25 at 4 07 34 PM

get_amount_in results:

Screenshot 2025-08-25 at 4 09 10 PM 8" />

@Vaibhav20201
Copy link
Copy Markdown
Author

I think the issue is here

image

The resolver returns expanded center price, hence we dont need to expand it again here

@Ishita-GlueX
Copy link
Copy Markdown

Ishita-GlueX commented Aug 27, 2025

get_amount_out is running fine but there are few cases in get_amount_in for which the quoted amounts are None but computed amount is returned from amm logic.

Here are the results for your reference.

Screenshot 2025-08-27 at 2 38 34 PM

Also while testing the actual txn(swapSingle) execution. In this particular case for get_amount_in, the computed get_amount_in is slightly off compared to the on-chain execution.

Here is the tenderly simulation you can have a look.
https://dashboard.tenderly.co/explorer/vnet/57e8cb8e-ad41-401f-98c5-c88817e9681c/tx/0x89d357dccf9cf893f617efbf0252146dd45deea463978789633ca9e310b173e8

For example:
Screenshot 2025-08-27 at 2 45 17 PM

In both the above cases, the issue seems to be with get_amount_in. Could you please recheck the logic there? get_amount_out is running fine for both.

@Ishita-GlueX
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants