For the complete documentation index, see llms.txt. This page is also available as markdown.

Quickstart

Archived version

You are viewing Smart Routing Address v0.2.1 documentation. View the latest documentation.

A smart routing address is a deposit address that accepts funds on any supported chain and delivers them to a destination you choose. Your user can send tokens to it from any chain (even from a CEX), and the funds show up in their wallet on the chain your app runs on.

The API is deliberately small: you call createSmartRoutingAddress, then send funds to the address it returns. That's really it.

In this example, we will create a smart routing address that receives funds on Base and transfers them to a wallet on Arbitrum, then send a deposit and check its status.

Install packages

You will be using the following packages:

npm i @zerodev/smart-routing-address@0.2.6 viem
yarn add @zerodev/smart-routing-address@0.2.6 viem
pnpm add @zerodev/smart-routing-address@0.2.6 viem
bun add @zerodev/smart-routing-address@0.2.6 viem

Create a project

In v0.2.1 a project is only needed for fee sponsorship. You can skip this step and leave out config.baseUrl below.

  • Go to the ZeroDev dashboard and create a project.
  • Copy the project ID and create a .env file as follows:
ZERODEV_PROJECT_ID=<YOUR_PROJECT_ID_FROM_DASHBOARD>

Create a smart routing address

Import the SDK and the chains you want to route between:

import {
  createSmartRoutingAddress,
  createCall,
  FLEX,
  SMART_ROUTING_ADDRESS_SERVER_URL,
  SMART_ROUTING_ADDRESS_V0_2_1,
} from "@zerodev/smart-routing-address";
import { erc20Abi } from "viem";
import { arbitrum, base } from "viem/chains";

Define the calls that run on Arbitrum when funds arrive. Build them with createCall, using FLEX placeholders for the token address and amount, which aren't known until a deposit lands:

// Replace with your own address
const owner = "0xddED85de258cC7a33A61BC6215DD766E87a97070";

// Transfer whatever ERC20 arrives, in whatever amount, to `owner`
const erc20Call = createCall({
  target: FLEX.TOKEN_ADDRESS,
  value: 0n,
  abi: erc20Abi,
  functionName: "transfer",
  args: [owner, FLEX.AMOUNT],
});

// Send whatever native amount arrives to `owner`
const nativeCall = createCall({
  target: owner,
  value: FLEX.NATIVE_AMOUNT,
});

Then create the address. Funds sent to it on Base trigger the matching actions on Arbitrum:

const { smartRoutingAddress, estimatedFees } = await createSmartRoutingAddress({
  owner,
  destChain: arbitrum,
  // Source tokens (any ERC20 and ETH on Base)
  srcTokens: [
    { tokenType: "ERC20", chain: base },
    { tokenType: "NATIVE", chain: base },
  ],
  // Which calls run on Arbitrum for each token that arrives
  actions: {
    USDC: { action: [erc20Call], fallBack: [] },
    WRAPPED_NATIVE: { action: [erc20Call], fallBack: [] },
    NATIVE: { action: [nativeCall], fallBack: [] },
  },
  slippage: 5000,
  config: {
    version: SMART_ROUTING_ADDRESS_V0_2_1,
    // Only needed for fee sponsorship
    baseUrl: `${SMART_ROUTING_ADDRESS_SERVER_URL}/${process.env.ZERODEV_PROJECT_ID}`,
  },
});

console.log("Smart routing address:", smartRoutingAddress);

The options are:

  • owner is an address that is authorized to recover funds from the smart routing address, in case the smart routing address fails to execute the actions for whatever reason. Typically you would set this to your user's EOA wallet, but you could also set it to your own address if you want to recover funds for users.

  • destChain is the chain on which the actions run. This is presumably the chain that your app runs on.

  • srcTokens is a list of tokens that the smart routing address should be able to receive. Only tokens listed in srcTokens will be routed to the destination. Other tokens sent to the address will have to be manually recovered by the owner. See Supported Chains for what you can list here.

  • actions maps each token type to the calls that run on destChain when that token arrives. Every entry needs an action list and a fallBack list; pass an empty fallBack if you don't need one.

  • slippage is the maximum slippage that the user can expect. slippage is an integer, where 1 is equal to 0.01% (so 100 would mean 1% slippage).

  • config.version is the Smart Routing Address version your address is created against. Pass SMART_ROUTING_ADDRESS_V0_2_1. For the latest version, see the current docs.

  • config.baseUrl is only needed for fee sponsorship. Leave it out otherwise.

  • allowPartialRoutes is optional. When you list many source chains, one temporarily unavailable route fails the whole call by default. Set it to true to create the address anyway and drop the routes that aren't available.

The return value also includes estimatedFees, the estimated fee for each source token deposit.

Send funds to the address

Send any ERC20 or ETH on Base to smartRoutingAddress, from any wallet or exchange. No further calls to the SDK are needed. The funds are bridged and the matching actions run on Arbitrum automatically.

Only send listed tokens

Only tokens on chains listed in srcTokens are routed. Anything else sent to the address sits there until the owner recovers it through the portal.

Check the deposit status

Once a deposit lands, you can track it through the bridge and the execution on the destination chain:

import { getSmartRoutingAddressStatus } from "@zerodev/smart-routing-address";

const status = await getSmartRoutingAddressStatus({
  smartRoutingAddress,
});

console.log(status.deposits);

Each entry in deposits includes the source deposit, the bridge transaction, and the execution on the destination chain. See Fetching Status for the full response shape.

Congratulations! You just routed funds across chains with a single deposit. Look up owner on Arbiscan to see the funds arrive.

Next steps

FLEX placeholders

Because the exact token and amount aren't known until a deposit lands, createCall lets you leave holes that the router fills in at execution time:

  • FLEX.TOKEN_ADDRESS — the address of the token that arrived.
  • FLEX.AMOUNT — the ERC20 amount that arrived (net of fees).
  • FLEX.NATIVE_AMOUNT — the native amount that arrived.

Use these anywhere a token address or amount is expected — as a target, a value, or inside args.

Keep going

On this page