One ETH balance is the exit and entry liquidity for every staked-ETH token, priced at the live rate, on 1inch Aqua, through 1inch's official router.
Built at ETHGlobal Tokyo 2026 for the 1inch "Build an Aqua App" track.
- 1inch's PeggedSwap fixes its price when the order is created. wstETH, rETH and weETH gain value every day, so a fixed peg goes stale within hours and arbitrage takes the difference from the maker. 1inch's own docs say PeggedSwap is "not suitable for pairs where the peg ratio drifts over time (e.g. WETH/wstETH)".
- Every staked-ETH pair needs its own pool with its own ETH. A maker who wants to serve three tokens splits its capital three ways.
- Leaving a staking token through the issuer's own queue is not instant.
- Prices follow the token's own on-chain rate. No oracle, nothing to push, nothing to go stale. wstETH reports
stEthPerToken(), rETHgetExchangeRate(), weETHgetRate(). One small rate-provider contract per token. - One wallet backs many markets. Aqua's shared-liquidity model lets the same 10 WETH be the backing for wstETH, rETH and weETH markets at once. Tokens leave the wallet only when a trade fills; an over-draw fails safely.
- Runs on 1inch's official router. Our pricing is packaged as an
Extructiontarget, so the liveAquaSwapVMRouterv1.0.2 serves it without any custom router. We also ship our own router (RateSpaceAquaRouter, opcode0x59) with the same maths as a native instruction; both give the same amounts to the wei. - The same order keeps trading as the rate moves. Anchors are fixed once in ETH value; the live rate is read on every trade; a guard band around the reference rate stops trading if the rate moves too far (cap 10%).
Everything below was re-run from this repository. Local rows need no network; fork rows need a mainnet RPC.
| Claim | Where | Result |
|---|---|---|
| The Python model and the Solidity return the same integers | test/ModelParity.t.sol; python3 analysis/gen_parity.py --check |
109 MovingPegSwap + 18 band + 16 PeggedSwap cases equal to the wei; --check prints True |
| After a 1.20 → 1.25 rate step, a RateSpace pool is barely off the new price; a frozen peg is far off | python3 analysis/s3a_arb_step.py |
ours 0.010206 bps, frozen PeggedSwap 157.015569 bps (best arbitrage, as bps of pool value, width A = 50) |
| The 0.05% fee blocks trading around a rate report while one report moves the rate by less than the break-even step | python3 analysis/s3e_fee_roundtrip.py |
break-even 10.0509 bps |
| Real rate updates are far below that | python3 analysis/s5_lido_reports.py --offline, python3 analysis/s6_lst_rates.py --offline |
Lido: 1228 reports since V2, largest 3.223442 bps, 0 drops. Last 365 days: rETH 363 updates, largest 3.491327; cbETH 364, largest 3.341668; weETH 14863 changes, largest 0.987708; 0 drops anywhere; 39 on-chain cross-checks match |
| One 10 WETH wallet backs three markets | test/SharedLiquidity.t.sol |
10e18 WETH in the wallet, 10e18 virtual WETH per strategy, 30e18 promised; three 1e18 sells paid 3519603734275451780 wei = the wallet's drop exactly; the next over-draw reverts |
| Our pricing on 1inch's official router equals our own router | test/extruction/MovingPegExtructionEquivalence.t.sol |
same 5-swap sequence, all 12 amounts equal to the wei, also with a stepped rate and with unbalanced anchors |
| Full local suite | MAINNET_RPC_URL= forge test |
86 passed, 0 failed (12 fork tests skip without an RPC) |
| A real Lido report moves our price with the rate, same order hash | test/fork/MainnetFork.t.sol (mainnet fork, blocks 26047292 → 26047293) |
ours 0.003 bps from the new rate, frozen PeggedSwap 0.617 bps |
| Gas on the live Aqua | test/fork/MainnetFork.t.sol |
ours (fee + MovingPegSwap) 155076; upstream fee + PeggedSwap 117248 |
Pricing on 1inch's LIVE router 0x111111338c5091e8440b67b168bae16a668ac0de via Extruction |
test/fork/LiveRouterExtruction.t.sol (mainnet fork) |
7/7: dispatch through opcode 0x20, 12 amounts equal our router, an arbitrary EOA fills, guards revert; gas 169756 (171512 with the fee) |
Both fork files ran 12/12 on 2026-09-26 against Ethereum mainnet block 26047293.
The contracts went through four rounds of adversarial review by independent reviewers (two per round) plus a hands-on re-run of every finding. Findings were reproduced before being accepted, and each surviving mutation got a pinning test. No value-extracting bug was found in any round; every round-4+ finding was a missing test. The confirming reviews are part of the commit history.
Needs Foundry (anvil, forge, cast) and bun. No mainnet RPC needed.
git submodule update --init
(cd packages/contracts/lib/swap-vm && bun install --ignore-scripts)
(cd packages/contracts/lib/swap-vm-v1 && bun install --ignore-scripts)
bun install --ignore-scripts
cd packages/contracts
script/demo.sh # anvil on :8545 (chain 31337); deploys 1inch Aqua + router v1.0.2 from source, RateSpace, demo tokens; seeds 4 orders
script/rate-step.sh wstETH 1 # optional: move a demo rate by +1 bp (also rETH | weETH)
script/demo-stop.sh # when done
cd ../.. && bun run dev:web # the app on http://localhost:3001 (RPC http://127.0.0.1:8545)Accounts (anvil's public test keys):
- Maker = anvil #0
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266: 10 WETH backing three no-fee orders (wstETH, rETH, weETH) plus one wstETH order with the 0.05% fee. - Taker = anvil #1
0x70997970C51812dc3A010C7d01b50e0d17dc79C8: 5 WETH and 2 of each demo token, router approved.
Import the keys into a browser wallet on chain 31337, then:
/— one maker wallet, its real WETH balance, and the virtual WETH each of the three markets quotes against./trade(taker) — pick a token, Exit (token → WETH) or Enter (WETH → token), type an amount, press Swap. The app never encodes order bytes itself: an on-chainRateSpaceOrderBuilderbuilt from 1inch's own libraries produces them./rate(maker) — press "Simulate report +1 bp". The rate and the quote move; the order hash stays the same.
Mainnet-fork proofs: put MAINNET_RPC_URL=<your rpc> in packages/contracts/.env and run forge test --match-path 'test/fork/*' -vv.
packages/contracts/
src/instructions/MovingPegSwap.sol the instruction (opcode 0x59): PeggedSwap with live, guarded rates
src/extruction/ MovingPegExtruction: the same maths as an Extruction target for the official router
src/rate-providers/ wstETH, rETH, weETH rate providers (IRateProvider)
src/opcodes/, src/routers/ RateSpaceAquaRouter (our router)
src/demo/, script/ on-chain order builder, demo tokens and rate feeds, deploy + seed script, demo.sh
test/ unit, parity, invariant, shared-liquidity, extruction, fork
analysis/ exact-integer Python model; every number in MATH.md comes from a script here
MATH.md the maths, with reproducible numbers
lib/swap-vm, lib/swap-vm-v1 1inch swap-vm @ 3b3da7d and @ v1.0.2 (read-only submodules)
apps/web/ the app (TanStack Start, viem)
PITCH.md the pitch
- Not audited.
- The demo runs on a local chain with mock tokens and settable mock rate feeds. Nothing is deployed on a public chain.
- Anchors must be value-balanced at ship time (the deploy script does this); unbalanced anchors move the centre price by design.
- Without a fee, a trader who knows a rate report is coming can buy before and sell after it. The 0.05% fee blocks this while one report moves the rate by less than 10.0509 bps; the largest report we measured across four tokens was 3.49 bps.
- The demo taker data carries no minimum-output threshold; add one before any non-local use.
- Team:
- Demo video:
- ETHGlobal showcase: