From 4ba44239a95f910ce46286dd5bee571f74d1f9f8 Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Fri, 10 Apr 2026 22:03:58 +1000 Subject: [PATCH 01/10] appendix: DEX ecosystem behaviour and sync/recoverSurplus recommendation Add appendix documenting how existing DEX protocols implement behaviours required by RFP-004: constant-product AMM, immutable fee tiers, slippage protection, fee distribution, pool creation, single transaction operations, pool analytics, ATA usage, and vault accounting patterns. Recommend implementing sync() (permissionless) and recoverSurplus() (restricted to zero-liquidity pools) based on ecosystem analysis. Add requirement F.9 to RFP-004 referencing the appendix. Research source: github.com/marclawclaw/research-dex Co-Authored-By: Claude Opus 4.6 --- RFPs/RFP-004-privacy-preserving-dex.md | 8 + appendix/dex-ecosystem-behaviour.md | 401 +++++++++++++++++++++++++ 2 files changed, 409 insertions(+) create mode 100644 appendix/dex-ecosystem-behaviour.md diff --git a/RFPs/RFP-004-privacy-preserving-dex.md b/RFPs/RFP-004-privacy-preserving-dex.md index 1e8111e..98d5697 100644 --- a/RFPs/RFP-004-privacy-preserving-dex.md +++ b/RFPs/RFP-004-privacy-preserving-dex.md @@ -92,6 +92,14 @@ participants. 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)). +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. Implement a `recoverSurplus(to)` function, callable only + when the pool has zero liquidity (total LP supply is zero), that + transfers surplus tokens from the vault to a specified account. See + [Appendix: DEX Ecosystem Behaviour, section 10](../appendix/dex-ecosystem-behaviour.md#10-recommendation-sync-and-recoversurplus-functions) + for rationale and ecosystem precedent. #### Usability diff --git a/appendix/dex-ecosystem-behaviour.md b/appendix/dex-ecosystem-behaviour.md new file mode 100644 index 0000000..d241275 --- /dev/null +++ b/appendix/dex-ecosystem-behaviour.md @@ -0,0 +1,401 @@ +# 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 + +| Protocol | Ecosystem | Type | TVL | Cumulative volume | +|----------|-----------|------|-----|-------------------| +| Uniswap V2 | Ethereum | Constant-product AMM | ~$945M | $604B+ | +| Uniswap V4 | Ethereum (17 chains) | Singleton AMM with hooks | >$1B | >$190B (2025) | +| Balancer V3 | Ethereum (9 chains) | Vault-based AMM with hooks | $104M to $151M | N/A (fee data only) | +| Curve StableSwapNG | Ethereum | StableSwap AMM | ~$2.3B | ~$126B (2025) | +| CoW Protocol | Ethereum (10 chains) | Intent-based batch auction | N/A (no custody) | $87B (2025) | +| Raydium | Solana | AMM + CLMM | $1.4B | $695.7B all-time | +| Orca Whirlpools | Solana (+ Eclipse) | CLMM | $246.8M | >$300B | + +## 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 over 100 forks (PancakeSwap, SushiSwap, Aerodrome, +and others). 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 | 6 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) | 100% (protocol fee optional: 0.05%) | 0% (or 1/6 of 0.3% when active) | Protocol fee activated Dec 2025 | +| 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, NFT positions in CLMMs, `admin_balances` in +Curve). 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. Associated Token Accounts for token custody + +**RFP-004 requirement:** Use Associated Token Accounts (ATAs) for all +token interactions, derived per `(owner, mint)` pair (requirement F.8). + +**Ecosystem practice on Solana:** Both Raydium and Orca use +deterministic PDA-derived token accounts for pool vaults: + +- **Raydium CLMM:** Vault seed `["pool_vault", pool_state, token_mint]` +- **Orca Whirlpools:** Similar PDA derivation per pool per token mint + +User-side, the SPL Associated Token Account program derives one token +account per `(wallet, mint)` pair, and Solana DEX frontends universally +create ATAs on-demand. + +On LEZ (which uses SVM), the ATA requirement aligns with standard +Solana practice. + +## 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. 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 sync/skim pattern entirely and achieves a 98% gas reduction for +state tracking versus persistent storage. + +### 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. Recommendation: `sync()` and `recoverSurplus()` functions + +**Context:** The LEZ DEX implementation uses cached reserves (the +Uniswap V2 pattern). Because anyone can transfer SPL tokens into a +pool's vault account without invoking the pool program, the actual +vault balance can exceed the pool's recorded reserves. Two functions +handle this discrepancy: + +- **`sync()`** updates the cached reserves to match the live vault + balance, absorbing the surplus into the pool. The surplus becomes + part of the pool's reserves, benefiting all current LPs + proportionally. +- **`recoverSurplus(to)`** (Uniswap V2's `skim()`) transfers the + difference between the vault balance and the cached reserve to a + specified address. The reserves remain unchanged; only the excess + leaves. + +### How surplus arises on LEZ + +On SVM, surplus can only arise through one channel: an external account +explicitly transfers tokens into the pool's vault account outside the +normal swap or liquidity flow. Unlike Ethereum, there are no rebasing +tokens, no fee-on-transfer mechanics, and no interest-bearing token +accrual on SVM. The surplus problem on LEZ is therefore narrower than +on Ethereum: it is limited to unsolicited direct transfers, whether +accidental or intentional. + +### Why sync() is needed + +Even though surplus arises less frequently on SVM than on Ethereum, +`sync()` serves an important role: it reconciles the pool's internal +accounting with the actual vault balance. Without it, surplus tokens +sit in the vault permanently, unacknowledged by the pool's reserve +state. `sync()` lets anyone absorb the surplus into the pool, where it +benefits LPs. + +On Ethereum, `sync()` also handles the opposite case: if a negative +rebase reduces the vault balance below the cached reserve, `sync()` +writes down the reserve so that swaps do not revert. On SVM, this +scenario cannot occur (token balances only change through explicit +instructions), so `sync()` on LEZ is strictly a surplus-absorption +function. + +### The case for recoverSurplus() + +The implementor identifies one scenario where `recoverSurplus()` is +genuinely useful: all LPs remove their liquidity, but surplus tokens +remain in the vault. In this state: + +- Calling `sync()` would set the reserves to the vault balance, but + with zero LP tokens outstanding, no one holds a claim on those + reserves. +- The next user to `sync()` and then add liquidity would effectively + absorb the surplus into their position. +- `recoverSurplus()` provides a direct path to extract the tokens + without requiring someone to enter the pool first. + +### The case against recoverSurplus() + +In Uniswap V2, `skim()` is permissionless: any caller can extract the +surplus at any time. This creates a race condition: + +- On Ethereum, positive rebases generate surplus continuously, and MEV + bots monitor all pools for skimmable amounts. The economic value of + rebases is captured by bots, not LPs. +- A caller can front-run a `recoverSurplus()` call with `sync()`, + absorbing the surplus into the pool and leaving nothing to recover. + +On LEZ, the MEV concern is reduced because surplus only arises from +unsolicited transfers (no rebasing tokens), making extractable surplus +rare and unpredictable. However, the front-running risk remains: any +pending `recoverSurplus()` transaction can be neutralised by a `sync()` +call. + +### Ecosystem precedent + +| Protocol | sync() | skim() / recoverSurplus() | Notes | +|----------|--------|--------------------------|-------| +| Uniswap V2 | Yes (permissionless) | Yes (permissionless) | Both needed for rebasing tokens and overflow protection | +| Uniswap V4 | Different function (settlement, not 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 separate array | +| Raydium | No | No | Surplus silently accumulates; unrecoverable | +| Orca Whirlpools | No | No | Same as Raydium | + +Uniswap V2 is the only protocol that implements both functions. +Later protocols eliminated the need entirely by moving to live balance +reads or transient accounting. Solana protocols do not implement either +function, and surplus tokens in Raydium and Orca vaults are +effectively permanently stuck. + +### Recommendation + +**Implement `sync()`. Implement `recoverSurplus()`, restricted to +the empty-pool case.** + +1. **`sync()`** should be permissionless, matching Uniswap V2. It + absorbs surplus into the pool, benefiting LPs. This is the expected + behaviour: if tokens arrive in the vault, they should become part of + the pool. + +2. **`recoverSurplus(to)`** should be callable only when the pool has + zero liquidity (total LP supply is zero). This addresses the + implementor's scenario (surplus in an empty pool) without creating a + permissionless extraction function that competes with `sync()` during + normal operation. + + When LP supply is zero, `sync()` cannot benefit anyone because there + are no LP holders. In this state, `recoverSurplus()` is the only + way to move the tokens out, and there is no front-running concern + because `sync()` in an empty pool would just set reserves that no + one can claim. + + If a broader permissionless `recoverSurplus()` is preferred (matching + Uniswap V2's `skim()` exactly), the trade-off is that callers will + always race against `sync()` callers. On LEZ this is low-stakes + (surplus is rare), but the restricted version is cleaner. + +3. **Do not implement live balance reads as an alternative.** Curve + StableSwapNG's `_balance()` approach avoids the problem entirely + but adds an external call per token per operation. Since the LEZ DEX + already uses cached reserves and `sync()`, switching to live reads + would be a larger architectural change for marginal benefit on a + chain with no rebasing tokens. + +## 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 From cbf28e0197db54b6f9452049ffe28df0e934c4ff Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Tue, 28 Apr 2026 11:32:58 +1000 Subject: [PATCH 02/10] appendix: drop recoverSurplus, frame ATA as compatibility - Remove recoverSurplus() recommendation. F.9 now requires only permissionless sync(); appendix section 10 describes ecosystem practice (Uniswap V2 sync/skim, V4/Balancer/Curve alternatives) without prescribing LEZ behaviour. - Reframe F.8: the DEX program must be compatible with ATAs but must not force them. Pool vaults may use PDAs (matching Raydium and Orca); user-side accepts any valid SPL token account owned by the caller. - Restructure appendix to describe what ecosystem DEXes do, not what LEZ should do. Recommendations belong in the RFP body. Co-Authored-By: Claude Opus 4.7 (1M context) --- RFPs/RFP-004-privacy-preserving-dex.md | 19 ++- appendix/dex-ecosystem-behaviour.md | 215 ++++++++++--------------- 2 files changed, 94 insertions(+), 140 deletions(-) diff --git a/RFPs/RFP-004-privacy-preserving-dex.md b/RFPs/RFP-004-privacy-preserving-dex.md index 98d5697..749c868 100644 --- a/RFPs/RFP-004-privacy-preserving-dex.md +++ b/RFPs/RFP-004-privacy-preserving-dex.md @@ -88,17 +88,20 @@ participants. 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)). +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. Implement a `recoverSurplus(to)` function, callable only - when the pool has zero liquidity (total LP supply is zero), that - transfers surplus tokens from the vault to a specified account. See - [Appendix: DEX Ecosystem Behaviour, section 10](../appendix/dex-ecosystem-behaviour.md#10-recommendation-sync-and-recoversurplus-functions) + 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 diff --git a/appendix/dex-ecosystem-behaviour.md b/appendix/dex-ecosystem-behaviour.md index d241275..3b390d5 100644 --- a/appendix/dex-ecosystem-behaviour.md +++ b/appendix/dex-ecosystem-behaviour.md @@ -173,23 +173,29 @@ Curve). 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. Associated Token Accounts for token custody - -**RFP-004 requirement:** Use Associated Token Accounts (ATAs) for all -token interactions, derived per `(owner, mint)` pair (requirement F.8). - -**Ecosystem practice on Solana:** Both Raydium and Orca use -deterministic PDA-derived token accounts for pool vaults: - -- **Raydium CLMM:** Vault seed `["pool_vault", pool_state, token_mint]` -- **Orca Whirlpools:** Similar PDA derivation per pool per token mint - -User-side, the SPL Associated Token Account program derives one token -account per `(wallet, mint)` pair, and Solana DEX frontends universally -create ATAs on-demand. - -On LEZ (which uses SVM), the ATA requirement aligns with standard -Solana practice. +## 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 @@ -260,130 +266,75 @@ unacknowledged by the pool's reserve accounting. | 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. Recommendation: `sync()` and `recoverSurplus()` functions - -**Context:** The LEZ DEX implementation uses cached reserves (the -Uniswap V2 pattern). Because anyone can transfer SPL tokens into a -pool's vault account without invoking the pool program, the actual -vault balance can exceed the pool's recorded reserves. Two functions -handle this discrepancy: - -- **`sync()`** updates the cached reserves to match the live vault - balance, absorbing the surplus into the pool. The surplus becomes - part of the pool's reserves, benefiting all current LPs - proportionally. -- **`recoverSurplus(to)`** (Uniswap V2's `skim()`) transfers the - difference between the vault balance and the cached reserve to a - specified address. The reserves remain unchanged; only the excess - leaves. - -### How surplus arises on LEZ - -On SVM, surplus can only arise through one channel: an external account -explicitly transfers tokens into the pool's vault account outside the -normal swap or liquidity flow. Unlike Ethereum, there are no rebasing -tokens, no fee-on-transfer mechanics, and no interest-bearing token -accrual on SVM. The surplus problem on LEZ is therefore narrower than -on Ethereum: it is limited to unsolicited direct transfers, whether -accidental or intentional. - -### Why sync() is needed - -Even though surplus arises less frequently on SVM than on Ethereum, -`sync()` serves an important role: it reconciles the pool's internal -accounting with the actual vault balance. Without it, surplus tokens -sit in the vault permanently, unacknowledged by the pool's reserve -state. `sync()` lets anyone absorb the surplus into the pool, where it -benefits LPs. - -On Ethereum, `sync()` also handles the opposite case: if a negative -rebase reduces the vault balance below the cached reserve, `sync()` -writes down the reserve so that swaps do not revert. On SVM, this -scenario cannot occur (token balances only change through explicit -instructions), so `sync()` on LEZ is strictly a surplus-absorption -function. - -### The case for recoverSurplus() - -The implementor identifies one scenario where `recoverSurplus()` is -genuinely useful: all LPs remove their liquidity, but surplus tokens -remain in the vault. In this state: - -- Calling `sync()` would set the reserves to the vault balance, but - with zero LP tokens outstanding, no one holds a claim on those - reserves. -- The next user to `sync()` and then add liquidity would effectively - absorb the surplus into their position. -- `recoverSurplus()` provides a direct path to extract the tokens - without requiring someone to enter the pool first. - -### The case against recoverSurplus() - -In Uniswap V2, `skim()` is permissionless: any caller can extract the -surplus at any time. This creates a race condition: - -- On Ethereum, positive rebases generate surplus continuously, and MEV - bots monitor all pools for skimmable amounts. The economic value of - rebases is captured by bots, not LPs. -- A caller can front-run a `recoverSurplus()` call with `sync()`, - absorbing the surplus into the pool and leaving nothing to recover. - -On LEZ, the MEV concern is reduced because surplus only arises from -unsolicited transfers (no rebasing tokens), making extractable surplus -rare and unpredictable. However, the front-running risk remains: any -pending `recoverSurplus()` transaction can be neutralised by a `sync()` -call. +## 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) | Yes (permissionless) | Both needed for rebasing tokens and overflow protection | +| Uniswap V2 | Yes (permissionless) | Yes (permissionless) | Handles rebasing tokens, direct transfers, and negative-rebase deficits | | Uniswap V4 | Different function (settlement, not 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 separate array | -| Raydium | No | No | Surplus silently accumulates; unrecoverable | +| 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 protocol that implements both functions. -Later protocols eliminated the need entirely by moving to live balance -reads or transient accounting. Solana protocols do not implement either -function, and surplus tokens in Raydium and Orca vaults are -effectively permanently stuck. - -### Recommendation - -**Implement `sync()`. Implement `recoverSurplus()`, restricted to -the empty-pool case.** - -1. **`sync()`** should be permissionless, matching Uniswap V2. It - absorbs surplus into the pool, benefiting LPs. This is the expected - behaviour: if tokens arrive in the vault, they should become part of - the pool. - -2. **`recoverSurplus(to)`** should be callable only when the pool has - zero liquidity (total LP supply is zero). This addresses the - implementor's scenario (surplus in an empty pool) without creating a - permissionless extraction function that competes with `sync()` during - normal operation. - - When LP supply is zero, `sync()` cannot benefit anyone because there - are no LP holders. In this state, `recoverSurplus()` is the only - way to move the tokens out, and there is no front-running concern - because `sync()` in an empty pool would just set reserves that no - one can claim. - - If a broader permissionless `recoverSurplus()` is preferred (matching - Uniswap V2's `skim()` exactly), the trade-off is that callers will - always race against `sync()` callers. On LEZ this is low-stakes - (surplus is rare), but the restricted version is cleaner. - -3. **Do not implement live balance reads as an alternative.** Curve - StableSwapNG's `_balance()` approach avoids the problem entirely - but adds an external call per token per operation. Since the LEZ DEX - already uses cached reserves and `sync()`, switching to live reads - would be a larger architectural change for marginal benefit on a - chain with no rebasing tokens. +Uniswap V2 is the only surveyed protocol that implements both +functions. 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 From be006301248a6514456617a9894426583a17d36a Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Tue, 28 Apr 2026 12:00:28 +1000 Subject: [PATCH 03/10] appendix: refresh metrics and tighten claims after fact-check - Refresh Protocols Considered table from DeFiLlama (snapshot 2026-04-27): V2 ~$970M, V4 ~$720M (~16 chains, was >$1B/17), Balancer V3 ~$80M post 2025-11 V2 exploit, Curve ~$1.7B, Raydium ~$1.0B, Orca ~$255M. Replace unsourced ">$300B" Orca cumulative with "$36B+ all-time" from Orca's own dashboards. - Soften "over 100 forks" of V2 to a sourced characterisation. - Clarify V2 protocol fee: UNIfication passed 2025-12-26; LPs receive 0.25%, protocol takes 0.05% (1/6 of 0.3%). - Replace "98% gas reduction" round-number claim with the underlying TSTORE-vs-SSTORE gas costs (100 vs 20,000) per EIP-1153. - Tighten section-10 table: V4 has `PoolManager.sync()` for flash-accounting balance checkpointing (not reserve reconciliation); previous wording read as "no sync at all". Co-Authored-By: Claude Opus 4.7 (1M context) --- appendix/dex-ecosystem-behaviour.md | 53 ++++++++++++++++------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/appendix/dex-ecosystem-behaviour.md b/appendix/dex-ecosystem-behaviour.md index 3b390d5..f146fe9 100644 --- a/appendix/dex-ecosystem-behaviour.md +++ b/appendix/dex-ecosystem-behaviour.md @@ -11,15 +11,18 @@ 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 | Constant-product AMM | ~$945M | $604B+ | -| Uniswap V4 | Ethereum (17 chains) | Singleton AMM with hooks | >$1B | >$190B (2025) | -| Balancer V3 | Ethereum (9 chains) | Vault-based AMM with hooks | $104M to $151M | N/A (fee data only) | -| Curve StableSwapNG | Ethereum | StableSwap AMM | ~$2.3B | ~$126B (2025) | -| CoW Protocol | Ethereum (10 chains) | Intent-based batch auction | N/A (no custody) | $87B (2025) | -| Raydium | Solana | AMM + CLMM | $1.4B | $695.7B all-time | -| Orca Whirlpools | Solana (+ Eclipse) | CLMM | $246.8M | >$300B | +| Uniswap V2 | Ethereum (multi-chain) | Constant-product AMM | ~$970M | $604B+ | +| Uniswap V4 | Ethereum (~16 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 @@ -28,10 +31,10 @@ 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 over 100 forks (PancakeSwap, SushiSwap, Aerodrome, -and others). Pool state (reserves, price, cumulative volume) is fully -public on-chain in every surveyed protocol. No production AMM stores -pool state privately. +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: @@ -77,7 +80,7 @@ distributed to LPs (requirement F.6). | Protocol | Fee payer | LP share | Protocol share | Other | |----------|-----------|----------|----------------|-------| -| Uniswap V2 | Trader (input token) | 100% (protocol fee optional: 0.05%) | 0% (or 1/6 of 0.3% when active) | Protocol fee activated Dec 2025 | +| Uniswap V2 | Trader (input token) | 0.25% (post-UNIfication) | 0.05% (1/6 of 0.3%) | UNIfication proposal passed 2025-12-26; 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 | @@ -232,11 +235,12 @@ 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. 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 sync/skim pattern entirely and achieves a 98% gas reduction for -state tracking versus persistent storage. +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) @@ -322,19 +326,20 @@ surplus before LPs benefit. | Protocol | sync() | skim() / recoverSurplus() | Notes | |----------|--------|--------------------------|-------| -| Uniswap V2 | Yes (permissionless) | Yes (permissionless) | Handles rebasing tokens, direct transfers, and negative-rebase deficits | -| Uniswap V4 | Different function (settlement, not reconciliation) | No | Flash accounting eliminates cached reserve drift | +| 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. 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. +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 From 960e7b6af96985fc4331b73f86eb94a2a6965a40 Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Tue, 28 Apr 2026 12:11:08 +1000 Subject: [PATCH 04/10] RFP-004: align with template (CLI, doc packets, Figma, soft reqs) Brings RFP-004 in line with RFP-000-template.md and the structure established by RFP-008: - Usability: add CLI requirement (template item 3); reorder so SDK, mini-app, CLI, IDL come first as standard items, RFP-specific items follow. - Supportability: split CI/test items, add doc packets for SDK and CLI, add Figma designs requirement. - Add Soft Requirements section with multi-hop routing, recoverSurplus() (moved from F.9 to a soft requirement), and compute-unit benchmarking. Co-Authored-By: Claude Opus 4.7 (1M context) --- RFPs/RFP-004-privacy-preserving-dex.md | 61 ++++++++++++++++++-------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/RFPs/RFP-004-privacy-preserving-dex.md b/RFPs/RFP-004-privacy-preserving-dex.md index 749c868..407282a 100644 --- a/RFPs/RFP-004-privacy-preserving-dex.md +++ b/RFPs/RFP-004-privacy-preserving-dex.md @@ -116,25 +116,28 @@ participants. 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 +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). +5. 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. +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). -5. Failed or rejected swaps must return clear, actionable error messages. -6. 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 +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. -8. The mini-app must display a swap preview before the user confirms: + (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. + taken, so the user can evaluate the trade before confirming. #### Reliability @@ -155,17 +158,19 @@ participants. 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, + 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. -4. A README documents end-to-end usage: deployment steps, program +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). -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. +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 @@ -190,6 +195,26 @@ participants. 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. +2. Provide a permissionless `recoverSurplus(to)` instruction, callable + only when the pool has zero liquidity (total LP supply is zero), to + sweep surplus tokens to a caller-specified address. See + [Appendix: DEX Ecosystem Behaviour, section 10](../appendix/dex-ecosystem-behaviour.md#10-reserve-reconciliation-sync-and-skim). + +#### Performance + +1. Compute unit usage of swap, add liquidity, remove liquidity, and + pool creation is documented and benchmarked against LEZ devnet + compute limits. + ### Privacy Architecture All DEX liquidity pools are public on-chain state. User privacy is From daff7885b5109752f32c0f834aab148b06566842 Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Tue, 28 Apr 2026 12:18:52 +1000 Subject: [PATCH 05/10] RFP-004: explicitly exclude skim/recoverSurplus Remove the soft requirement and add an Out of Scope note. Among surveyed protocols only Uniswap V2 implements a skim()-style instruction; Uniswap V4, Balancer V3, Curve StableSwapNG, Raydium, and Orca Whirlpools do not. Surplus reconciliation is handled exclusively by the permissionless sync(). Co-Authored-By: Claude Opus 4.7 (1M context) --- RFPs/RFP-004-privacy-preserving-dex.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/RFPs/RFP-004-privacy-preserving-dex.md b/RFPs/RFP-004-privacy-preserving-dex.md index 407282a..caff372 100644 --- a/RFPs/RFP-004-privacy-preserving-dex.md +++ b/RFPs/RFP-004-privacy-preserving-dex.md @@ -204,10 +204,6 @@ If possible. 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. -2. Provide a permissionless `recoverSurplus(to)` instruction, callable - only when the pool has zero liquidity (total LP supply is zero), to - sweep surplus tokens to a caller-specified address. See - [Appendix: DEX Ecosystem Behaviour, section 10](../appendix/dex-ecosystem-behaviour.md#10-reserve-reconciliation-sync-and-skim). #### Performance @@ -215,6 +211,19 @@ If possible. pool creation is documented and benchmarked against LEZ devnet compute limits. +### 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 From 25fa54df6f7fefe354eace69c2c2ae21cd612c27 Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Tue, 28 Apr 2026 12:19:21 +1000 Subject: [PATCH 06/10] RFP-004: move compute unit benchmarking to hard Performance The template lists compute-unit documentation as a hard Performance requirement. Consolidate it with the existing transaction-size documentation requirement and drop the redundant soft item. Co-Authored-By: Claude Opus 4.7 (1M context) --- RFPs/RFP-004-privacy-preserving-dex.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/RFPs/RFP-004-privacy-preserving-dex.md b/RFPs/RFP-004-privacy-preserving-dex.md index caff372..2b3087b 100644 --- a/RFPs/RFP-004-privacy-preserving-dex.md +++ b/RFPs/RFP-004-privacy-preserving-dex.md @@ -148,11 +148,12 @@ participants. 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 +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 @@ -205,12 +206,6 @@ If possible. transaction (e.g. flash-accounting style settlement of intermediate hops), reducing slippage on token pairs without a direct pool. -#### Performance - -1. Compute unit usage of swap, add liquidity, remove liquidity, and - pool creation is documented and benchmarked against LEZ devnet - compute limits. - ### Out of Scope The following are explicitly excluded from this RFP: From 1fd32bc1d52ee7d2091fedfd6fff40d04e1bad99 Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Fri, 29 May 2026 15:30:46 +1000 Subject: [PATCH 07/10] RFP-004: add standardized Platform Dependencies section Align RFP-004 with the Dependencies convention now used across RFPs 008, 012-017, 019, and 020. Document LP-0013 (token authorities, open) as the hard blocker for on-chain custody, and LP-0015/LP-0014/LP-0012 (all closed) as resolved dependencies, plus privacy primitives and the compute-budget risk. Co-Authored-By: Claude Opus 4.8 (1M context) --- RFPs/RFP-004-privacy-preserving-dex.md | 80 ++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/RFPs/RFP-004-privacy-preserving-dex.md b/RFPs/RFP-004-privacy-preserving-dex.md index 2b3087b..a75b02c 100644 --- a/RFPs/RFP-004-privacy-preserving-dex.md +++ b/RFPs/RFP-004-privacy-preserving-dex.md @@ -265,6 +265,86 @@ For every protocol operation initiated from a private account (swap, add/remove 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 Team experienced with: From 1357d552a7960a56685e6675c18a1c655fc249e7 Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Fri, 29 May 2026 15:32:56 +1000 Subject: [PATCH 08/10] appendix: fix fact-check findings (Orca tiers, Curve LP positions) - Orca Whirlpools has 9 tick spacings / fee tiers, not 6 (tick spacings 1,2,4,8,16,32,64,128,256). - Curve LP positions are tracked by LP token balances, not admin_balances[] (which holds accrued admin fees, per section 9). - UNIfication proposal passed 2025-12-25 (on-chain vote close), not -26. - Uniswap V4: ~12 mainnet deployments, not ~16. - Qualify Uniswap V2 cumulative volume as V2-only DeFiLlama figure. Co-Authored-By: Claude Opus 4.8 (1M context) --- appendix/dex-ecosystem-behaviour.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/appendix/dex-ecosystem-behaviour.md b/appendix/dex-ecosystem-behaviour.md index f146fe9..1b33491 100644 --- a/appendix/dex-ecosystem-behaviour.md +++ b/appendix/dex-ecosystem-behaviour.md @@ -16,8 +16,8 @@ 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+ | -| Uniswap V4 | Ethereum (~16 chains) | Singleton AMM with hooks | ~$720M | >$190B (2025) | +| 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) | @@ -63,7 +63,7 @@ Multiple pools for the same pair with different tiers can coexist | 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 | 6 tick spacings mapping to fee tiers (0.01% to 2%) | Yes (adaptive pools add a variable component) | 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. @@ -80,7 +80,7 @@ distributed to LPs (requirement F.6). | 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-26; pre-activation LPs received the full 0.3% | +| 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 | @@ -171,8 +171,8 @@ aggregate metrics on-chain or via indexers: | 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, NFT positions in CLMMs, `admin_balances` in -Curve). RFP-004's privacy model does not change this: LP positions +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. From 0ba5ec746d402b03c1ee249ec12177dd4884d9a0 Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Fri, 29 May 2026 15:34:22 +1000 Subject: [PATCH 09/10] style: run mdformat on RFP-004 and DEX appendix Per README formatting instructions (mdformat + mdformat-gfm + mdformat-frontmatter, config in .mdformat.toml). Co-Authored-By: Claude Opus 4.8 (1M context) --- RFPs/RFP-004-privacy-preserving-dex.md | 429 +++++++++++----------- appendix/dex-ecosystem-behaviour.md | 477 ++++++++++++------------- 2 files changed, 430 insertions(+), 476 deletions(-) diff --git a/RFPs/RFP-004-privacy-preserving-dex.md b/RFPs/RFP-004-privacy-preserving-dex.md index a75b02c..4a7123f 100644 --- a/RFPs/RFP-004-privacy-preserving-dex.md +++ b/RFPs/RFP-004-privacy-preserving-dex.md @@ -7,56 +7,51 @@ status: open 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,137 +59,128 @@ 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. 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 + 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 + 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 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. +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). -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). +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. +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. 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. +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. +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) +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 @@ -202,104 +188,98 @@ 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. +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 +- 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. +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. +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 +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. +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. +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. @@ -312,38 +292,35 @@ 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. +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. +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. - +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 @@ -361,12 +338,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) @@ -377,5 +352,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 index 1b33491..585d3dd 100644 --- a/appendix/dex-ecosystem-behaviour.md +++ b/appendix/dex-ecosystem-behaviour.md @@ -1,356 +1,335 @@ # 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. +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. +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) | +| 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). +**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. +**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. +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. +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). +**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%). +| 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). +**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 | | +| 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). +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). +**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: +**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 | +| 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). +**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 | +| 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. +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). +**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). +**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. +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. +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). +**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: +**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 | +| 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. +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: +**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. +- **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. + 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). +**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: +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). +- **`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). +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. +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. +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. +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) | +| 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. +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. +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. +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: +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. +- **`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. +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. +| 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: +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/ +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 From 65c6b441126b3a80f5cfa3853e03214ae7bb936d Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Fri, 29 May 2026 15:35:54 +1000 Subject: [PATCH 10/10] RFP-004: add dependencies to frontmatter Mirror the structured dependencies list used in RFP-008 (id + reason). Statuses verified against logos-co/lambda-prize: LP-0013 open (hard blocker), LP-0015/LP-0014/LP-0012 closed (delivered). Co-Authored-By: Claude Opus 4.8 (1M context) --- RFPs/RFP-004-privacy-preserving-dex.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RFPs/RFP-004-privacy-preserving-dex.md b/RFPs/RFP-004-privacy-preserving-dex.md index 4a7123f..0aebe99 100644 --- a/RFPs/RFP-004-privacy-preserving-dex.md +++ b/RFPs/RFP-004-privacy-preserving-dex.md @@ -4,6 +4,15 @@ 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 ---