Development of Stable Swap Protocol
Curve Finance holds $3-5B TVL not because it's first DEX, but because it solved specific math problem: Uniswap V2 with x*y=k curve gives 1% slippage already at 0.1% pool swap. For assets meant to be worth same (USDC/USDT, stETH/ETH, WBTC/renBTC), this is unacceptable. Stable swap invariant concentrates liquidity around parity and reduces slippage orders of magnitude.
If your project works with pegged assets — LST (liquid staking tokens), stablecoins, wrapped assets — you need exactly this math.
Stable swap math: why it's not just "AMM for stablecoins"
Curve StableSwap invariant
Curve uses hybrid invariant, combining constant sum (x+y=k, zero slippage) and constant product (x*y=k, infinite liquidity). Formula for two assets:
A * n^n * sum(x_i) + D = A * D * n^n + D^(n+1) / (n^n * prod(x_i))
where A — amplification coefficient, D — invariant, n — number of assets.
Amplification coefficient — key parameter. At A=0 system behaves like Uniswap (constant product). At A→∞ — like constant sum. Curve uses A=100-2000 depending on pool. For USDC/USDT pairs — high A (100-200), because assets rarely deviate from parity. For stETH/ETH at start — lower, because stETH traded at discount and high A would lead to pool imbalance.
Important: A can change. But A change must be gradual — Curve implements ramp_A/stop_ramp_A with minimum 7-day timelock and max 10x change per time. Sharp A change on imbalanced pool — essentially changing asset prices, equivalent to manipulation.
Solving D via Newton iterations
Invariant equation has no analytical solution for D — Newton-Raphson method used. Typical implementation converges in 4-8 iterations at normal balances. Problem: at severely imbalanced pool or extreme values iterations may not converge.
In Solidity this looks like loop with 255 iteration limit and |D_new - D_prev| <= 1 check. If not converged — revert. Rare case, but without it contract can hang in infinite loop during attack with specially crafted parameters.
Calculation accuracy and PRECISION_MUL
Different stablecoins have different decimals: USDC — 6, DAI — 18, USDT — 6. Inside contract all balances normalized to 18 decimals via PRECISION_MUL = [1e12, 1e12, 1] (for USDC/USDT/DAI pool). Forgetting this normalization — means arithmetic vulnerability allowing unfair asset extraction via remove_liquidity_one_coin.
Real case: exactly decimals normalization error became one vector in several Curve forks, where attacker could extract more 18-decimal tokens than deposited 6-decimal tokens.
What we build: protocol components
Basic pool (2-3 assets)
Contract stores balances, implements exchange(), add_liquidity(), remove_liquidity(), remove_liquidity_one_coin(). LP-token — ERC-20 with mint on liquidity addition. Fee (usually 0.04%) split between LP and protocol fee.
get_dy() (view function calculating output) must replicate exchange() logic with 1 wei precision. If discrepancy exists — arbitrageurs will exploit it.
Meta-pool architecture
Curve uses meta-pools for pairs like newStable/3CRV. New stableswap pooled not with three assets directly, but with LP-token of base 3pool. This:
- Reduces liquidity fragmentation
- Gives access to entire 3pool liquidity via single trading pair
Implementation more complex: exchange_underlying() must unwrap nested LP-token to base assets. Math remains same, but additional pool calls appear.
Rate providers for non-pegged assets (Curve V2 / Balancer-style)
stETH/ETH doesn't trade exactly 1:1 — stETH accumulates yield. Need rate provider: contract returning current exchange rate. Pool uses rate in invariant calculation, allowing slippage-free trading even when assets not 1:1.
Critical point: if rate provider is manipulable (e.g., uses spot price from pool) — entire pool math becomes exploitable. Rate provider must use external trusted oracle or accumulated rate (like stETH/ETH from Lido contract), independent from current trades.
Stack and tools
Solidity 0.8.x for main contracts. For high-precision math use mulDiv from OpenZeppelin Math — avoids overflow on intermediate calculations like a * b / c where a * b might exceed uint256.
Foundry for testing: fork-tests on Ethereum mainnet allow reproducing real Curve pool balances and comparing our contract output with original. Discrepancy >1 wei on same inputs — red flag.
Vyper (like Curve) vs Solidity: original Curve written in Vyper. We write Solidity for better toolchain compatibility (Foundry, Slither, Hardhat ecosystem). Math — identical with correct implementation.
| Component | Tool | Reason |
|---|---|---|
| Contracts | Solidity 0.8.x | Toolchain compatibility |
| Math | OpenZeppelin Math.mulDiv | Overflow-safe |
| Tests | Foundry + fork mainnet | Compare with Curve original |
| Static analysis | Slither + Aderyn | Detect arithmetic issues |
| Fuzzing | Echidna | D-constant invariants |
Risks when forking Curve
Forking Curve — tempting, code open. But:
Vyper→Solidity translation contains traps. In Vyper @view functions can't change state at compiler level. In Solidity only view modifier enforces this, not always applied correctly during translation.
Read-only reentrancy: read-only reentrancy attack on Curve (2023) allowed price manipulation in LP-token via call during remove_liquidity. Protocols using Curve LP-token price as oracle were vulnerable. If your pool planned as oracle for other protocols — implement reentrancy lock on all state-changing functions and separate view-safe price feed.
Development process
Specification (1 week). Define: number of assets, need rate providers, architecture (basic pool vs meta-pool), fee model, governance parameters (who changes A and fees).
Mathematical core development (1-2 weeks). Invariant, Newton's method for D, get_y() for output calculation. Test coverage: comparison with Curve Python reference implementation.
Pool and LP-token contracts (1-2 weeks). exchange, add/remove liquidity, admin functions with timelock.
Integration tests (1 week). Fork-tests, fuzzing invariants, stress testing extreme imbalance scenarios.
Audit. For pools with real funds — external audit mandatory. Stable swap math non-trivial and contains non-obvious edge cases.
Timeline: 2-3 months from specification to audit readiness. Depends on architecture complexity (basic pool vs meta-pool with rate providers). Cost — after finalizing requirements.







