Skip to main content
Warp supports arbitrary input and output tokens. You don’t need to configure anything — if the token a user wants to send or receive isn’t a settlement token, the orchestrator automatically routes through a swap as part of the intent. Settlement tokens (the tokens Warp bridges natively) are: ETH, WETH, USDC, USDT, USDT0.
  • If the user is spending a non-settlement token on the origin chain, the orchestrator swaps it to a settlement token before bridging.
  • If the destination token is a non-settlement token, the orchestrator swaps the bridged settlement token to the requested token on arrival.
Swaps are only available on chains where the orchestrator has quoter support. On settlement-only chains (Plasma, HyperEVM, Soneium), input and output tokens must be settlement tokens.

Automatic swaps

Specify any token as the destination token. If it isn’t a settlement token, the swap is handled automatically.The example below sends DEGEN (Base) and receives OP (Optimism) — neither is a settlement token.
import { base, optimism } from 'viem/chains'
import { parseUnits } from 'viem'

// DEGEN on Base
const degenBase = '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed'
const degenAmount = parseUnits('1000', 18)

// OP token on Optimism
const opOptimism = '0x4200000000000000000000000000000000000042'
const opAmount = parseUnits('10', 18)

const receiver = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'

const transaction = await rhinestoneAccount.sendTransaction({
  sourceChains: [base],
  targetChain: optimism,
  calls: [
    {
      to: opOptimism,
      data: encodeFunctionData({
        abi: erc20Abi,
        functionName: 'transfer',
        args: [receiver, opAmount],
      }),
    },
  ],
  tokenRequests: [
    {
      address: opOptimism,
      amount: opAmount,
    },
  ],
})
The orchestrator will:
  1. Swap the user’s input token to a settlement token on the source chain
  2. Bridge the settlement token to the destination chain
  3. Swap to the requested output token on arrival
The token address in tokenRequests must be the address on the target chain.

Custom swap calldata

For specific routing logic on the destination chain, you can supply your own swap calldata. Fetch it from a DEX aggregator and include it as calls on the destination chain.Constraint: the final token received from your custom swap must be a settlement token. The orchestrator needs to settle in a token it recognises — if your swap outputs an arbitrary token, the intent cannot be fulfilled.The example below bridges to Base and executes a custom swap from WETH to cbBTC using an external aggregator.1. Fetch approval and swap calldata from your aggregator:
const walletAddress = rhinestoneAccount.getAddress()
const wethBase = '0x4200000000000000000000000000000000000006'
const wethAmount = parseEther('0.1')
const cbbtcBase = '0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf'

// Fetch from your preferred DEX aggregator API
const { approveTx, swapTx } = await fetchSwapCalldata({
  src: wethBase,
  dst: cbbtcBase,
  amount: wethAmount,
  from: walletAddress,
})
2. Submit as a crosschain transaction:
const transaction = await rhinestoneAccount.sendTransaction({
  sourceChains: [arbitrum],
  targetChain: base,
  calls: [
    { to: approveTx.to, data: approveTx.data, value: approveTx.value },
    { to: swapTx.to, data: swapTx.data, value: swapTx.value },
  ],
  tokenRequests: [
    { address: wethBase, amount: wethAmount },
  ],
})
This bridges from Arbitrum to get WETH on Base, then executes your custom swap to cbBTC.