Aggregator overview
What ArmoryTradeManager is, how venues are addressed by dexId, and what routing through it gives an integrator.
On this page
ArmoryTradeManager is the aggregator's single on-chain entry point. It takes one
trade at a time, denominated in native APE on one side and an ERC-20 on the other,
resolves a dexId through an append-only adapter registry, and hands the trade to
the adapter that owns that venue.
There are exactly two trading functions. buy is payable and swaps native APE into
a token. sell pulls a token from the caller and returns native APE. There is no
token-to-token entry point and no WAPE entry point. The APE side is what the
platform fee is taken from and what the accounting is measured in, so a trade
without an APE leg has no shape the manager can execute.
Buys encode WAPE → token. Sells encode token → WAPE.
The path in a sell Route runs from the token to WAPE. The path in a buy Route
runs from WAPE to the token. Both shipped adapters check the first and last token on
every venue kind, so reusing a buy path for a sell reverts with TokenMismatch
before any router is called.
This is the single most common way an integration breaks. It is not symmetric, it is not auto-detected, and there is no flag for it. See Path encoding.
Venues and dexIds
A dexId is a uint8 key into adapters. The launch registry is:
dexIds 0 and 1 are both served by ArmoryAdapter. dexIds 2 and 3 by
CamelotAdapter. One adapter contract may back several dexIds: it branches
internally on route.dex.
Treat the on-chain adapter registry as the authority for each dexId.
What routing through the manager buys you
- One approval, forever. Users approve the manager, not a router and not an adapter. Adapters never touch user allowances and hold no funds between transactions. When a venue is added, no user re-approves anything.
- One calldata shape across every venue.
Routeis the same tuple whether the trade lands on a constant-product pair, a Uniswap-style concentrated pool, or an Algebra pool. A new venue reinterprets the tuple. It never changes it. - Balance-delta accounting on the APE side. Sell proceeds are what actually arrived at the manager, not what a router claimed to send. Adapter return values are informational.
- Automatic refund of unconsumed APE on a buy, emitted as
EthRefunded. - A slippage floor the manager enforces itself, on top of whatever floor the venue applies.
- One event to index.
TradeExecutedcarries the venue, both sides of the trade, the fee and the referrer.
What it costs you: a platform fee on the APE side, and one extra hop of gas versus calling the venue's router directly. If you already have direct access to a pool the manager routes to, and you do not need the uniform surface, calling that router yourself is cheaper.
Trust model
addAdapteris owner-only and append-only. AdexIdthat has been bound can never be re-pointed.addAdapterreverts withAdapterExists(dexId). Trades on an existing route therefore carry exactly the trust assumptions that route had at the moment it was registered.- The only mutable routing bit is
dexPaused[dexId]. Pausing can disable a venue. It cannot redirect one. - Adapters are not owned and expose no admin surface. Every address an adapter
touches (its manager, WAPE, its routers, its Camelot referrer) is
immutable. Replacing venue plumbing means deploying a new adapter under a new dexId. - Ownership is
ApeOwnable.onlyOwnerreadsmanager.owner()live off a hardcodedIGovernanceManagerconstant, which is the platform multisig. The manager itself has noownerstorage slot and notransferOwnership.
The platform fee
ArmoryTradeManager charges a platform fee on the APE side of every trade. The rate
is a per-venue on-chain parameter: feeBpsFor(dexId) returns the venue's override if
one is set, otherwise the contract-wide default.
Read feeBpsFor live before you quote
feeBpsFor(dexId) is the authority on what a trade costs. It can change, so
read it on chain as part of the same batch used to quote the trade.
Where the fee lands is also configurable. feeReceiver is an ITradeFeeReceiver
implementation and is swappable by the owner. An optional IReferralManager can take
a share of the fee when a caller passes a non-zero ref. Both seams, and what a
third party may implement against them, are covered in
Hooks.
Addresses
See Hooks before integrating referralRouter. Every
other deployed address is in Addresses.
Check venue availability
Before quoting, confirm that the dexId has an adapter and is not paused. A
registered venue can still lack a pool or sufficient liquidity for the requested
pair, so handle that separately from UnknownDex and DexIsPaused.
import { zeroAddress } from "viem";
const [adapter, paused] = await Promise.all([
client.readContract({ address: tradeManager, abi, functionName: "adapters", args: [dexId] }),
client.readContract({ address: tradeManager, abi, functionName: "dexPaused", args: [dexId] }),
]);
const routable = adapter !== zeroAddress && !paused;Where to go next
- TradeManager reference for the full external ABI, every event and every revert.
- Path encoding for the byte layouts and the direction rule.
- Writing an adapter to add a venue.
- Integration recipes for working viem and Solidity.