Time-Weighted Average Price (TWAP) and Dollar-Cost Averaging (DCA) are order archetypes that release liquidity over a fixed schedule. They are authored as SwapVM programs (SwapVM is the Aqua swap engine) and execute on the limit-order router, not on the Aqua swap router. Both are implemented by the _twap instruction, which combines a linear unlock schedule with a Dutch auction decay on each interval's sub-order.
A TWAP sells a fixed total amount evenly over duration. DCA is the same program executed periodically, where each execution fills the currently unlocked tranche.
The maker's tokens stay in their own wallet under a revocable, per-chain, per-token allowance and are pulled per tranche only as fills occur. Fills are not guaranteed: a tranche can be skipped when it is illiquid, and the schedule expresses intended pacing, not a promised outcome.
Core instruction
| Instruction | Role |
|---|---|
_twap |
Manages the unlock schedule, the Dutch auction per tranche, and internal invalidation |
_twap is a terminal instruction. It replaces the balance-and-swap formula combination used in ordinary limit orders.
Program
Program memory program = ProgramBuilder.init(_opcodes());
bytes memory bytecode = bytes.concat(
program.build(_twap, TWAPArgsBuilder.build(TwapArgs({
balanceIn: totalAmountIn, // Total input to sell over duration
balanceOut: totalAmountOut, // Total output expected
startTime: startTime, // uint40 — start of schedule
duration: duration, // uint40 — total schedule length (seconds)
priceBumpAfterIlliquidity: bumpBps, // uint64 — price improvement after a missed interval
minTradeAmountOut: minOut // uint256 — minimum output per tranche (dust guard)
})))
);
_twap encodes args via abi.encode (6 × 32 = 192 bytes).
Args (from TWAPSwap)
| Field | Type | Description |
|---|---|---|
balanceIn |
uint256 |
Total input amount over full duration |
balanceOut |
uint256 |
Total expected output |
startTime |
uint40 |
Schedule start (unix seconds) |
duration |
uint40 |
Total schedule length in seconds |
priceBumpAfterIlliquidity |
uint64 |
BPS rate improvement after a missed slot (BPS = 1e9) |
minTradeAmountOut |
uint256 |
Minimum output per tranche; reverts if the tranche is too small |
How it works
At execution time,
_twapcomputes the currently unlocked fraction ofbalanceInandbalanceOutbased on elapsed time.It runs a Dutch auction within the current tranche. The price improves within the slot while no fill has occurred.
priceBumpAfterIlliquiditymakes the next tranche more attractive when the previous one was skipped.Internal invalidation tracks cumulative fills; no separate invalidator instruction is needed.
DCA usage
To implement periodic DCA:
Set
durationto the total campaign length (for example, 30 days).Each execution fills the tranche for the current time slot.
The maker signs once; the program handles scheduling automatically.
Optional modifiers
| Goal | Add |
|---|---|
| Expiry | _deadline before _twap |
| Holder-gated execution | _onlyTakerTokenBalanceNonZero before _twap |
Related
TWAPSwap: instruction args and internal schedule math
Limit Order: simpler fixed-rate pattern
Dutch Auction: time-decay on a single tranche