diff --git a/RFPs/RFP-004-privacy-preserving-dex.md b/RFPs/RFP-004-privacy-preserving-dex.md index 1e8111e..0aebe99 100644 --- a/RFPs/RFP-004-privacy-preserving-dex.md +++ b/RFPs/RFP-004-privacy-preserving-dex.md @@ -4,59 +4,63 @@ title: Privacy-Preserving Decentralized Exchange (DEX) tier: XL funding: $XXXXX status: open +dependencies: + - id: LP-0013 + reason: Hard blocker. Token transfer-authority primitives are required for the DEX program to custody pool reserves, pay swap output, return LP deposits, and route trading fees. Currently open. + - id: LP-0015 + reason: General cross-program calls via tail calls, used to compose token transfers with reserve and state updates within a single atomic swap. Delivered (closed). + - id: LP-0014 + reason: Associated Token Accounts for user-facing token accounts (requirement F.8). Delivered (closed). + - id: LP-0012 + reason: Structured event emission, used by the pool analytics view and third-party indexers to observe pool state changes. Delivered (closed). category: Applications & Integrations --- - # RFP-004 — Privacy-Preserving Decentralized Exchange (DEX) ## 🧭 Overview -Build a decentralized exchange on LEZ with public AMM liquidity pools. -Users with public accounts interact with the DEX directly. Users with -private accounts interact via the deshield→swap→re-shield pattern: the -SDK deshields tokens to a fresh ephemeral public account, executes the -swap in a public pool, and re-shields the output back to the user's -private account. When interacting from a private account, the origin -and destination of funds are not traceable on-chain, protecting user -identity without requiring private pool state. - -A DEX is the most critical application for any new chain ecosystem. -On Ethereum, Uniswap has processed over $3.4 trillion in cumulative -volume and holds ~$6.8B in TVL; on Solana, Jupiter has routed over $1 -trillion in cumulative volume, and Solana DEXes processed $326B in Q3 -2025 alone. Trading is the primary activity that bootstraps economic -activity on any chain. - -On transparent chains, this trading comes with severe downsides: -front-running and sandwich attacks extract hundreds of millions in MEV -from ordinary users. On LEZ, when users interact from a private account, their identity is -never linked to a swap on-chain — observers see a trade from an -ephemeral public account with no prior history, making identity-based -front-running and wallet-profiling impossible. Sandwich attacks, -back-running, and CEX-DEX arbitrage remain possible as they depend on -trade size and pool state rather than user identity; mitigating these -is out of scope for this RFP. This is a meaningful privacy improvement -and a key differentiator for the Logos ecosystem. - -The team building this should have deep experience in AMM or order-book -design, SVM program development, and MEV-resistant trading mechanisms. +Build a decentralized exchange on LEZ with public AMM liquidity pools. Users +with public accounts interact with the DEX directly. Users with private accounts +interact via the deshield→swap→re-shield pattern: the SDK deshields tokens to a +fresh ephemeral public account, executes the swap in a public pool, and +re-shields the output back to the user's private account. When interacting from +a private account, the origin and destination of funds are not traceable +on-chain, protecting user identity without requiring private pool state. + +A DEX is the most critical application for any new chain ecosystem. On Ethereum, +Uniswap has processed over $3.4 trillion in cumulative volume and holds ~$6.8B +in TVL; on Solana, Jupiter has routed over $1 trillion in cumulative volume, and +Solana DEXes processed $326B in Q3 2025 alone. Trading is the primary activity +that bootstraps economic activity on any chain. + +On transparent chains, this trading comes with severe downsides: front-running +and sandwich attacks extract hundreds of millions in MEV from ordinary users. On +LEZ, when users interact from a private account, their identity is never linked +to a swap on-chain — observers see a trade from an ephemeral public account with +no prior history, making identity-based front-running and wallet-profiling +impossible. Sandwich attacks, back-running, and CEX-DEX arbitrage remain +possible as they depend on trade size and pool state rather than user identity; +mitigating these is out of scope for this RFP. This is a meaningful privacy +improvement and a key differentiator for the Logos ecosystem. + +The team building this should have deep experience in AMM or order-book design, +SVM program development, and MEV-resistant trading mechanisms. ## 🔥 Why This Matters -Without a DEX on LEZ, users who bridge assets and hold them in private -accounts have no way to trade without moving funds off-chain or to a -centralised exchange, breaking the privacy guarantees that private -accounts provide. A LEZ-native DEX is the missing link between bridging -assets in and participating in a private economy. +Without a DEX on LEZ, users who bridge assets and hold them in private accounts +have no way to trade without moving funds off-chain or to a centralised +exchange, breaking the privacy guarantees that private accounts provide. A +LEZ-native DEX is the missing link between bridging assets in and participating +in a private economy. -Privacy also enables structural fairness. On Ethereum, ~$290M was -extracted via sandwich attacks in 2025; on Solana, $370–500M over a -16-month period. While solutions like Flashbots (Ethereum) and Jito's -DontFront (Solana) mitigate MEV, they are afterthoughts bolted onto -transparent systems. On LEZ, MEV resistance is a first-class property -of the execution environment, making the DEX inherently fairer for all -participants. +Privacy also enables structural fairness. On Ethereum, ~$290M was extracted via +sandwich attacks in 2025; on Solana, $370–500M over a 16-month period. While +solutions like Flashbots (Ethereum) and Jito's DontFront (Solana) mitigate MEV, +they are afterthoughts bolted onto transparent systems. On LEZ, MEV resistance +is a first-class property of the execution environment, making the DEX +inherently fairer for all participants. ## ✅ Scope of Work @@ -64,166 +68,268 @@ participants. #### Functionality -1. Implement an automated market maker (AMM) program on LEZ with - public liquidity pools supporting the deshield→swap→re-shield - interaction pattern for privacy-preserving trading. +1. Implement an automated market maker (AMM) program on LEZ with public + liquidity pools supporting the deshield→swap→re-shield interaction pattern + for privacy-preserving trading. 2. Support creation of liquidity pools for arbitrary token pairs. -3. Liquidity providers can add and withdraw liquidity directly from a - public account, or via the deshield→interact→reshield pattern from - a private account. The LP position is public on-chain; when using a - private account, which private account originated or received the - funds is not traceable. +3. Liquidity providers can add and withdraw liquidity directly from a public + account, or via the deshield→interact→reshield pattern from a private + account. The LP position is public on-chain; when using a private account, + which private account originated or received the funds is not traceable. 4. Traders can swap tokens directly from a public account, or via the - deshield→swap→re-shield pattern from a private account. Trade size - and direction are visible on-chain; when using a private account, - which private account originated the funds or where they go after - re-shielding is not traceable. -5. Traders and LPs using public accounts can interact with the same - pools; their transactions are executed transparently on-chain - (standard public account behaviour). -6. The pool creator selects a fee tier at pool creation time (e.g., - 0.01%, 0.05%, 0.3%, 1%); the fee tier is immutable per pool. - Multiple pools for the same token pair with different fee tiers - can coexist. Trading fees are paid by the trader and distributed - to LPs. -7. Implement slippage protection with user-configurable tolerance and - minimum output guarantees. -8. Use Associated Token Accounts (ATAs) for all token interactions — - pool token accounts, LP token accounts, and trader token accounts - must use the deterministic ATA derivation per `(owner, mint)` pair - (see [LP-0014](https://github.com/logos-co/lambda-prize/blob/main/prizes/LP-0014.md)). + deshield→swap→re-shield pattern from a private account. Trade size and + direction are visible on-chain; when using a private account, which private + account originated the funds or where they go after re-shielding is not + traceable. +5. Traders and LPs using public accounts can interact with the same pools; their + transactions are executed transparently on-chain (standard public account + behaviour). +6. The pool creator selects a fee tier at pool creation time (e.g., 0.01%, + 0.05%, 0.3%, 1%); the fee tier is immutable per pool. Multiple pools for the + same token pair with different fee tiers can coexist. Trading fees are paid + by the trader and distributed to LPs. +7. Implement slippage protection with user-configurable tolerance and minimum + output guarantees. +8. The DEX program must be compatible with Associated Token Accounts (ATAs) for + user-facing token accounts: when a trader or LP supplies an ATA derived per + `(owner, mint)` pair (see + [LP-0014](https://github.com/logos-co/lambda-prize/blob/main/prizes/LP-0014.md)), + the program must accept it without requiring an alternative derivation. ATAs + must not be forced on users; the program must also accept any valid SPL token + account owned by the caller. Pool-side vault accounts may use program-derived + addresses (PDAs) rather than ATAs, matching Solana DEX practice. +9. Implement a permissionless `sync()` function that updates a pool's cached + reserves to match the actual vault token balances, absorbing any surplus from + unsolicited transfers into the pool for the benefit of LPs. See + [Appendix: DEX Ecosystem Behaviour, section 10](../appendix/dex-ecosystem-behaviour.md#10-reserve-reconciliation-sync-and-skim) + for rationale and ecosystem precedent. #### Usability -1. Provide an SDK that can be used to build Logos modules for - interacting with the DEX (swapping, pool creation, liquidity - management). When the user interacts from a private account, the - SDK must handle the atomic deshield — transferring both the swap - token and a small amount of native token for gas — as a single - indivisible action, preventing accidental privacy leaks from +1. Provide an SDK that can be used to build Logos modules for interacting with + the DEX (swapping, pool creation, liquidity management). When the user + interacts from a private account, the SDK must handle the atomic deshield — + transferring both the swap token and a small amount of native token for gas — + as a single indivisible action, preventing accidental privacy leaks from externally funding account A. -2. Provide a Logos mini-app GUI with local build instructions, - downloadable assets, and loadable in Logos app (Basecamp) via - git repo. -3. Provide a pool analytics view showing aggregate volume, TVL, and - fee revenue without revealing individual positions. -4. Documentation must clearly explain what information is public vs. - private for each action (trade size and pool used are visible - on-chain; the private account that originated or receives the funds - is not traceable). -5. Failed or rejected swaps must return clear, actionable error messages. -6. Provide an IDL for the DEX program, preferably using the +2. Provide a Logos mini-app GUI with local build instructions, downloadable + assets, and loadable in Logos app (Basecamp) via git repo. +3. Provide a CLI that covers core functionality of the program (pool creation, + swapping, LP management). The CLI may have fewer features than the GUI + mini-app but must support all essential operations. +4. Provide an IDL for the DEX program, preferably using the [SPEL framework](https://github.com/logos-co/spel). -7. Before each swap or liquidity operation, the mini-app must show the - estimated transaction fee. When the user interacts from a private - account, it must also confirm that the shielded balance covers both - the operation amount and fees within the single deshield action; a - clear, actionable error must be shown if the balance is insufficient - — preventing partial deshields that could leave funds stranded in - an ephemeral account. -8. The mini-app must display a swap preview before the user confirms: - estimated output amount, effective price, price impact, and fee - taken — so the user can evaluate the trade before confirming. +5. Provide a pool analytics view showing aggregate volume, TVL, and fee revenue + without revealing individual positions. +6. Documentation must clearly explain what information is public vs. private for + each action (trade size and pool used are visible on-chain; the private + account that originated or receives the funds is not traceable). +7. Failed or rejected swaps must return clear, actionable error messages. +8. Before each swap or liquidity operation, the mini-app must show the estimated + transaction fee. When the user interacts from a private account, it must also + confirm that the shielded balance covers both the operation amount and fees + within the single deshield action; a clear, actionable error must be shown if + the balance is insufficient (preventing partial deshields that could leave + funds stranded in an ephemeral account). +9. The mini-app must display a swap preview before the user confirms: estimated + output amount, effective price, price impact, and fee taken, so the user can + evaluate the trade before confirming. #### Reliability -1. Pool state must remain consistent under concurrent swap submissions; - no double-spend or incorrect pool balance. +1. Pool state must remain consistent under concurrent swap submissions; no + double-spend or incorrect pool balance. #### Performance -1. A swap against an existing pool completes within a single LEZ - transaction. -2. The transaction size of each operation (swap, add/remove - liquidity, pool creation) must be documented; LEZ's block size is - limited and this budget may change during testnet. -3. Pool creation and liquidity operations complete within a single - transaction each. +1. A swap against an existing pool completes within a single LEZ transaction. +2. Pool creation and liquidity operations complete within a single transaction + each. +3. Compute unit usage and transaction size of each operation (swap, add + liquidity, remove liquidity, pool creation) must be documented and + benchmarked against LEZ devnet limits; LEZ's per-transaction compute budget + and block size may change during testnet. #### Supportability 1. The DEX program is deployed and tested on LEZ devnet/testnet. -2. End-to-end integration tests run against a LEZ sequencer (standalone - mode) and are included in CI — CI must be green on the default - branch. -3. Every hard requirement in Functionality, Usability, Reliability, - and Performance has at least one corresponding test. -4. A README documents end-to-end usage: deployment steps, program - addresses, and step-by-step instructions for interacting with the - DEX via CLI and front-end (pool creation, swapping, LP management). -5. Submit a [doc packet](https://github.com/logos-co/logos-docs/issues/new?template=doc-packet.yml) - for the SDK, covering the developer integration journey for swapping, - pool creation, and liquidity management. -6. Provide Figma designs or equivalent for the mini-app GUI. +2. End-to-end integration tests run against a LEZ sequencer (standalone mode) + and are included in CI. +3. CI must be green on the default branch. +4. Every hard requirement in Functionality, Usability, Reliability, and + Performance has at least one corresponding test. +5. A README documents end-to-end usage: deployment steps, program addresses, and + step-by-step instructions for interacting with the DEX via CLI and front-end + (pool creation, swapping, LP management). +6. Submit a + [doc packet](https://github.com/logos-co/logos-docs/issues/new?template=doc-packet.yml) + for the SDK, covering the developer integration journey for pool creation, + swapping, and liquidity management. +7. Submit a + [doc packet](https://github.com/logos-co/logos-docs/issues/new?template=doc-packet.yml) + for the CLI, covering the core operator/user journey. +8. Provide Figma designs or equivalent for the mini-app GUI. #### + Privacy -1. The mini-app and SDK must support both direct public account - interaction and the deshield→swap→re-shield pattern for private - account interaction. When a user interacts from a private account, - the SDK must enforce the complete deshield→swap→re-shield pattern - — the re-shield step must not be skippable. -2. When interacting from a private account, the mini-app must display - a pre-confirmation summary for each operation that clearly - identifies what will be visible on-chain (trade size, direction, - pool address, ephemeral intermediary account) and what will remain - private (the originating private account, the destination of - re-shielded tokens, and any link between separate swaps by the - same user). -3. When interacting from a private account, the SDK must validate - that the target account for re-shielding swap output is a private - (shielded) account before submitting the transaction, and reject - the operation with an explicit error if it is not. -4. The ephemeral public account (account A) created during the - deshield step must never be reused across operations. Each swap - or liquidity operation from a private account must use a freshly - generated account with no prior on-chain history. +1. The mini-app and SDK must support both direct public account interaction and + the deshield→swap→re-shield pattern for private account interaction. When a + user interacts from a private account, the SDK must enforce the complete + deshield→swap→re-shield pattern — the re-shield step must not be skippable. +2. When interacting from a private account, the mini-app must display a + pre-confirmation summary for each operation that clearly identifies what will + be visible on-chain (trade size, direction, pool address, ephemeral + intermediary account) and what will remain private (the originating private + account, the destination of re-shielded tokens, and any link between separate + swaps by the same user). +3. When interacting from a private account, the SDK must validate that the + target account for re-shielding swap output is a private (shielded) account + before submitting the transaction, and reject the operation with an explicit + error if it is not. +4. The ephemeral public account (account A) created during the deshield step + must never be reused across operations. Each swap or liquidity operation from + a private account must use a freshly generated account with no prior on-chain + history. + +### Soft Requirements + +If possible. + +#### Functionality + +1. Support multi-hop routing across multiple pools within a single transaction + (e.g. flash-accounting style settlement of intermediate hops), reducing + slippage on token pairs without a direct pool. + +### Out of Scope + +The following are explicitly excluded from this RFP: + +- A `skim()` or `recoverSurplus()` instruction that extracts surplus tokens from + a pool's vault to a caller-specified address. Surplus reconciliation is + handled exclusively by the permissionless `sync()` function (Functionality + requirement F.9), which folds surplus into the pool to benefit LPs. Among + surveyed protocols, only Uniswap V2 exposes a `skim()`-style instruction; + Uniswap V4, Balancer V3, Curve StableSwapNG, Raydium, and Orca Whirlpools do + not. See + [Appendix: DEX Ecosystem Behaviour, section 10](../appendix/dex-ecosystem-behaviour.md#10-reserve-reconciliation-sync-and-skim). ### Privacy Architecture -All DEX liquidity pools are public on-chain state. User privacy is -enforced at the UX layer for private account users. The mini-app and -SDK support both direct public account interaction and private account -interaction via the deshield→swap→re-shield pattern. When a user -interacts from a private account, the SDK must enforce the complete -pattern as described below. +All DEX liquidity pools are public on-chain state. User privacy is enforced at +the UX layer for private account users. The mini-app and SDK support both direct +public account interaction and private account interaction via the +deshield→swap→re-shield pattern. When a user interacts from a private account, +the SDK must enforce the complete pattern as described below. #### Interaction flow -For every protocol operation initiated from a private account (swap, add/remove liquidity): +For every protocol operation initiated from a private account (swap, add/remove +liquidity): -1. The user initiates the action from their private account. The SDK - deshields to a **fresh, single-use** public account (account A) - with no prior on-chain history. The deshield atomically transfers - both the operation token **and** enough native token for gas in a - single indivisible action. +1. The user initiates the action from their private account. The SDK deshields + to a **fresh, single-use** public account (account A) with no prior on-chain + history. The deshield atomically transfers both the operation token **and** + enough native token for gas in a single indivisible action. 2. Account A executes the operation in a public pool. -3. Account A shields any outputs (swap proceeds, withdrawn liquidity) - back to the user's private account. Account A is never reused. +3. Account A shields any outputs (swap proceeds, withdrawn liquidity) back to + the user's private account. Account A is never reused. -> **Gas:** Both the operation token and gas must come exclusively from -> the deshield in step 1. Funding account A from any external source -> — such as a CEX withdrawal or a known wallet — creates an on-chain -> link to an existing identity and breaks the privacy guarantee. The -> SDK must make this impossible; the atomic deshield is a single, -> indivisible user action. +> **Gas:** Both the operation token and gas must come exclusively from the +> deshield in step 1. Funding account A from any external source — such as a CEX +> withdrawal or a known wallet — creates an on-chain link to an existing +> identity and breaks the privacy guarantee. The SDK must make this impossible; +> the atomic deshield is a single, indivisible user action. #### What is public (observable on-chain) -- All pool state: token pair, fee tier, total TVL, cumulative volume, - current price. +- All pool state: token pair, fee tier, total TVL, cumulative volume, current + price. - All swap and liquidity transactions: trade size, direction, and the - originating account address (the ephemeral intermediary account for - private account interactions, the user's public account otherwise). + originating account address (the ephemeral intermediary account for private + account interactions, the user's public account otherwise). - LP position sizes and fee earnings. #### What is private - Which private account originated the funds for a swap or LP deposit. - Where output tokens go after re-shielding. -- Any link between multiple operations by the same user (no on-chain - linkability across ephemeral accounts). +- Any link between multiple operations by the same user (no on-chain linkability + across ephemeral accounts). + +## ⚠ Platform Dependencies + +This RFP is open for proposals. Proposers may begin design and development work, +but a working on-chain deployment depends on the primitives below. The privacy +primitives are core LEE features; the token-program and runtime primitives are +tracked as lambda prizes. + +### Hard blockers + +These must be available on LEZ before the DEX can hold liquidity and settle +swaps on-chain. + +#### Token authorities (LP-0013) + +The DEX program is a token custodian: it holds pool reserves for each token +pair, pays swap output to traders, returns deposits to LPs on withdrawal, and +routes trading fees to LPs. This requires the transfer-authority primitives in +[LP-0013](https://github.com/logos-co/lambda-prize/blob/master/prizes/LP-0013.md), +currently **open**. + +### Resolved dependencies +These primitives were once blockers but are now delivered on LEZ, so they no +longer gate this RFP. They remain in the dependencies index for reference. + +#### General cross-program calls (LP-0015) + +A swap must transfer the input token into the pool vault, compute the output +using the constant-product formula, transfer the output token to the trader, and +update cached reserves, all within one atomic transaction. General cross-program +calls via tail calls let the operation continue into a protected continuation +after each token transfer. +[LP-0015](https://github.com/logos-co/lambda-prize/blob/master/prizes/LP-0015.md) +is **closed**, delivered by the LEZ team as part of the core runtime. + +#### Associated Token Accounts (LP-0014) + +User-facing token accounts use the deterministic ATA derivation per +`(owner, mint)` pair (Functionality requirement F.8). +[LP-0014](https://github.com/logos-co/lambda-prize/blob/master/prizes/LP-0014.md) +is **closed**. + +#### Event emission (LP-0012) + +The pool analytics view (Usability requirement) and any third-party indexer +observe pool state changes (swaps, liquidity added or removed) through +structured events rather than polling every account. +[LP-0012](https://github.com/logos-co/lambda-prize/blob/master/prizes/LP-0012.md) +is **closed**. + +### Privacy primitives + +The deshield→swap→re-shield pattern relies on LEE private accounts and the +atomic deshield action described in the Scope of Work. These are core LEE +features rather than lambda prizes; proposers should confirm the current state +of private-account support on LEZ devnet against the Resources below before +relying on it. + +### Risks + +#### Compute budget + +LEZ currently processes one private transaction per block (as of 2026-04). A +swap that deshields, transfers into the pool vault, computes output, transfers +output, re-shields, and updates reserves is compute-intensive. On Solana, where +the per-instruction default is 200,000 compute units and the per-transaction +maximum is 1.4M compute units (see +[Solana compute budget](https://solana.com/docs/core/fees/compute-budget)), a +multi-step settlement consumes hundreds of thousands of compute units; the exact +figure for LEZ depends on the implementation. If LEZ's per-transaction compute +budget is lower, multi-hop routing (the soft Functionality requirement) may not +fit in a single transaction. Performance requirement P.3 requires benchmarking +each operation against LEZ devnet limits. ## 👤 Recommended Team Profile @@ -241,12 +347,10 @@ Team experienced with: Estimated duration: **14 weeks** (Uniswap V2 equivalent with deshield→swap→re-shield support). - ## 🌍 Open Source Requirement All code must be released under the **MIT+Apache2.0 dual License**. - ## Resources - [Logos Documentation](https://github.com/logos-co/logos-docs) @@ -257,5 +361,5 @@ All code must be released under the **MIT+Apache2.0 dual License**. **[Submit Proposal](https://github.com/logos-co/rfp/issues/new?template=proposal.yml)** -We typically respond within **14 days**. For clarification questions, -please use **Discussions**. +We typically respond within **14 days**. For clarification questions, please use +**Discussions**. diff --git a/appendix/dex-ecosystem-behaviour.md b/appendix/dex-ecosystem-behaviour.md new file mode 100644 index 0000000..585d3dd --- /dev/null +++ b/appendix/dex-ecosystem-behaviour.md @@ -0,0 +1,336 @@ +# Appendix: DEX Ecosystem Behaviour + +This appendix surveys how existing decentralised exchanges implement behaviours +required by [RFP-004](../RFPs/RFP-004-privacy-preserving-dex.md). Each section +maps an RFP requirement to the equivalent mechanism in production protocols, +showing that the requirement reflects established industry practice. All data is +sourced from the [research-dex](https://github.com/marclawclaw/research-dex) +vault; individual project notes contain full citations. + +## Protocols considered + +TVL figures are DeFiLlama snapshots as of 2026-04-27. Volume figures cite each +protocol's own dashboards or DeFiLlama where available. + +| Protocol | Ecosystem | Type | TVL | Cumulative volume | +| ---------------------------------------- | ---------------------- | -------------------------- | ------------------------------- | -------------------------------- | +| Uniswap V2 | Ethereum (multi-chain) | Constant-product AMM | ~$970M | $604B+ (V2-only; DeFiLlama) | +| Uniswap V4 | Ethereum (~12 chains) | Singleton AMM with hooks | ~$720M | >$190B (2025) | +| Balancer V3 | Ethereum (9 chains) | Vault-based AMM with hooks | ~$80M (post 2025-11 V2 exploit) | N/A (fee data only) | +| Curve Finance (StableSwapNG and earlier) | Ethereum (multi-chain) | StableSwap AMM | ~$1.7B | ~$126B (2025) | +| CoW Protocol | Ethereum (~9 chains) | Intent-based batch auction | N/A (no custody) | $87B (2025) | +| Raydium | Solana | AMM + CLMM | ~$1.0B | $695.7B all-time | +| Orca Whirlpools | Solana (+ Eclipse) | CLMM | ~$255M | $36B+ all-time (Orca dashboards) | + +## 1. Constant-product AMM with public pool state + +**RFP-004 requirement:** Implement an AMM program with public liquidity pools +(requirement F.1). + +**Ecosystem practice:** The constant-product invariant (`x * y = k`) is the most +widely deployed AMM mechanism. Uniswap V2 is the reference implementation, with +major forks including PancakeSwap, SushiSwap, and Aerodrome (DeFiLlama tracks +dozens of V2-derived deployments). Pool state (reserves, price, cumulative +volume) is fully public on-chain in every surveyed protocol. No production AMM +stores pool state privately. + +On Solana, both Raydium and Orca implement the same constant-product invariant +for their base pools, adapted for the SVM account model: token balances reside +in SPL Token Accounts whose authority is a program-derived address (PDA), and +pool state is stored in a separate data account. + +RFP-004's specification of a public-state AMM with the deshield/swap/re-shield +privacy layer applied at the UX level is consistent with the universal industry +pattern: pool state is public; privacy, where it exists, is enforced outside the +pool contract. + +## 2. Immutable fee tiers with multiple pools per pair + +**RFP-004 requirement:** The pool creator selects a fee tier at creation (e.g. +0.01%, 0.05%, 0.3%, 1%); the tier is immutable. Multiple pools for the same pair +with different tiers can coexist (requirement F.6). + +**Ecosystem practice:** + +| Protocol | Fee tier model | Immutable | Multiple pools per pair | +| ------------------ | ------------------------------------------------------------------- | --------------------------------------------- | ----------------------- | +| Uniswap V2 | Fixed 0.3% (all pools) | Yes | No (one pool per pair) | +| Uniswap V4 | Configurable per pool; hooks can add dynamic fees | Yes (base tier) | Yes | +| Balancer V3 | Configurable per pool; StableSurge hook adds dynamic fee | Yes (base tier) | Yes | +| Curve StableSwapNG | Configurable per pool; `offpeg_fee_multiplier` adds dynamic scaling | Yes (base tier) | Yes | +| Raydium CLMM | 8 fee tiers (0.01% to 2%) | Yes | Yes | +| Orca Whirlpools | 9 tick spacings mapping to fee tiers (0.01% to 2%) | Yes (adaptive pools add a variable component) | Yes | + +Immutable base fee tiers are the norm. Every protocol launched after Uniswap V2 +supports multiple pools per pair with different fee tiers. The RFP-004 fee model +(0.01%, 0.05%, 0.3%, 1%) mirrors the standard Uniswap V3/V4 tier set, which is +also the set used by Raydium and Orca (with additional tiers at 0.02% and 2%). + +## 3. Trading fees paid by trader, distributed to LPs + +**RFP-004 requirement:** Trading fees are paid by the trader and distributed to +LPs (requirement F.6). + +**Ecosystem practice:** + +| Protocol | Fee payer | LP share | Protocol share | Other | +| ------------------ | --------------------- | ------------------------ | ---------------------------------- | --------------------------------------------------------------------------------- | +| Uniswap V2 | Trader (input token) | 0.25% (post-UNIfication) | 0.05% (1/6 of 0.3%) | UNIfication proposal passed 2025-12-25; pre-activation LPs received the full 0.3% | +| Uniswap V4 | Trader (input token) | Majority | Configurable per pool | Hook can adjust | +| Balancer V3 | Trader (input token) | ~50% (varies) | ~50% (split with pool creator) | Yield fee: 10% on boosted pools | +| Curve StableSwapNG | Trader (output token) | Majority | Admin fee (fraction of swap fee) | Dynamic fee on imbalanced pools | +| Raydium CLMM | Trader | 84% | 12% RAY buyback + 4% treasury | | +| Orca Whirlpools | Trader | 87% | 12% DAO treasury + 1% Climate Fund | | + +In every surveyed protocol, the trader pays fees as part of the swap. Fees +accrue to LPs implicitly by growing the pool invariant (Uniswap V2/V4), or via +fee accumulator tracking (CLMM protocols, Curve). + +The RFP-004 model (trader pays, LPs receive) is universal. + +## 4. Slippage protection with minimum output guarantees + +**RFP-004 requirement:** Implement slippage protection with user-configurable +tolerance and minimum output guarantees (requirement F.7). + +**Ecosystem practice:** Every surveyed protocol enforces slippage protection via +a `minimum_amount_out` (or equivalent) parameter that reverts the transaction if +the output falls below the user's threshold: + +| Protocol | Parameter | Enforcement | +| ------------------ | --------------------------------------- | ------------------------------------- | +| Uniswap V2 | `amountOutMin` on Router | Router reverts if output < minimum | +| Uniswap V4 | `amountSpecified` + `sqrtPriceLimitX96` | PoolManager reverts on limit breach | +| Balancer V3 | `limit` in swap params | Vault reverts if output < limit | +| Curve StableSwapNG | `_min_dy` on `exchange()` | Pool reverts if output < minimum | +| Raydium | `minimum_amount_out` | Program reverts if output < minimum | +| Orca Whirlpools | `other_amount_threshold` | Program reverts if output < threshold | + +This is a universal safety mechanism. No production AMM omits it. + +## 5. Pool creation for arbitrary token pairs + +**RFP-004 requirement:** Support creation of liquidity pools for arbitrary token +pairs (requirement F.2). + +**Ecosystem practice:** Permissionless pool creation is the default: + +| Protocol | Permissionless | Factory pattern | +| ------------------ | -------------- | ------------------------------------------------------------- | +| Uniswap V2 | Yes | `createPair()` on Factory contract | +| Uniswap V4 | Yes | `initialize()` on PoolManager | +| Balancer V3 | Yes | Pool registration on Vault | +| Curve StableSwapNG | Yes | `CurveStableSwapFactoryNG.vy` using `create_from_blueprint()` | +| Raydium | Yes | `initialize()` instruction per pool type | +| Orca Whirlpools | Yes | `initialize_pool()` instruction | + +Every protocol uses a factory or registry pattern where any user can create a +pool for any token pair. Curve restricts some parameters (asset type +classification) but pool creation itself is open. + +## 6. Single-transaction operations + +**RFP-004 requirement:** A swap, pool creation, and liquidity operations each +complete within a single LEZ transaction (requirements P.1, P.3). + +**Ecosystem practice:** Every surveyed protocol executes swaps, pool creation, +and liquidity add/remove as atomic single-transaction operations. On Ethereum, +this is a single EVM transaction. On Solana, this is a single Solana transaction +(which may contain multiple instructions). + +Uniswap V4 and Balancer V3 go further: multi-hop swaps across multiple pools +settle in a single transaction with only two token transfers (input and output) +regardless of the number of intermediate pools, using flash accounting. On +Solana, Raydium and Orca achieve multi-hop routing through Jupiter aggregation +within a single transaction. + +The RFP-004 single-transaction requirement is consistent with every production +protocol. + +## 7. Pool analytics: aggregate volume, TVL, and fee revenue + +**RFP-004 requirement:** Provide a pool analytics view showing aggregate volume, +TVL, and fee revenue without revealing individual positions (requirement U.3). + +**Ecosystem practice:** All surveyed protocols expose pool-level aggregate +metrics on-chain or via indexers: + +| Metric | On-chain | Typical source | +| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| TVL | Derived from pool token balances (public) | DeFiLlama, protocol subgraphs | +| Volume | Cumulative swap counters stored on-chain (Uniswap V2: `kLast`, TWAP accumulators; Raydium CLMM: `swap_in_amount` / `swap_out_amount` fields) | Subgraph indexing of Swap events | +| Fee revenue | Derived from volume x fee rate, or accumulated in on-chain fee counters | Protocol dashboards | + +Individual LP positions are public on-chain in every protocol (LP token balances +in Uniswap V2 and Curve, NFT positions in CLMMs). RFP-004's privacy model does +not change this: LP positions remain public, but the private account that +originated the funds is not traceable when the deshield pattern is used. + +## 8. Token account derivation on Solana DEXes + +**Ecosystem practice on Solana:** Solana DEX programs split token account +handling between pool-side and user-side: + +- **Pool-side vaults are PDA-derived, not ATAs.** Both Raydium and Orca use + deterministic program-derived addresses for pool token accounts. Raydium CLMM + uses the seed `["pool_vault", pool_state, token_mint]`; Orca Whirlpools uses + similar per-pool, per-mint derivation. Pool vaults are not ATAs because the + pool program (not a wallet) is the owning authority. +- **User-side accounts are conventionally ATAs but not required by the + program.** Raydium and Orca instructions accept any valid SPL token account + owned by the caller, validating ownership and mint rather than derivation. + Frontends and SDKs default to ATAs and create them on-demand via the SPL + Associated Token Account program, so users encounter ATAs as the path of least + resistance, but non-ATA token accounts (e.g. delegated accounts, Token-2022 + accounts with extensions) can also be used. + +The SPL Associated Token Account program itself derives one token account per +`(wallet, mint)` pair, providing a predictable address that any sender can +compute without coordination. + +## 9. Vault accounting and balance reconciliation + +**RFP-004 context:** The DEX program must maintain correct pool balances under +concurrent swaps (requirement R.1). + +Three distinct vault accounting models exist in production: + +### Cached reserves with sync/skim (Uniswap V2) + +The pool caches token reserves in storage (`reserve0`, `reserve1`) and updates +them after each operation. Actual ERC-20 balances can drift from cached reserves +through direct transfers, rebasing tokens, or fee-on-transfer mechanics. Two +functions handle reconciliation: + +- **`sync()`** updates cached reserves to match live balances (absorbs a deficit + from a negative rebase; LPs bear the loss). +- **`skim(to)`** transfers the surplus (balance minus reserve) to a caller + (extracts orphaned tokens from a positive rebase; anyone can call it). + +This model is simple and self-healing but leaks positive rebase value to MEV +bots (the `skim()` surplus is publicly extractable). + +### Live balance reads with fee isolation (Curve StableSwapNG) + +The pool reads live token balances via `_balance()` on every operation and +stores admin fees in a separate `admin_balances[]` array. There is no cached +reserve to drift. Positive rebases accrue to LPs (not skimmable by bots); +negative rebases reduce LP value automatically. Admin fees are isolated from LP +balances, so neither rebases nor direct transfers corrupt fee accounting. + +### Flash accounting / transient accounting (Uniswap V4, Balancer V3) + +A singleton contract holds all pool tokens. Operations accumulate credits and +debits in EIP-1153 transient storage (TSTORE/TLOAD). Only the net token amounts +are transferred at the end of the session. There is no cached reserve separate +from the live balance. This model eliminates the reserve-reconciliation pattern: +TSTORE costs 100 gas vs SSTORE's 20,000 gas for a cold write, so per-operation +state tracking is roughly two orders of magnitude cheaper. + +### Solana/SVM account model (Raydium, Orca) + +Token balances reside in SPL Token Accounts whose authority is a pool PDA. The +pool program can only move tokens *out* of the vault by signing with the PDA via +`invoke_signed`. However, any external account can transfer tokens *into* a +vault account: the SPL Token `transfer` instruction only requires the sender's +signature, not the recipient's. This means surplus can accumulate in vault +accounts through unsolicited transfers, just as on Ethereum. + +Neither Raydium nor Orca implements a `sync()` or `skim()` equivalent. Both +protocols update pool state atomically within each swap or liquidity instruction +and do not read live vault balances for reserve reconciliation. In practice, +unsolicited transfers to pool vaults are rare on Solana (there are no rebasing +tokens, no fee-on-transfer mechanics, and no interest accrual), so the surplus +problem has not been operationally significant. The surplus simply sits in the +vault, unacknowledged by the pool's reserve accounting. + +### Summary + +| Model | Surplus possible | Recovery mechanism | Rebase handling | +| ---------------------------------- | -------------------------------- | ------------------- | ------------------------------------------ | +| Cached reserves (V2) | Yes (direct transfer, rebase) | `sync()` / `skim()` | Partial | +| Live balance reads (Curve) | No (reads live balance) | Not needed | Native | +| Flash accounting (V4, Balancer V3) | No (singleton, transient deltas) | Not needed | Via hooks | +| SVM accounts (Raydium, Orca) | Yes (unsolicited transfer) | None implemented | Not applicable (no rebasing tokens on SVM) | + +## 10. Reserve reconciliation: `sync()` and `skim()` + +In a cached-reserve AMM, the pool stores token reserves in its own state and +updates them after each operation. The live token balance (actual tokens held by +the pool's vault) can diverge from the cached reserves when value enters or +leaves the vault outside the normal swap or liquidity flow. Different protocols +handle this divergence differently. + +### How divergence arises + +On Ethereum, several mechanisms can cause vault balances to drift from cached +reserves: + +- **Direct transfers:** anyone can `transfer()` ERC-20 tokens to a pool's + address without calling the pool contract. +- **Rebasing tokens:** elastic-supply tokens (e.g. AMPL, stETH before wrapper) + periodically adjust holder balances; positive rebases create surplus, negative + rebases create deficits. +- **Fee-on-transfer tokens:** the amount received differs from the amount sent, + so post-transfer balances do not match the swap arithmetic. +- **Interest-bearing tokens:** balances grow over time without explicit + transfers. + +On SVM, the surface is narrower. There are no rebasing tokens, no +fee-on-transfer mechanics, and no interest-bearing balance growth. The only +divergence channel is an explicit SPL `transfer` instruction sending tokens to +the pool's vault account outside the normal flow, because the SPL Token program +permits transfers without the recipient's signature. + +### `sync()` and `skim()` in Uniswap V2 + +Uniswap V2 exposes two permissionless functions to reconcile cached reserves +with vault balances: + +- **`sync()`** writes the cached reserves to match the live balances. Surplus is + absorbed into the pool and accrues to LPs proportionally. Deficit (from + negative rebase) is written down so swaps continue to function rather than + reverting. +- **`skim(to)`** transfers the difference between the live balance and the + cached reserve to a caller-specified address, leaving the cached reserves + unchanged. This extracts surplus without folding it into LP value. + +Both functions are permissionless. Their interaction creates a race: any pending +`skim()` can be neutralised by a `sync()` call, since `sync()` consumes the +surplus into the pool. On Ethereum, MEV bots monitor pools for skimmable amounts +and routinely capture rebase surplus before LPs benefit. + +### Ecosystem precedent + +| Protocol | sync() | skim() / recoverSurplus() | Notes | +| ------------------ | ----------------------------------------------------------------------------------------------------- | ------------------------- | ----------------------------------------------------------------------- | +| Uniswap V2 | Yes (permissionless reserve sync) | Yes (permissionless) | Handles rebasing tokens, direct transfers, and negative-rebase deficits | +| Uniswap V4 | `PoolManager.sync()` exists but checkpoints balances for flash accounting, not reserve reconciliation | No | Flash accounting eliminates cached reserve drift | +| Balancer V3 | No | No | Transient accounting; no cached reserves | +| Curve StableSwapNG | No (reads live balances) | No | Admin fees isolated in a separate array | +| Raydium | No | No | Surplus silently accumulates in vault accounts | +| Orca Whirlpools | No | No | Same as Raydium | + +Uniswap V2 is the only surveyed protocol that implements both functions for +reserve reconciliation. Later AMMs eliminated the need for them by switching to +live balance reads (Curve) or transient accounting (Uniswap V4, Balancer V3). +Solana DEXes implement neither: surplus tokens in Raydium and Orca vaults remain +unacknowledged by pool accounting and are not recoverable through any +program-exposed instruction. + +## References + +Full research notes with per-claim source URLs are available in the +[research-dex](https://github.com/marclawclaw/research-dex) Obsidian vault. Key +sources by section: + +1. Uniswap V2 whitepaper: https://app.uniswap.org/whitepaper.pdf +2. Uniswap V4 whitepaper: https://app.uniswap.org/whitepaper-v4.pdf +3. Balancer V3 launch: + https://medium.com/balancer-protocol/balancer-v3-is-live-2e5e8462aa4c +4. Curve StableSwapNG docs: + https://docs.curve.finance/stableswap-exchange/stableswap-ng/pools/overview/ +5. Raydium CLMM source: https://github.com/raydium-io/raydium-clmm +6. Orca Whirlpools source: https://github.com/orca-so/whirlpools +7. Orca developer docs: https://dev.orca.so +8. DeFiLlama: https://defillama.com