// 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#

ChainChain IDSwapCrossLimitDCA
Ethereum1
Avalanche43114
Arbitrum One42161
Optimism10
Base8453
BNB Chain56—
Polygon PoS137
Robinhood Chain4663—
HyperEVM999
World Chain480
Monad143
Katana747474—
Unichain130
Plasma9745—
Arc5042
Elysium99901———

Arc swap is ERC-20 only (no native wrap). Elysium is swap-only. Limit and DCA share the same chain set.

The router proxy (0x1abe089620a6f3E3Ff802f2165AB8dbC10cd5CE8) and the quoter (0x27fe493F8A4F02e3049080d6aBD96646C7B1a666) are each one CREATE2 address on every chain. Quotes go to the quoter; swaps go to the router. Limit-order contracts use the same CREATE2 address on every limit-order chain (all except Elysium). 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, Plasma, or Elysium).

Contract addresses#

The canonical deployment addresses. Router, quoter, 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). Elysium has the router and quoter only.

ContractAddress
Router (UUPS proxy)0x1abe089620a6f3E3Ff802f2165AB8dbC10cd5CE8
Quoter0x27fe493F8A4F02e3049080d6aBD96646C7B1a666
Limit Order Protocol0x330d568D8b159dd80e8c625e194d70C70CA11113
Limit Order Bootstrap0xF4B6Bf4a747Ce68D42D8D828aD096E0DDB20000C
WETH Unwrapper0xEcC3717a8B374F0bcC03f6844C1236C052812622
CrossComposer0x65d60FC7F806A53A3B112D56E97e3D7ab51cb541

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.

approve.ts
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#

Quotes are eth_calls to the CREATE2 quoter (0x27fe…a666). 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. To keep the RPC on your node, GET /v1/:chain/quotePlan returns Multicall3 calldata ranked by per-adapter USD reserve; an empty or all-failing plan means no route. The ABIs to decode it are in the API reference. POST the winning offer, or legswhen the plan's split probes beat it, to /v1/:chain/swap. That response matches GET /swap and sets quoteMode to "client".

RXQuoter.sol
// RXQuoter 0x27fe493F8A4F02e3049080d6aBD96646C7B1a666 — view only
struct FormattedOffer {
    uint256[] amounts;
    address[] adapters;
    address[] path;
    uint256   gasEstimate;
    bytes[]   extras;      // aligned with adapters; pass to hops[i].extra
}

function query(
    uint256 amountIn,
    address tokenIn,
    address tokenOut,
    uint256 maxSteps,             // 1–4
    address[] calldata hopTokens, // intermediates for this call; empty = direct only
    uint256 gasPrice              // gwei; 0 ranks by amountOut
) external view returns (FormattedOffer memory);

function queryExactPath(
    uint256 amountIn,
    address[] calldata tokens,
    uint8[] calldata adapterIdx
) external view returns (FormattedOffer memory);

// RXRouter 0x1abe089620a6f3E3Ff802f2165AB8dbC10cd5CE8 — execution
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;

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.

The router takes its fee from amountIn before the legs run, so a quote for the full amount overstates the output. Set Trade.amountOut to amountOut × (1 − feeBps / 1e4) × (1 − slippage), where feeBps is your whitelisted fee, or DEFAULT_FEE (10 bps) if you are not on the fee whitelist.

swap.sol
address[] memory hopTokens = new address[](0); // or your intermediate tokens
FormattedOffer memory offer = quoter.query(
    amountIn, tokenIn, tokenOut, maxSteps, hopTokens, 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
);

hopTokens replaces on-chain TRUSTED_TOKENS for that call; it is not a union. An empty array quotes the direct route only. maxSteps is 1–4. gasPrice is gwei; 0 ranks by amountOut and still fills gasEstimate. 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, call queryExactPath on the quoter (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.