Decay AMM

The Decay AMM is a constant-product (xyc) automated market maker with a Mooniswap-style virtual-balance decay modifier. You author it as a program for SwapVM (the Aqua swap engine) and register it on the Aqua router (AquaSwapVMRouter). After each swap, _decayXD temporarily widens the spread by inflating balanceIn and deflating balanceOut, then decays back to true reserves over period seconds. This discourages sandwich attacks by making back-runs land at a worse price immediately after the initial swap.

A Decay AMM is a maker strategy on Aqua: liquidity stays in the maker's wallet under a revocable, per-chain, per-token allowance and moves only when a taker fills a swap atomically. The Aqua registry holds no tokens of its own.


Core instructions

A Decay AMM strategy composes three instructions from the SwapVM instruction set:

Instruction Role
_dynamicBalancesXD Load reserves; persist after swap
_decayXD Apply virtual offset; call ctx.runLoop() for subsequent instructions
_xycSwapXD Compute x × y = k pricing inside the decay loop

_decayXD is a wrapping instruction; it calls ctx.runLoop() internally, so the swap formula executes inside its nested loop. Place it before _xycSwapXD.


Program

Solidity
1
2
3
4
5
6
7
8
9
Program memory program = ProgramBuilder.init(_opcodes());
bytes memory bytecode = bytes.concat(
    program.build(_dynamicBalancesXD, BalancesArgsBuilder.build(
        dynamic([tokenA, tokenB]),
        dynamic([uint256(1_000e18), uint256(1_000e18)])
    )),
    program.build(_decayXD, DecayArgsBuilder.build(300)), // 300-second decay period
    program.build(_xycSwapXD)
);

Args (from Decay)

Field Size Type Description
period 2 bytes uint16 Seconds until the virtual offset fully decays to zero

How decay works

  1. After a swap, the storage records (amountIn, amountOut, timestamp) of the last swap.

  2. On the next quote/swap, _decayXD computes elapsed time since the last swap.

  3. If elapsed < period, it applies a proportional virtual offset: balanceIn is inflated and balanceOut is deflated by offset × (1 − elapsed/period).

  4. The offset is largest immediately after a swap and approaches zero as elapsed → period.

This means a sandwich attacker's back-run sees a wider spread proportional to how quickly it follows the victim swap.


Program position

_dynamicBalancesXD → _decayXD → [fee] → _xycSwapXD

If adding a fee, place it between _decayXD and _xycSwapXD; both are wrapping instructions and will nest correctly.

_dynamicBalancesXD → _decayXD → _flatFeeAmountInXD → _xycSwapXD

See Modifiers for the full wrapping order example.


Optional modifiers

Goal Add
Flat fee _flatFeeAmountInXD between _decayXD and _xycSwapXD
Expiry _deadline before balance setup

  • Decay — instruction args and virtual offset math

  • XYCSwap — constant-product formula

  • Constant Product — same curve without MEV protection

  • Modifiers — wrapping instruction ordering rules

Did you find what you need?