
SeaWall
Aqua-powered ETH downside insurance: fully collateralized puts, SwapVM-enforced settlement.
Demo
About
Seawall is an Aqua-powered, fully collateralized ETH put option protocol that gives ETH holders downside protection without selling their ETH. A buyer purchases a put with a chosen strike and expiry, while an LP makes USDC collateral available through Aqua to back the option's maximum payout. The premium is calculated by an on-chain pricing engine, and the collateral liability is reserved before the option becomes active. Custom SwapVM instructions manage the complete lifecycle — opening, buying, exercising, settling and expiring. At expiry the settlement price determines the intrinsic payout, which is paid to the buyer from the reserved collateral. The design is permissionless and keeperless, with strict collateral accounting that prevents options from being created when their maximum liability exceeds available funds. Seawall turns Aqua's programmable token balances into a practical on-chain financial primitive for transparent, automated ETH downside protection.
How it's made
Stack: Solidity 0.8.30 (Foundry, viaIR) for the protocol The protocol is four contracts: PricingEngine (deterministic Black-Scholes), CollateralManager (total / reserved / available accounting), OptionManager (the lifecycle state machine), and SeawallRouter — an extension of 1inch SwapVM. The router inherits the official SwapVM + AquaOpcodes and appends five custom instructions. OPTION_OPEN, OPTION_BUY, OPTION_EXERCISE, OPTION_SETTLE, OPTION_EXPIRE. Programs are built on-chain by the router ([opcode][argsLen][args]) and every lifecycle action goes through the unmodified swap() entry point: the VM decodes the program, dispatches to our handlers, validates maker/taker traits and performs the transfers. The pool is exactly one Aqua strategy, poolId = keccak256(abi.encode(buyOrder)). LP collateral is the live rawBalances(maker, router, poolId, USDC); premiums settle through Aqua's transfer phase (push) and payouts via pull inside OPTION_SETTLE. The LP never deposits into a vault — Aqua records how much the protocol may pull, and every pull is preceded by a live balance check, so an unavailable balance reverts instead of paying from nowhere. How SwapVM benefited us: the option lifecycle is bytecode, not a bespoke contract per product — a new option type would be a new program, and the whole lifecycle is enforced by the same VM dispatch, trait validation and transfer machinery as any SwapVM swap. Hacky bits: - SwapVM requires amountOut > 0, but option operations aren't swaps. Each instruction settles a 1 wei WETH "execution marker" through the regular transfer phase (LPs ship dust WETH per strategy), while the real payout is moved by OPTION_SETTLE straight from the pool's Aqua balance. - We replicated SwapVM's TakerTraitsLib.build in TypeScript for the UI. Its uint160 index pack is big-endian, so the encoded order is index9..index0 — the first version was reversed and only an end-to-end smoke test against the real router caught it. - The router must fit EIP-170: we consolidated four order builders into one, moved pool admin into OptionManager, and tuned the optimizer to land at 23.3 KB. - Forge 1.5.1 mis-decodes the router's four-address constructor args on top-level script CREATEs, so a small RouterDeployer creates it from inside a call. Testing: 99 Foundry tests — pricing reference values, lifecycle, integration through the real Aqua registry and SwapVM transfer phase, stateful invariants (reserved <= total, payout <= notional, accounting identity), and a mainnet-fork test that reads the real Chainlink ETH/USD feed and uses the canonical deployed Aqua registry with real USDC/WETH. A TypeScript smoke script exercises the exact UI encoding and asserts every USDC movement.