From fb6dd22c1255b85f75abccbc1b074a7211db13c1 Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Fri, 10 Apr 2026 22:03:58 +1000 Subject: [PATCH 1/6] 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 20eedb3ba92b73d27d4c538ac9cb2e97be42f8db Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Tue, 28 Apr 2026 11:32:58 +1000 Subject: [PATCH 2/6] 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 645c3c9cec0d6c6bc6bdaa25eddf0b0b3907ea81 Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Tue, 28 Apr 2026 12:00:28 +1000 Subject: [PATCH 3/6] 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 7ba1e5eab5d9c85640108148ae2cd726cee18adb Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Tue, 28 Apr 2026 12:11:08 +1000 Subject: [PATCH 4/6] 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 39bd6ed07b4b6abfd646bf89a7be673ed31b04d2 Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Tue, 28 Apr 2026 12:18:52 +1000 Subject: [PATCH 5/6] 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 1c5ff498c0d4a89a1555f89c6cd6fd58c8c40900 Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Tue, 28 Apr 2026 12:19:21 +1000 Subject: [PATCH 6/6] 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: