// Developers
Integration
How to connect RouterX to an existing operator stack — liquidation, arbitrage, or other strategies — from contract addresses and approvals to executing a gas-aware route on-chain.
Architecture#
RouterX is composed of three layers. The Router contract is the single entrypoint you call to settle a trade. Behind it, Adapters normalize each DEX into a common swap interface, and the Quoter computes gas-adjusted paths across those adapters.
- Router — validates the route, pulls input tokens, executes adapter calls, and enforces the minimum output.
- Adapters — thin, audited wrappers for Uniswap, Curve, Balancer, PancakeSwap, and more.
- Quoter — an off-chain quoting service and on-chain lens that both return identical, executable routes.
Supported chains#
| Chain | Chain ID | Swap | Cross | Limit | DCA |
|---|---|---|---|---|---|
| Ethereum | 1 | ||||
| Avalanche | 43114 | ||||
| Arbitrum One | 42161 | ||||
| Optimism | 10 | ||||
| Base | 8453 | ||||
| BNB Chain | 56 | — | |||
| Polygon PoS | 137 | ||||
| Robinhood Chain | 4663 | — | |||
| HyperEVM | 999 | ||||
| World Chain | 480 | ||||
| Monad | 143 | ||||
| Katana | 747474 | — | |||
| Unichain | 130 | ||||
| Plasma | 9745 | — | |||
| Arc | 5042 |
Arc swap is ERC-20 only (no native wrap). Limit and DCA share the same chain set.
0x1abe089620a6f3E3Ff802f2165AB8dbC10cd5CE8 on every chain via CREATE2, so you can hardcode a single address in cross-chain systems. Limit-order contracts use the same CREATE2 address on every chain. CrossComposer is CREATE2-identical on the 11 Composer source chains (Ethereum, Optimism, Unichain, Polygon, Base, Arbitrum, Avalanche, Monad, World Chain, HyperEVM, and Arc — not BSC, Robinhood, Katana, or Plasma).Contract addresses#
The canonical deployment addresses. Router and limit-order contracts are identical on every chain via CREATE2. CrossComposer is CREATE2-identical on the 11 Composer source chains. Always verify against the on-chain source before approving tokens. Katana and Plasma have the router and limit-order protocol but not CrossComposer. Unichain and Arc have the router, limit orders, and CrossComposer; Arc Wave A is ERC-20 only (no native wrap).
| Contract | Address |
|---|---|
| Router (UUPS proxy) | 0x1abe089620a6f3E3Ff802f2165AB8dbC10cd5CE8 |
| Limit Order Protocol | 0x330d568D8b159dd80e8c625e194d70C70CA11113 |
| Limit Order Bootstrap | 0xF4B6Bf4a747Ce68D42D8D828aD096E0DDB20000C |
| WETH Unwrapper | 0xEcC3717a8B374F0bcC03f6844C1236C052812622 |
| CrossComposer | 0xe1426503902f201f922eDB45c7c2002D10502029 |
CrossComposer is live on 11 CCTP ∩ swap source chains (including Unichain and Arc). Cross quotes run in the SDK (rx.cross.quote); transfer history is served by https://cross-api.routerx.exchange (routerx-cross) via rx.cross.
Integration flow#
Every integration follows the same four steps:
- Quote — request the best gas-adjusted route for your trade.
- Approve — grant the Router an allowance for the input token (once per token, or use Permit2).
- Simulate — confirm the route still executes at the target block.
- Settle — submit the route calldata to the Router.
Approvals#
The Router pulls the input token via transferFrom, so it needs an ERC-20 allowance. You can either approve the token directly or use Permit2 to sign an off-chain approval and save a transaction.
import { erc20Abi, maxUint256 } from 'viem'
// One-time approval of the Router for WETH
await wallet.writeContract({
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
abi: erc20Abi,
functionName: 'approve',
args: ['0x1abe089620a6f3E3Ff802f2165AB8dbC10cd5CE8', maxUint256],
})Scope your allowances
For hot wallets running unattended, prefer per-trade Permit2 signatures over infinite approvals to limit exposure if a key is compromised.On-chain settlement#
Settlement is a call to the CREATE2 router (0x1abe…5CE8) — typically swap, swapFromAVAX, or swapToAVAX with a Trade built from the quote offer. Prefer rx.swap.swap / buildSwapTx or GET /v1/:chain/swap so you do not hand-encode calldata.
struct FormattedOffer {
uint256[] amounts;
address[] adapters;
address[] path;
uint256 gasEstimate;
bytes[] extras; // aligned with adapters; pass to hops[i].extra
}
function findBestPathWithGas(
uint256 amountIn,
address tokenIn,
address tokenOut,
uint256 maxSteps, // must be < 4
uint256 gasPrice // gwei, for gas-aware scoring
) external view returns (FormattedOffer memory);
struct Hop {
address adapter;
address tokenOut;
bytes extra;
}
struct SplitLeg {
uint256 split; // bps of post-fee amount; last leg takes remainder
Hop[] hops;
}
struct Trade {
uint256 amountIn;
uint256 amountOut; // min out enforced on-chain
address tokenIn;
SplitLeg[] legs;
}
function swap(Trade calldata trade, address to, uint256 fee) external;
function swapFromAVAX(Trade calldata trade, address to, uint256 fee) external payable;
function swapToAVAX(Trade calldata trade, address to, uint256 fee) external;
function swapWithPermit2(
Trade calldata trade, address to, uint256 fee,
uint256 nonce, uint256 deadline, bytes calldata signature
) external;
function swapToAVAXWithPermit2(
Trade calldata trade, address to, uint256 fee,
uint256 nonce, uint256 deadline, bytes calldata signature
) external;
function queryExactPath(
uint256 amountIn,
address[] calldata tokens,
uint8[] calldata adapterIdx
) external view returns (FormattedOffer memory);Zip a 1-leg quote into Trade by pairing each adapter with path[i + 1] and extras[i]. Multi-path trades set legs.length > 1; each split is a share of post-fee input out of 1e4, and the last leg receives the remainder.
FormattedOffer memory offer = router.findBestPathWithGas(
amountIn, tokenIn, tokenOut, maxSteps, gasPrice
);
Hop[] memory hops = new Hop[](offer.adapters.length);
for (uint256 i; i < hops.length; ++i) {
hops[i] = Hop({
adapter: offer.adapters[i],
tokenOut: offer.path[i + 1],
extra: offer.extras[i]
});
}
SplitLeg[] memory legs = new SplitLeg[](1);
legs[0] = SplitLeg({ split: 0, hops: hops });
router.swap(
Trade({
amountIn: offer.amounts[0],
amountOut: offer.amounts[offer.amounts.length - 1],
tokenIn: offer.path[0],
legs: legs
}),
recipient,
feeBps
);Native input uses swapFromAVAX with msg.value = amountIn. ERC-20 input needs an allowance to the router (or Permit2 via swapWithPermit2). For a caller-specified route that skips path search, use queryExactPath (tokens.length == adapterIdx.length + 1, hops in 1–4) then the same zip into Trade.
Production checklist#
- Verify contract addresses against on-chain bytecode before approving.
- Always pass a tight deadline (seconds, not minutes) for MEV-sensitive flows.
- Simulate the route in the same block you intend to land in.
- Set minAmountOut from your own model, not only from the quote.
- Monitor the Adapter Registry for newly added or paused venues.
Ready to script it? Head to the SDK or read the raw API reference.