Integrate ARCH
Addresses, how to find ARCH pools, how to quote them, how to swap them, what the events mean. For routers, aggregators, trading terminals and indexers on Arc.
01 What you are integrating
ARCH is bin liquidity, one price per bin with a fixed step between bins, built as a Uniswap v4 hook. Every ARCH pool is an ordinary pool in Uniswap’s own PoolManager on Arc. The pool key names the hook, the PoolManager calls it, and the hook prices the swap against its bins. Any router that speaks v4 trades ARCH pools today with no integration work. What follows is what to know so that a pool is not skipped by mistake, and how to read what it does.
Every ARCH pool is quoted in USDC. The other side is any ERC-20. A pool between two non-USDC tokens does not exist here.
02 Addresses, Arc mainnet
| ArchHook | 0x8d70573B4181B265324886117d01015cBAa86A88 | The protocol. Bins, fees, swaps. |
| ArchPositionManager | 0x5519Fd8BD6E06D24bD2DDca9c91B91757A24BEf9 | ERC-721 positions. |
| ArchLens | 0xf6fA73c95B5BBD152Eae06130109C59AaE29fFd4 | Read-only: pools, bins, quotes, positions. |
| PoolManager | 0x8366a39CC670B4001A1121B8F6A443A643e40951 | Uniswap v4, canonical on Arc. Every ARCH pool lives here. |
| UniversalRouter | 0x4fcA4a51Ab4F23A7447b3284fBd7D73289A89Fb1 | Uniswap's. Executes ARCH swaps as it would any v4 pool. |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 | Allowances for the router, one per spender. |
| V4Quoter | 0xB337c45cf082c20c397476611a60c69Ee0d6d50F | Exact quotes for any v4 pool, hooked or not. |
| StateView | 0xB16b0088688B1c35132794A079C69975C661D49A | v4 core state. Reads zero liquidity for ARCH pools, see below. |
| ArchArb | 0x721f557a8545AF05204bD90d354d69773C4180d8 | The keeper. Its quote is public and exact. |
| USDC | 0x3600000000000000000000000000000000000000 | The ERC-20 predeploy, 6 decimals. Same balance as native USDC. |
| Deploy block | 21140557 | Scan from here for ARCH events. |
The same table as JSON: /api/contracts.
03 Finding pools
Two logs name every ARCH pool. The PoolManager emits Initialize for it like for any v4 pool; the hooks field in that log is the ArchHook address above, and it is not indexed, so filter on the data. The hook itself emits PoolCreated with the pool id, both currencies and the bin step:
PoolCreated(bytes32 indexed poolId, address indexed currency0, address indexed currency1, uint16 binStep, uint16 baseFactor, uint24 activeId, uint128 minBinLiquidity, bool invertForDisplay, address creator)
The v4 key of a pool is deterministic: currency0 and currency1 sorted by address, fee equal to baseFactor times binStep divided by 100, tickSpacing equal to the bin step, hooks equal to the ArchHook address. The hook answers it directly: poolKey(bytes32 poolId) returns the key. The pool id is the keccak256 of the ABI-encoded key, as in every v4 pool.
Arc refuses an eth_getLogs wider than about 6,000 blocks and caps a response at 2,000 logs. Scan in windows.
04 Quoting, and the one rule
Do not filter ARCH pools out on core liquidity. StateView.getLiquidity(poolId) reads zero for every ARCH pool, because the liquidity sits in the hook’s bins and not in v4’s own accounting. An integration that drops pools with zero core liquidity before quoting them drops every ARCH pool while it is full. Quote first, then decide.
Two ways to quote, both exact and both read-only in effect. Through the V4Quoter above, quoteExactInputSingle with the pool key, the direction, the amount and empty hook data; call it, never send it, and it works for any hook on Arc. Or through the lens, ArchLens.quoteExactInput(poolId, zeroForOne, amountIn), which also returns the fee split. Exact output is supported by both: the hook prices exact-in and exact-out swaps.
Depth: ArchLens.getActiveTvl(poolId, radius) returns what sits within a number of bins of the active bin, and getBinsAround returns the bins themselves. A quote that reverts means the pool runs out at that size.
05 Swapping
Through Uniswap’s UniversalRouter above, an ARCH swap is command V4_SWAP (0x10) with the actions SWAP_EXACT_IN_SINGLE (0x06), SETTLE_ALL (0x0c) and TAKE_ALL (0x0f). The router on Arc decodes the six-field ExactInputSingleParams: pool key, zeroForOne, amountIn, amountOutMinimum, minHopPriceX36, hookData. A five-field payload reverts inside the router with no reason data. Allowances go through Permit2, one per spender.
Through your own PoolManager.unlock, the swap is a plain swap(key, params, hookData) with empty hook data. Exact input is a negative amountSpecified, exact output a positive one. The price limit is not used by the hook but must be a valid bound. The delta the PoolManager returns is the whole result, hook included.
06 Fees
A pool has a base fee, which is baseFactor times binStep and is the same number the key carries in v4’s fee units, so 2500 is 0.25 %. On top of it sits a variable fee that rises with recent volatility and decays back. The protocol takes a flat 0.50 % of every input and a share of the LP fee. In the Swap event, fee is everything the trader paid above what entered the bins, protocolFee is the protocol’s whole take out of that, and amountIn minus fee is what the bins received.
07 Events
| PoolCreated | poolId, currency0, currency1, binStep, baseFactor, activeId, minBinLiquidity, invertForDisplay, creator | One per pool. |
| Swap | poolId, router, zeroForOne, amountIn, amountOut, fee, protocolFee, activeIdBefore, activeIdAfter, volatilityAccumulator, binsPriced, binsTraversed | One per swap. The volume event. |
| BinSwap | poolId, binId, zeroForOne, amountIn, amountOut, fee, lpFee | One per bin priced. Drives per-bin charts. |
| Deposit | poolId, tokenId, payer, binIds, amounts0, amounts1, shares | Liquidity in, per bin. |
| Withdraw | poolId, tokenId, recipient, binIds, amounts0, amounts1, sharesBurned | Liquidity out, per bin. |
| FeesClaimed | poolId, tokenId, recipient, amount0, amount1 | LP fees paid out. |
router in Swap is the PoolManager’s caller, which is the UniversalRouter on nearly every swap. It is not the trader. Count traders from the transaction’s sender.
08 Reading bins and positions
The lens is the read surface: getPool, getBin, getBinsAround, getBinRange, getActiveTvl, quoteExactInput, quoteExactOutput, previewDeposit, getPosition, getPositionsOf, getPriceFromId, getIdFromPrice. A bin id maps to a price by getPriceFromId(binStep, id), a 128.128 fixed-point number of currency1 per currency0; id 8388608 is price one. Positions are ERC-721 tokens on the position manager, each a contiguous range of bins in one pool.
09 The keeper
ArchArb closes the price gap between an ARCH pool and any other pool for the same pair, v4 or v3, in one transaction and with no capital, using v4’s flash accounting. It runs whenever the other pool moves and the ARCH pool has not, so an ARCH pool trades at the market price and its LPs earn a fee on every move of the main venue. quote(first, second, amountIn) is public and exact for anyone who wants the same number; arb is restricted to its owner. The source is in the repository under contracts/src/ArchArb.sol and anyone can deploy their own copy.
How the pools work for a trader or an LP is on the How it works page.