Fluid DEX¶
Connector for Fluid DEX protocol.
almanak.framework.connectors.fluid
¶
Fluid DEX Connector — Phase 1 (Arbitrum swaps + LP scaffolding).
Provides swap support for Fluid DEX T1 pools on Arbitrum via swapIn(). LP open/close intent routing is wired but LP deposit reverts on-chain (phase 2).
Scope (phase 1): - Arbitrum only - Swaps via swapIn() (fully functional) - LP deposit deferred (Liquidity-layer routing causes reverts)
Key contracts (Arbitrum): - DexFactory: 0x91716C4EDA1Fb55e84Bf8b4c7085f84285c19085 - DexResolver: 0x11D80CfF056Cef4F9E6d23da8672fE9873e5cC07
Example
from almanak.framework.connectors.fluid import FluidAdapter, FluidConfig
config = FluidConfig( chain="arbitrum", wallet_address="0x...", rpc_url="https://arb-mainnet.g.alchemy.com/v2/...", ) adapter = FluidAdapter(config)
FluidAdapter
¶
High-level adapter for Fluid DEX LP operations.
Provides LP open/close with compile-time encumbrance guard. Phase 1 operates only on unencumbered pools (no smart-debt/collateral).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
FluidConfig
|
FluidConfig with chain, wallet, and RPC settings |
required |
token_resolver
|
TokenResolver | None
|
Optional TokenResolver for symbol -> address resolution |
None
|
resolve_token_address
¶
Resolve a token symbol or address to checksummed address.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token
|
str
|
Token symbol (e.g., "USDC") or address |
required |
Returns:
| Type | Description |
|---|---|
str
|
Checksummed address |
get_token_decimals
¶
Get decimals for a token.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token
|
str
|
Token symbol or address |
required |
Returns:
| Type | Description |
|---|---|
int
|
Token decimals (never defaults to 18 — raises if unknown) |
find_pool
¶
Find a Fluid DEX pool for a token pair.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token0
|
str
|
First token symbol or address |
required |
token1
|
str
|
Second token symbol or address |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
Pool address if found, None otherwise |
get_pool_data
¶
Get full pool data from the resolver.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dex_address
|
str
|
Pool contract address |
required |
Returns:
| Type | Description |
|---|---|
DexPoolData
|
DexPoolData with configs, reserves, and encumbrance flags |
build_add_liquidity_transaction
¶
build_add_liquidity_transaction(
dex_address: str,
amount0: Decimal,
amount1: Decimal,
token0_decimals: int,
token1_decimals: int,
) -> TransactionData
Build a transaction to open a new LP position.
Phase 1 limitation: Fluid DEX deposit() reverts on all pools due to complex Liquidity-layer routing. LP open is wired but not functional on-chain. Proper share calculation requires Liquidity-layer integration (follow-up).
Raises:
| Type | Description |
|---|---|
FluidSDKError
|
Always — LP deposit is not yet supported. |
build_remove_liquidity_transaction
¶
Build a transaction to close an LP position (full withdrawal).
Calls operate(nftId, -MAX_INT256, 0, wallet) on the pool contract. The negative max collateral delta means "withdraw everything".
ENCUMBRANCE GUARD: This method refuses to build the transaction if the pool has smart-collateral or smart-debt enabled.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dex_address
|
str
|
Pool contract address |
required |
nft_id
|
int
|
NFT position ID to close |
required |
Returns:
| Type | Description |
|---|---|
TransactionData
|
TransactionData with the operate() call |
Raises:
| Type | Description |
|---|---|
FluidSDKError
|
If the operation fails |
get_position_details
¶
Build FluidPositionDetails for a position.
Reads pool data from the resolver to populate APR fields.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nft_id
|
int
|
NFT position ID |
required |
dex_address
|
str
|
Pool contract address |
required |
Returns:
| Type | Description |
|---|---|
FluidPositionDetails
|
FluidPositionDetails with pool data and APR info |
build_approve_tx
¶
Build an ERC20 approval transaction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token_address
|
str
|
Token contract address |
required |
spender
|
str
|
Address to approve spending for |
required |
amount
|
int | None
|
Amount to approve (None = max uint256) |
None
|
Returns:
| Type | Description |
|---|---|
TransactionData
|
TransactionData for the approval |
extract_position_id
¶
Extract LP position NFT tokenId from operate() receipt.
Called by ResultEnricher for LP_OPEN intents.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
receipt
|
dict
|
Transaction receipt dict |
required |
Returns:
| Type | Description |
|---|---|
int | None
|
NFT position ID or None if not found |
FluidConfig
dataclass
¶
Configuration for Fluid DEX adapter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chain
|
str
|
Chain name (must be "arbitrum" for phase 1) |
required |
wallet_address
|
str
|
Address of the wallet executing transactions |
required |
rpc_url
|
str
|
RPC endpoint URL |
required |
default_slippage_bps
|
int
|
Default slippage tolerance in basis points (default: 50 = 0.5%) |
50
|
FluidPositionDetails
dataclass
¶
FluidPositionDetails(
fluid_nft_id: str,
dex_address: str,
token0: str,
token1: str,
swap_fee_apr: float = 0.0,
lending_yield_apr: float = 0.0,
combined_apr: float = 0.0,
is_smart_collateral: bool = False,
is_smart_debt: bool = False,
)
Typed position details for Fluid DEX LP positions.
Stored in PositionInfo.details as a dict (via asdict()). String NFT ID is consistent with Uniswap V3 / TraderJoe patterns.
Attributes:
| Name | Type | Description |
|---|---|---|
fluid_nft_id |
str
|
NFT token ID (string for consistency with other connectors) |
dex_address |
str
|
Pool contract address |
token0 |
str
|
Token0 address |
token1 |
str
|
Token1 address |
swap_fee_apr |
float
|
Swap fee APR (from exchange price data) |
lending_yield_apr |
float
|
Lending yield APR (from liquidity resolver) |
combined_apr |
float
|
Combined APR (swap_fee_apr + lending_yield_apr) |
is_smart_collateral |
bool
|
Whether pool has smart collateral enabled |
is_smart_debt |
bool
|
Whether pool has smart debt enabled |
FluidReceiptParser
¶
Receipt parser for Fluid DEX transactions (swaps and LP operations).
Extracts NFT position IDs and token amounts from LogOperate events. Supports both LP_OPEN (new position) and LP_CLOSE (withdrawal) receipts.
SUPPORTED_EXTRACTIONS declares which ResultEnricher fields this parser supports.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chain
|
str
|
Chain name for token resolution (default: "arbitrum") |
'arbitrum'
|
parse_receipt
¶
Parse a Fluid DEX transaction receipt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict with 'logs', 'transactionHash', etc. |
required |
Returns:
| Type | Description |
|---|---|
FluidParseResult
|
FluidParseResult with extracted events and data |
extract_position_id
¶
Extract LP position NFT tokenId from receipt.
Called by ResultEnricher for LP_OPEN intents.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict |
required |
Returns:
| Type | Description |
|---|---|
int | None
|
NFT position ID or None if not found |
extract_swap_amounts
¶
Extract swap amounts from receipt.
Called by ResultEnricher for SWAP intents. Resolves token decimals to produce human-readable decimal amounts (consistent with other parsers).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict |
required |
Returns:
| Type | Description |
|---|---|
SwapAmounts | None
|
SwapAmounts with input/output amounts, or None |
extract_lp_close_data
¶
Extract LP close data from receipt.
Called by ResultEnricher for LP_CLOSE intents. Returns amounts collected on withdrawal (negative amounts = withdrawn).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict |
required |
Returns:
| Type | Description |
|---|---|
LPCloseData | None
|
LPCloseData with collected amounts, or None if not found |
extract_liquidity
¶
Extract liquidity amount from LP_OPEN receipt.
For Fluid DEX, liquidity is represented by the collateral shares (token0_amt + token1_amt deposited).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict |
required |
Returns:
| Type | Description |
|---|---|
int | None
|
Combined deposit amount or None |
FluidSDK
¶
Low-level Fluid DEX protocol SDK.
Reads pool data directly from pool contracts using raw eth_call: - constantsView(): token addresses (words 9, 10 of 18-word response) - readFromStorage(bytes32(0)): dexVariables with smart-collateral/debt flags
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chain
|
str
|
Chain name (must be "arbitrum" for phase 1) |
required |
rpc_url
|
str
|
RPC endpoint URL |
required |
get_all_dex_addresses
¶
Get all Fluid DEX pool addresses from the resolver.
get_dex_data
¶
Get pool data by calling constantsView() and readFromStorage() directly.
Uses raw eth_call to avoid ABI decoding issues with the complex DexEntireData struct. constantsView() returns 18 words: - word[9]: token0 address - word[10]: token1 address
readFromStorage(bytes32(0)) returns dexVariables: - bit 1: isSmartCollateralEnabled - bit 2: isSmartDebtEnabled
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dex_address
|
str
|
Pool contract address |
required |
Returns:
| Type | Description |
|---|---|
DexPoolData
|
DexPoolData with token addresses and encumbrance flags |
is_position_encumbered
¶
Check if a specific position has outstanding debt.
In Fluid DEX, ALL pools have smart-debt capability (that's the design). The encumbrance check is at the POSITION level, not pool level: - Positions we create with newDebt=0 have no debt, so they're safe to close. - For safety, we verify the pool's smart-debt flag but don't block on it for positions we know were opened without debt.
For phase 1, this always returns False for nft_id=0 (new position check) since we enforce newDebt=0 in build_operate_tx().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dex_address
|
str
|
Pool contract address |
required |
nft_id
|
int
|
NFT position ID (0 = checking for new position) |
0
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the position has outstanding debt |
find_dex_by_tokens
¶
Find a Fluid DEX pool for a given token pair.
Token order is automatically handled (tries both orderings).
get_swap_quote
¶
Get a swap quote (estimate) from a Fluid DEX pool.
Calls swapIn via eth_call with state overrides to simulate token approval and balance. Without overrides, swapIn() reverts because it internally calls transferFrom() which requires prior approval.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dex_address
|
str
|
Pool contract address |
required |
swap0to1
|
bool
|
True to swap token0->token1, False for token1->token0 |
required |
amount_in
|
int
|
Input amount in token's smallest unit |
required |
to
|
str
|
Recipient address |
required |
Returns:
| Type | Description |
|---|---|
int
|
Expected output amount in token's smallest unit |
build_swap_tx
¶
build_swap_tx(
dex_address: str,
swap0to1: bool,
amount_in: int,
amount_out_min: int,
to: str,
value: int = 0,
) -> dict[str, Any]
Build a swapIn transaction for a Fluid DEX pool.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dex_address
|
str
|
Pool contract address |
required |
swap0to1
|
bool
|
True to swap token0->token1, False for token1->token0 |
required |
amount_in
|
int
|
Input amount in token's smallest unit |
required |
amount_out_min
|
int
|
Minimum acceptable output (slippage protection) |
required |
to
|
str
|
Recipient address |
required |
value
|
int
|
Native token value (for ETH-paired swaps) |
0
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Transaction dict with 'to', 'data', 'value', 'gas' |
build_operate_tx
¶
build_operate_tx(
dex_address: str,
nft_id: int,
new_col: int,
new_debt: int,
to: str,
) -> dict[str, Any]
Build an operate() transaction for a Fluid DEX pool.
operate() is the main entry point for LP operations: - Open position: nft_id=0, new_col>0, new_debt=0 - Close position: nft_id=X, new_col<0 (negative = withdraw), new_debt=0