Events & Interfaces

source-verified

This page is the canonical reference for the Aqua smart-contract event log and its Solidity interfaces. Every signature, event, and error below is copied verbatim from source: the registry surface from 1inch/aqua (src/interfaces/IAqua.sol, src/Aqua.sol, src/AquaApp.sol) and the router/swap surface from 1inch/swap-vm (src/SwapVM.sol, src/interfaces/ISwapVM.sol, src/interfaces/ITakerCallbacks.sol).

Two contracts, two event streams. The Aqua registry (AquaRouter, canonical deployment 0x1111113ccf1426a8e30e2bff5e005d929bf6a90a) emits Shipped / Docked / Pulled / Pushed for balance accounting. The swap router (AquaSwapVMRouter v1.0.2, 0x111111338c5091e8440b67b168bae16a668ac0de) emits Swapped for executed swaps — and, in the current build only, ProtocolFeeSkipped. They are separate addresses; index them independently. (These are the 2026-07 vanity deployments; the previous 0xe8026b…139b / 0x016b41…b070 pair is superseded — see Contract addresses.)

Events

No event on Aqua usesindexedparameters. Every field of Shipped, Docked, Pulled, Pushed — and Swapped — is a data field, not a topic. Consequently each log carries exactly one topic: topics[0] = the event's signature hash. You cannot pre-filter by maker, app, or token at the RPC eth_getLogs level. Filter by { address, topics[0] } and ABI-decode all fields out of the data blob, then match maker/app/token client-side.

Event Canonical signature (hash source) Indexed params Emitted by topic0 = keccak256(signature)
Shipped Shipped(address,address,bytes32,bytes) (maker, app, strategyHash, strategy) None — all 4 fields in data Aqua registry, on ship() 0xdc3622e06fb145651f567d421c9ef261d71d43e3778b761907bc0d70d42e52b0
Docked Docked(address,address,bytes32) (maker, app, strategyHash) None — all 3 fields in data Aqua registry, on dock() 0xd173a1d140c154eb1ce9298d251d5eb8c4089cc2d16e70f1067bdc810c6fe004
Pulled Pulled(address,address,bytes32,address,uint256) (maker, app, strategyHash, token, amount) None — all 5 fields in data Aqua registry, on pull() 0x3ad61047071575417c75e3311e5d46ff042e292b5dd8769ff18b4b254098ca7a
Pushed Pushed(address,address,bytes32,address,uint256) (maker, app, strategyHash, token, amount) None — all 5 fields in data Aqua registry, on push() 0x3f18354abbd5306dd1665c2c90f614a4559e39dd620d04fbe5458e613b6588f3
Swapped Swapped(bytes32,address,address,address,address,uint256,uint256) (orderHash, maker, taker, tokenIn, tokenOut, amountIn, amountOut) None — all 7 fields in data Swap router (SwapVM), on swap() 0x54bc5c027d15d7aa8ae083f994ab4411d2f223291672ecd3a344f3d92dcaf8b2
ProtocolFeeSkipped ProtocolFeeSkipped(bytes32,address,address,uint256) (orderHash, token, to, amount) None — all 4 fields in data Swap router, on swap() when an Aqua protocol fee could not be collected 0x0b295783a78ac3079d7d7eafbca862bd69a8961a5e6e5115ab301dcbc0536e97

These topic0 values were computed as keccak256 of the exact canonical signature strings in the second column (parameter names stripped, no spaces). They are verifiable independently: the same routine reproduces the well-known Transfer(address,address,uint256) hash 0xddf252ad…523b3ef. If you prefer to recompute, hash the exact strings shown — e.g. cast keccak "Shipped(address,address,bytes32,bytes)".

Correction to an earlier draft: Pulled has five fields — (maker, app, strategyHash, token, amount) — and does not carry a to recipient field in the event. The recipient is an argument to the pull(...) function but is not logged. Any decoder or ABI that adds a sixth to field to Pulled is wrong and will misalign the amount word.

ProtocolFeeSkipped exists only on the current router build (0x111111338c…c0de, deployed 2026-07-26 from the v1.0.2 git tag, declared in src/instructions/Fee.sol). It fires when a strategy charges an Aqua protocol fee that could not be collected: the swap proceeds anyway and the uncollected fee stays with the maker, so this log is the only signal that protocol revenue was skipped. Earlier routers (0x016b41…b070, v1.0.1) never emit it — do not scan for it in their history.

Field semantics

  • maker — the liquidity provider whose wallet balances are affected.
  • app — the app/strategy contract authorized to pull from the maker (the strategy's app implementation).
  • strategyHashbytes32 key identifying the shipped strategy. Returned by ship() and used as the lookup key everywhere.
  • strategy (Shipped only) — the full ABI-encoded strategy/program bytes (see Decoding the Shipped strategy bytes). Presented in full rather than pre-hashed specifically for data availability.
  • token / amount (Pulled/Pushed) — the ERC-20 moved and its raw amount. Pushed increases the maker's strategy balance; Pulled decreases it.

interface IAqua

Consolidated and copy-pasteable. Declarations are verbatim from 1inch/aqua/src/interfaces/IAqua.sol; NatSpec comments are trimmed for brevity — signatures, types, and mutability are unchanged.

Solidity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// SPDX-License-Identifier: LicenseRef-Degensoft-Aqua-Source-1.1
pragma solidity ^0.8.0;

interface IAqua {
    // ---- Errors ----
    error MaxNumberOfTokensExceeded(uint256 tokensCount, uint256 maxTokensCount);
    error StrategiesMustBeImmutable(address app, bytes32 strategyHash);
    error DockingShouldCloseAllTokens(address app, bytes32 strategyHash);
    error PushToNonActiveStrategyPrevented(address maker, address app, bytes32 strategyHash, address token);
    error SafeBalancesForTokenNotInActiveStrategy(address maker, address app, bytes32 strategyHash, address token);

    // ---- Events (no indexed params) ----
    event Shipped(address maker, address app, bytes32 strategyHash, bytes strategy);
    event Docked(address maker, address app, bytes32 strategyHash);
    event Pulled(address maker, address app, bytes32 strategyHash, address token, uint256 amount);
    event Pushed(address maker, address app, bytes32 strategyHash, address token, uint256 amount);

    // ---- Views ----
    function rawBalances(address maker, address app, bytes32 strategyHash, address token)
        external view returns (uint248 balance, uint8 tokensCount);

    function safeBalances(address maker, address app, bytes32 strategyHash, address token0, address token1)
        external view returns (uint256 balance0, uint256 balance1);

    // ---- State-changing ----
    function ship(
        address app,
        bytes calldata strategy,
        address[] calldata tokens,
        uint256[] calldata amounts
    ) external returns (bytes32 strategyHash);

    function dock(address app, bytes32 strategyHash, address[] calldata tokens) external;

    function pull(address maker, bytes32 strategyHash, address token, uint256 amount, address to) external;

    function push(address maker, address app, bytes32 strategyHash, address token, uint256 amount) external;
}

Note onrawBalances: it returns a packed (uint248 balance, uint8 tokensCount). A tokensCount of 0 means "never shipped"; a sentinel value (_DOCKED) means "docked / closed". Use safeBalances when you need the revert-on-inactive guarantee for two tokens at once.

App integration surface (AquaApp)

An Aqua "app" (the strategy implementation) inherits the abstract base AquaApp from 1inch/aqua/src/AquaApp.sol. This is the surface an app uses inside its own swap method to enforce that the taker actually pushed the promised tokens. It is not a standalone interface in source; the load-bearing members are reproduced verbatim below.

Solidity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
abstract contract AquaApp {
    // Reverts if strategy params don't match this app's address
    error InvalidAquaStrategy(address maker, bytes32 strategyHash, bytes32 salt, address app, address actualThis);
    // Reverts if taker hasn't pushed enough tokens to the maker
    error MissingTakerAquaPush(address token, uint256 newBalance, uint256 expectedBalance);
    // Reverts if _safeCheckAquaPush runs without reentrancy protection
    error MissingNonReentrantModifier();

    IAqua public immutable AQUA;

    // Wrap every swap method in this (or the equivalent explicit lock/unlock)
    modifier nonReentrantStrategy(address maker, bytes32 strategyHash);

    constructor(IAqua aqua);

    // Verify taker settlement; MUST be called inside nonReentrantStrategy
    function _safeCheckAquaPush(
        address maker,
        bytes32 strategyHash,
        address token,
        uint256 expectedBalance
    ) internal view;
}

Swap router interface (ISwapVM)

The router's external quote/execute surface, verbatim from the deployed source src/interfaces/ISwapVM.sol — identical in the v1.0.1 and v1.0.2 git tags of 1inch/swap-vm (the v1.0.2 tag, cut 2026-07-26, changed only the fee instruction). This is the interface the live router at 0x111111338c…c0de actually exposes — selector 0x44aa5f14 for quote, 0xf4d2d412 for swap. The taker names tokenIn and tokenOut explicitly, and that pair is the swap direction. quote() is a preview (safe to staticcall); swap() executes. Both return (amountIn, amountOut, orderHash).

Solidity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface ISwapVM {
    struct Order {
        address maker;
        MakerTraits traits;   // packed flags + receiver
        bytes data;           // encoded hooks data + program bytecode (no token prefix)
    }

    function hash(Order calldata order) external view returns (bytes32);

    function quote(
        Order calldata order,
        address tokenIn,
        address tokenOut,
        uint256 amount,
        bytes calldata takerTraitsAndData
    ) external view returns (uint256 amountIn, uint256 amountOut, bytes32 orderHash);

    function swap(
        Order calldata order,
        address tokenIn,
        address tokenOut,
        uint256 amount,
        bytes calldata takerTraitsAndData
    ) external returns (uint256 amountIn, uint256 amountOut, bytes32 orderHash);
}

The 1inch/swap-vm main branch has refactored this surface (dropping the token args and moving the pair into order.data), but that revision is not deployed — the newest release tag is v1.0.2 (cut 2026-07-26), which keeps the 5-argument surface unchanged. Build against the 5-argument signature above; it matches the on-chain router and the published @1inch/swap-vm-sdk (0.4.0).

For Aqua orders, hash(order) returns keccak256(abi.encode(order)) (no EIP-712 domain); for signature orders it returns the EIP-712 typed hash. This orderHash is the value emitted in Swapped.

Taker swap-callback (ITakerCallbacks)

A taker contract may implement these to be invoked during settlement, verbatim from 1inch/swap-vm/src/interfaces/ITakerCallbacks.sol.

Solidity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface ITakerCallbacks {
    // Called before tokenIn is transferred from taker to maker
    function preTransferInCallback(
        address maker, address taker,
        address tokenIn, address tokenOut,
        uint256 amountIn, uint256 amountOut,
        bytes32 orderHash, bytes calldata takerData
    ) external;

    // Called before tokenOut is transferred from maker to taker
    function preTransferOutCallback(
        address maker, address taker,
        address tokenIn, address tokenOut,
        uint256 amountIn, uint256 amountOut,
        bytes32 orderHash, bytes calldata takerData
    ) external;
}

Custom errors

Verbatim from source, with the exact revert condition. Registry errors are declared in IAqua and reverted in Aqua.sol; app errors are declared and reverted in AquaApp.sol; router errors in SwapVM.sol.

Error Declared in Reverts when
MaxNumberOfTokensExceeded(uint256 tokensCount, uint256 maxTokensCount) IAqua A strategy is shipped with more tokens than allowed (tokensCount collides with the _DOCKED sentinel). maxTokensCount = _DOCKED - 1.
StrategiesMustBeImmutable(address app, bytes32 strategyHash) IAqua ship() targets a (app, strategyHash) that already exists (tokensCount != 0). Strategies cannot be modified once shipped.
DockingShouldCloseAllTokens(address app, bytes32 strategyHash) IAqua dock() is called with a tokens array whose length ≠ the strategy's token count, i.e. it would not close every token.
PushToNonActiveStrategyPrevented(address maker, address app, bytes32 strategyHash, address token) IAqua push() targets a strategy that is not active — never shipped (tokensCount == 0) or already docked (tokensCount == _DOCKED).
SafeBalancesForTokenNotInActiveStrategy(address maker, address app, bytes32 strategyHash, address token) IAqua safeBalances() is queried for a token whose tokensCount is 0 or _DOCKED (token not part of an active strategy).
InvalidAquaStrategy(address maker, bytes32 strategyHash, bytes32 salt, address app, address actualThis) AquaApp Strategy parameters resolve to an app address that is not the executing contract (address(this)).
MissingTakerAquaPush(address token, uint256 newBalance, uint256 expectedBalance) AquaApp _safeCheckAquaPush finds the maker's post-swap balance below the minimum expected — the taker underpaid.
MissingNonReentrantModifier() AquaApp _safeCheckAquaPush is called outside a reentrancy-locked context (the nonReentrantStrategy guard is not held).
BadSignature(address maker, bytes32 orderHash, bytes signature) SwapVM A signature-based order fails ECDSA recovery to maker.
AquaBalanceInsufficientAfterTakerPush(uint256 balance, uint256 preBalance, uint256 amount, uint256 amountNetPulled) SwapVM Post-push Aqua balance accounting for an Aqua order does not reconcile.
MakerTraitsUnwrapIsIncompatibleWithAqua() SwapVM An Aqua order sets shouldUnwrapWeth — unsupported for Aqua orders.
MakerTraitsCustomReceiverIsIncompatibleWithAqua() SwapVM An Aqua order sets a custom receiver — unsupported for Aqua orders.

Decoding the Shipped strategy bytes

The strategy field in Shipped is the full ABI-encoded strategy passed to ship() — deliberately logged in full (not pre-hashed) so that anyone can reconstruct a maker's program from logs alone (data availability). It encodes an ISwapVM.Order-shaped program whose data tail is the strategy bytecode: a sequence of VM instructions the swap router executes.

  • To interpret the bytecode, map each opcode to its handler in the router's opcode set. For the Aqua router this is AquaOpcodes (1inch/swap-vm/src/opcodes/AquaOpcodes.sol), which dispatches opcodes such as 10–16 (Controls: jump/deadline/balance guards), 17 (XYC swap), 18 (XYC concentrate), 19 (Decay), 21/27–30 (Fee variants), 31 (PeggedSwap), 32 (Extruction). Instruction bodies live under src/instructions/.
  • The opcode table is versioned. Opcode numbers are assigned by the deployed router build and new instructions are appended to preserve backward compatibility — so a given opcode byte only has meaning relative to the exact router version that will execute the strategy. Decode against the opcode table of the specific AquaSwapVMRouter deployment the strategy targets, not a generic table. In particular, never derive the live table from the undeployed main branch: main's opcode enum has been renumbered (no reserved gaps; unknown opcodes revert UnknownOpcode(uint256)) and is not deployed anywhere — the deployed v1.0.x builds are the only valid reference. The v1.0.2 tag (the current router's source) left AquaOpcodes.sol untouched relative to v1.0.1, so the opcode table is identical across all deployed v1.0.x routers.

Several router builds are live-looking on-chain. The canonical router is AquaSwapVMRouter v1.0.2 at 0x111111338c5091e8440b67b168bae16a668ac0de (the 2026-07-26 vanity deployment, built from the v1.0.2 git tag). Superseded but still live, decode-only: 0x1111113db0e0ef9d0e3a50d5f094a3a57a26c0de (first vanity router, 2026-07-19 – 2026-07-26), 0x016b417bc933370f5eacc40b1d58b015ac72b070 (2026-07-16 universal deploy, v1.0.1 source recompiled), and 0x3c4758979ec30ca45857cabc2462a70699ed790e (v1.0.1, superseded 2026-07-16). The older 0x8fdd04dbf6111437b44bbca99c28882434e0958f is a stale build with a different opcode layout — do not decode current strategies against it.

Read paths / monitoring

Enumerate a maker's strategies

There is no on-chain registry enumeration. Reconstruct a maker's active strategies from the log stream:

  1. eth_getLogs on the Aqua registry address filtered by topics[0] = Shipped topic0. Because maker is not indexed, you must decode each log's data and keep those where maker matches.
  2. Subtract strategies that later emitted Docked (match on maker, app, strategyHash) to get the currently-active set.
  3. For live balances of an active strategy, call rawBalances(maker, app, strategyHash, token) (or safeBalances for a token pair).

Poll vs subscribe

  • Subscribe (eth_subscribe / logs) for near-real-time updates on Pulled/Pushed/Swapped. Remember all fields are in data; your subscription filter can only pin address + topics[0], so decode-and-match client-side.
  • Poll (eth_getLogs over block ranges) for backfill and for chains/providers without reliable subscriptions. Chunk block ranges to stay under provider log limits and dedupe on (blockNumber, logIndex).

Backfill needs a per-chain deployment block

To backfill the full history you must start eth_getLogs at the block where the Aqua registry (and, separately, the swap router) was deployed on that chain. This deployment block is per-deployment and differs on every chain — it is not encoded in this doc and must be looked up from the deployment artifacts / block explorer for each of the 13 Aqua chains. Do not assume a shared or zero start block; scanning from genesis is unnecessary and scanning from the wrong block will silently drop early strategies.

orderHash == strategyHash. The Swapped.orderHash emitted by the router is the same 32-byte value as the strategy identifier returned by ship() and logged in Shipped as strategyHash. Join fills to strategies on this equality exactly: Swapped.orderHash === Shipped.strategyHash (this is why analytics can attribute volume by that key).

Per-chain deployment blocks

The AquaSwapVMRouter v1.0.1 (superseded 2026-07-16 by 0x016b417bc933370f5eacc40b1d58b015ac72b070, in turn superseded by the canonical vanity router 0x111111338c5091e8440b67b168bae16a668ac0de on 2026-07-26) was deployed at the same deterministic address on every supported chain:

0x3c4758979ec30ca45857cabc2462a70699ed790e

The table below lists the block in which the router creation transaction was mined on each of the 12 Aqua chains. Values are taken from the router deployment receipts in the 1inch/swap-vm broadcast artifacts (broadcast/__DeployPadCreate.s.sol/<chainId>/run-latest.json) and converted from hex to decimal.

Chain Chain ID Router deploy block (decimal)
Ethereum 1 25330939
Optimism 10 153012311
BNB Chain 56 104589422
Gnosis 100 46728143
Unichain 130 50875110
Polygon 137 88612289
Sonic 146 74069677
zkSync Era 324 70817618
Base 8453 47416981
Arbitrum 42161 474133160
Avalanche 43114 88164944
Linea 59144 31054397

When indexing router logs, start your eth_getLogs range at the deploy block shown above for each chain rather than from genesis. No router events exist before this block, so scanning earlier ranges wastes provider calls and returns nothing.

The table above covers the v1.0.1 router only. Each later router generation has its own, later deploy blocks — per chain, read them from the contract's creation transaction on the block explorer. On Ethereum: the current vanity router 0x111111338c…c0de was created in block 25618917 (2026-07-26) and the current registry 0x1111113ccf…a90a in block 25567141 (2026-07-19), both per Etherscan/Blockscout creation transactions. Do not reuse the v1.0.1 blocks for the current deployment: they are weeks earlier and will waste scans (though they return nothing rather than wrong data).

Registry deploy block

The Aqua registry deployment block is not present in the public 1inch/swap-vm broadcast artifacts, which cover only the router. To index registry events, read the registry contract's own creation transaction on each chain (for example, look up the contract address and inspect the block of its creation transaction) and begin your eth_getLogs range from that block. Do not assume it matches the router deploy block — determine it per chain from the registry's creation transaction rather than guessing.

Function-selector reference

Every function on the deployed contracts is dispatched by its 4-byte selector — bytes4(keccak256(canonicalSignature)). The tables below list the selectors for the AquaSwapVMRouter v1.0.2 swap surface and the Aqua registry (IAqua) surface, so any transaction or trace can be decided against the chain by matching calldata[0:4]. The canonical signature column is the exact string hashed: struct arguments are expanded to their ABI tuple form and the MakerTraits user-defined value type resolves to its underlying uint256.

Order.traitsis theMakerTraitstype — auint256user-defined value type (type MakerTraits is uint256;, 1inch/swap-vm/src/libs/MakerTraits.sol). For ABI-encoding and selector computation it is indistinguishable from uint256, so the Order struct (address maker, MakerTraits traits, bytes data) hashes as the tuple (address,uint256,bytes). This same statement holds identically wherever Order appears in this reference.

Router selectors — AquaSwapVMRouter v1.0.2

Both entry points take the same five arguments — (Order order, address tokenIn, address tokenOut, uint256 amount, bytes takerTraitsAndData) — verbatim from 1inch/swap-vm/src/interfaces/ISwapVM.sol, identical at the v1.0.1 and v1.0.2 tags. quote() is a view preview; swap() executes. Router at 0x111111338c5091e8440b67b168bae16a668ac0de.

Function Selector Canonical signature (hashed)
quote 0x44aa5f14 quote((address,uint256,bytes),address,address,uint256,bytes)
swap 0xf4d2d412 swap((address,uint256,bytes),address,address,uint256,bytes)

These are the five-argument v1.0.2 signatures — Order, tokenIn, tokenOut, amount, takerTraitsAndData. A three-argument quote/swap(Order,uint256,bytes) hashes to different selectors and does not match the deployed router; decode calldata against the selectors above.

Registry selectors — Aqua (IAqua)

Selectors for the registry surface, computed from the signatures in 1inch/aqua/src/interfaces/IAqua.sol at v1.0.0 (the current Aqua release tag). Registry at 0x1111113ccf1426a8e30e2bff5e005d929bf6a90a.

Function Selector Canonical signature (hashed) Mutability
ship 0xf50b870f ship(address,bytes,address[],uint256[]) nonpayable
dock 0x28defc17 dock(address,bytes32,address[]) nonpayable
pull 0xb00bbd10 pull(address,bytes32,address,uint256,address) nonpayable
push 0x47d72768 push(address,address,bytes32,address,uint256) nonpayable
rawBalances 0x6d58b4cc rawBalances(address,address,bytes32,address) view
safeBalances 0x65f2fe14 safeBalances(address,address,bytes32,address,address) view

To recompute any selector, hash the exact string in the canonical signature column and take the first 4 bytes — e.g. cast sig "ship(address,bytes,address[],uint256[])" returns 0xf50b870f. The same routine reproduces the well-known Transfer(address,address,uint256) topic hash, so the values are independently verifiable.

Did you find what you need?