Crypto Training
Fluid Deep Dive: DEX v2 Architecture, Money Market Flows, and Security Threat Models
A code-informed, security-first walkthrough of Fluid's Liquidity Layer, DEX v2 (D3/D4), and Money Market: architecture, user stories, fund flows, invariants, and vulnerability hunting playbooks.
Fluid is one of the most interesting protocol stacks to study right now because it combines:
- a shared Liquidity Layer
- AMM logic (DEX v2)
- lending-style debt/collateral semantics
- concentrated liquidity
- aggressive gas optimization and precision trade-offs
This post is a security-oriented architecture guide written from code and docs, not just from marketing diagrams. The goal is to build a mental model you can actually use for research, integration reviews, and audits.
Primary references:
2026-01-fluid-dex-v2/fluid-contracts(contest/audit codebase)mellow/fluid/fluid-contracts-public(public repo with Liquidity Layer context)external-docs/dex-v2-swaps.htmlexternal-docs/understanding-fluid-key-concepts.htmlexternal-docs/blog-fluid-dex-v2.html
Why Fluid's model is unusual#
Most DeFi systems are either:
- pure AMM with reserves and fee accounting, or
- pure lending market with collateral and debt books.
Fluid DEX v2 intentionally blurs this by introducing smart collateral (D3) and smart debt (D4), with range orders and money market integration.
The key consequence: a single user action may alter AMM state, debt/supply accounting, fee growth, and collateral health in one atomic flow.
Layered architecture#
At a high level, think in three planes:
- Settlement plane: Liquidity Layer holds funds and executes supply/borrow/payback/withdraw.
- Logic plane: DEX v2 modules (D3/D4) update pool and position state.
- Risk plane: Money Market computes health factor, caps, liquidation bounds, and position lifecycle.
DEX v2 execution model: startOperation -> callback -> settle#
The most important primitive in DEX v2 is not swap; it is the operation envelope.
In dexV2/base/core/main.sol:
startOperation(bytes)activates transient operation state.- Caller callback (
startOperationCallback) executes arbitrary module calls viaoperate(...). - Pending transfers must be fully cleared before completion via
PendingTransfers.requireAllPendingTransfersCleared().
This gives DEX v2 an explicit flash-accounting-like envelope.
Why this matters for security#
The invariant is simple but deep:
- Module logic may create pending deltas.
- Only successful settlement paths must clear them.
- If pending deltas survive the operation, the whole transaction reverts.
That blocks many classes of "state moved but funds unpaid" bugs.
The Pending Transfers ledger (transient)#
pendingTransfers.sol keeps per-user per-token pending values in transient storage (tstore/tload) and tracks non-zero entry counts.
Useful mental shortcut: pending values are the atomic IOU book for the current operation envelope.
settle() is a mini settlement engine#
settle is not just transfer logic. It supports:
- net supply and borrow
- storing/un-storing balances in DEX contract
- callback-based funding
- fallback paths when Liquidity interaction fails
A lot of subtle attack surface sits here: callback authorization, balance delta checks, branch-specific accounting, and fallback consistency.
D3 vs D4: same shell, different economics#
Both are concentrated-liquidity modules, but the accounting interpretation differs.
D3 (Smart Collateral)#
D3 behaves like supply-oriented range liquidity:
- swaps use supply exchange price conversions
- position actions are
deposit/withdraw - pending changes are mostly in pending supply
D4 (Smart Debt)#
D4 is debt-oriented liquidity:
- swaps use borrow exchange price conversions
- position actions are
borrow/payback - pending borrow and pending supply can both move in one action (fees + debt netting)
The D4 fee model is one of the most non-trivial parts of the system and deserves targeted invariant testing.
Money Market as NFT position orchestrator#
Money Market wraps user state into NFT positions and can route D3/D4 actions through DEX v2 callbacks.
In moneyMarket/core/operateModule/main.sol:
operate(nftId, positionIndex, actionData)can create new NFT/position or mutate existing one.- For D3/D4 positions, it decodes position metadata and calls
DEX_V2.startOperation(...).
In moneyMarket/core/callbackModule/main.sol:
- callback validates sender (
msg.sender == DEX_V2) - branches by position type and action sign
- executes DEX user-module calls
- settles amounts
- updates fee storage and caps
- checks health factor where needed
User stories and real use cases#
1) Simple D3 LP (yield + range + collateral semantics)#
User objective: supply concentrated liquidity and earn fees while participating in the broader collateralized system.
2) D4 debt-side LP#
User objective: open debt-side concentrated position, effectively borrowing into a range strategy.
3) Multi-step aggregator swap with callback integration#
Integrator objective: custom route with maximum control and minimal gas overhead.
4) Liquidator workflow#
Liquidator objective: repay unhealthy debt and seize collateral value with penalty constraints.
Fund flow map (token-level)#
This is the part auditors and integrators should obsess over.
A) D3 deposit fund flow#
B) D3 withdraw flow#
C) D4 borrow flow#
D) D4 payback flow#
E) Fee collection flow (D3/D4)#
F) Liquidity callback trust boundary#
This edge is a prime audit boundary: callback data provenance and caller authenticity assumptions must stay tight.
Core invariants worth formalizing#
If you only verify a handful of properties, verify these:
-
Operation closure invariant Every successful
startOperationexits with all pending supply/borrow counts equal to zero. -
No free-credit invariant User cannot increase claimable assets or reduce debt without equivalent cost through
settlepaths. -
D3 accounting shape invariant D3 actions should not accidentally create net positive pending borrow without explicit D4 context.
-
D4 debt-fee invariant Fee-induced pending supply/borrow netting cannot let users erase debt or over-collect fees.
-
HF safety invariant Operations that worsen risk must keep post-check health constraints satisfied in required branches.
-
Callback authorization invariant Only trusted originators can trigger privileged callback paths (
startOperationCallback,liquidityCallback). -
Rounding boundedness invariant BigMath/rounding drift must remain within expected conservative windows and never accumulate into insolvency.
Threat model#
Assets to protect#
- pooled assets in Liquidity
- collateral value in MM positions
- debt accounting correctness
- fee accounting correctness
Trust boundaries#
- governance/auth roles (trusted per design assumptions)
- Liquidity contract correctness (critical shared dependency)
- oracle correctness for HF/liquidation
- callback counterparties (integrators/custom contracts)
Attacker profiles#
- malicious integrator contract using callback path
- MEV/searcher exploiting edge rounding/ordering
- sophisticated user crafting multi-step netting sequences
- grief attacker trying to stall or trap flows
Potential vulnerability classes (research candidates)#
I am intentionally framing these as hunt targets, not confirmed vulnerabilities.
1) Pending transfer desynchronization across rare branches#
settle has many branch combinations (skip LL, call LL, LL-failed fallback, net-in/net-out, store path). Bugs here can create medium/high issues if pending deltas or unaccounted values diverge.
2) D4 fee/debt netting inconsistencies#
D4 uses combined pending borrow and pending supply adjustments for fee handling. Any sign inversion or asymmetry over long sequences can create hidden debt forgiveness or fee inflation.
3) Callback-induced cross-function reentrancy assumptions#
Reentrancy lock is applied on settle, but operation envelope + callbacks + module calls are still complex. Cross-function reentry that reorders pending updates is a classic candidate.
4) Rounding-driven value leakage under stress#
BigMath + explicit conservative rounding is intentional, but drift can still produce edge-case user-level unfairness, stuck dust, or reconciliation anomalies that become material at scale.
5) Liquidation edge constraints and minimums#
Some checks are intentionally relaxed in withdraw/payback paths to avoid stuck liquidations. This is operationally pragmatic, but always expands the state space for weird edge behavior.
6) Liquidity split and rebalancing dependence#
If liquidity is split between DEX side and Liquidity side, flows can fail despite aggregate liquidity. This is a known design risk, but still a source of practical exploitability patterns (grief, timing, selective failure).
Auditor's test strategy for this architecture#
To reason about Fluid correctly, one-off unit tests are not enough. Use layered testing:
- Stateful fuzzing for random action sequences across D3/D4/MM.
- Invariants for pending-clearing, debt conservation, and HF bounds.
- Differential tests between path variants (skip-LL vs LL path).
- Adversarial callback harnesses for integration-style attacks.
- Formal checks for arithmetic/sign constraints on the hardest paths.
Observability and production monitoring#
If this were my production rollout checklist, I would track:
- pending supply/borrow counts at operation boundaries
- cumulative rounding residuals and reconciliation deltas
- fallback-to-stored-balance frequency in
settle - liquidation failure reasons and dust thresholds
- per-pool D4 fee-vs-debt deltas over time
How to read the two repos together#
The public repo (fluid-contracts-public) gives strong context on the Liquidity-first architecture, BigMath philosophy, and gas optimization patterns.
The contest repo (2026-01-fluid-dex-v2/fluid-contracts) extends this with:
- DEX v2 singleton and modular D3/D4 engines
- Money Market NFT-position orchestration around DEX v2 callbacks
- security-critical operation envelope and pending-transfer lifecycle
Deep call-trace playbooks#
Below are compact traces that map user intent to state transitions. These traces are useful when writing fuzz harnesses because they define concrete pre/post expectations.
Playbook A: D3 deposit with fee offset#
Intent: add D3 liquidity, pay net supply after subtracting accrued fees.
Key observation: the fee offset is applied before net settlement, so mismatch bugs usually appear as residual pending supply, not silent success.
Playbook B: D4 payback with conservative rounding#
Intent: reduce debt-side position while preserving protocol-favorable rounding.
Key observation: D4 fee semantics update both borrow and supply books; this is where sign bugs can become severe.
Playbook C: liquidation with D4 payback then D3 seize#
Intent: repay unhealthy debt and extract collateral value path-dependently.
Key observation: liquidation combines oracle prices, rounding, and multi-module state updates. Integration errors here are often medium/high because they directly move collateral.
Checklist-driven risk matrix (Solodit-style lens)#
Using a checklist mindset (attacker-first, external calls, math/rounding, token assumptions), the most relevant Fluid hotspots are:
| Checklist lens | Fluid hotspot | Why it matters |
|---|---|---|
| External call sequencing | settle callback path + LL callback path | Multiple external edges inside active operation envelope |
| Reentrancy model | settle lock vs operate openness | Cross-function ordering assumptions can break under adversarial callbacks |
| Accounting conservation | Pending supply/borrow + store fallback | Branch-specific bugs can leak or strand value |
| Precision/rounding | BigMath + explicit +/-1 rounding | Drift may be acceptable locally but harmful over long sequences |
| Liquidation correctness | MM liquidation + D3/D4 payback/withdraw | Value transfer under stress; easiest place for high impact |
| Access control boundaries | Whitelist/governance assumptions | Trusted roles are assumed; misconfiguration risk remains operationally significant |
What would actually qualify as Medium/High here?#
For this architecture, minor rounding weirdness is usually low/accepted. Medium/high outcomes generally need one of these:
-
Economic extraction at scale Example shape: a user can repeatedly cycle D4 swap/borrow/payback and end with less debt than paid value, or claim more withdrawable balance than supplied.
-
Collateral seizure mispricing Example shape: liquidation path over-seizes beyond intended penalty due to conversion/sign mismatch.
-
Permanent liveness/funds lock for normal users Example shape: state can enter a valid-onchain but non-recoverable condition where users cannot close/withdraw without trusted intervention.
-
Cross-user impact from malicious pool/token in supposedly isolated contexts Example shape: token/pool behavior can corrupt shared accounting and affect unrelated pools/users.
Formal verification targets (practical set)#
A useful formal set is not "prove everything"; it is "prove no catastrophic accounting divergence." Suggested properties:
- Operation closure: after any successful
startOperation, both pending counts are zero. - No-negative stored balances: user stored token amount never underflows in any settle branch.
- Sign consistency in D4: payback cannot increase net debt for same action tuple.
- Fee boundedness: collected fee never exceeds fee accrued for a position lifecycle segment.
- Liquidation monotonicity: liquidation cannot improve target position HF above allowed bound in paths that require post-check constraints.
Pseudo-spec sketch:
/// property: operation closure
assert(PendingSupplyCount() == 0 && PendingBorrowCount() == 0);
/// property: no free debt reduction
/// if userDebtAfter < userDebtBefore then netUserPaymentValue > 0
assert(!(debt_after < debt_before && net_payment_value == 0));
/// property: fee boundedness
assert(fee_collected_lifetime <= fee_accrued_lifetime + epsilon_rounding);
Integrator hardening checklist#
If you are integrating Fluid DEX v2 or MM from a router/aggregator contract:
- Validate callback caller strictly (
msg.sender == DexV2) in your callback entrypoints. - Use explicit slippage bounds on every hop and every settle-affecting action.
- Avoid reentrant external calls inside your callback body unless strictly required.
- Keep per-operation accounting in memory and reconcile against expected deltas before finishing.
- Treat native token handling and
0xEeee...conventions carefully to avoid stuck ETH edges. - Prefer conservative pathing when liquidity split is known to be volatile.
Final takeaways#
Fluid DEX v2 is not "just another concentrated liquidity DEX." It is an execution shell joining AMM mechanics with debt/collateral semantics through a shared settlement layer.
The most important things to internalize are:
- operation envelope correctness (
startOperationlifecycle) - pending transfer accounting discipline
- D4 fee/debt netting semantics
- Money Market callback and health-factor boundary checks
- rounding/precision trade-offs as first-class protocol behavior
If you understand these five, you can reason about most real risk in the system and avoid superficial audit conclusions.
Links#
- Fluid technical docs: https://docs.fluid.instadapp.io/
- DEX v2 integration doc: https://docs.fluid.instadapp.io/integrate/dex-v2-swaps.html
- DEX v2 announcement post: https://blog.instadapp.io/fluid-dex-v2/
- Public contracts repo: https://github.com/instadapp/fluid-contracts-public