Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.rhinestone.dev/llms.txt

Use this file to discover all available pages before exploring further.

Warp supports two patterns for crosschain execution:
  1. Bridge and execute: transfer tokens to a destination chain and run arbitrary calls there in the same intent.
  2. Gas relay: execute calls on a destination chain without bridging any tokens. The relayer fronts gas on the destination and takes repayment from the user’s existing balance on the source chain.

Bridge and execute

Use this when the destination calls need tokens on that chain — for example, depositing into a vault, buying an NFT, or performing a swap. Provide tokenRequests for the tokens needed on the destination, and calls for the actions to execute there. Warp bridges the tokens and executes the calls in a single intent.
import { rhinestone } from '@rhinestone/sdk'
import { base, arbitrum } from 'viem/chains'
import { encodeFunctionData, parseUnits } from 'viem'

const usdcArbitrum = '0xaf88d065e77c8cC2239327C5EDb3A432268e5831'
const usdcAmount = parseUnits('100', 6)

const prepared = await rhinestoneAccount.prepareTransaction({
  sourceChains: [base],
  targetChain: arbitrum,
  tokenRequests: [
    {
      address: usdcArbitrum,
      amount: usdcAmount,
    },
  ],
  calls: [
    // Approve vault to spend USDC
    {
      to: usdcArbitrum,
      data: encodeFunctionData({
        abi: erc20Abi,
        functionName: 'approve',
        args: [VAULT_ADDRESS, usdcAmount],
      }),
    },
    // Deposit into vault
    {
      to: VAULT_ADDRESS,
      data: encodeFunctionData({
        abi: vaultAbi,
        functionName: 'deposit',
        args: [usdcAmount, rhinestoneAccount.getAddress()],
      }),
    },
  ],
})
const signed = await rhinestoneAccount.signTransaction(prepared)
const transaction = await rhinestoneAccount.submitTransaction(signed)
The calls execute in the context of the user’s account on the destination chain. If the vault returns receipt tokens, they are already credited to the account — no additional transfer is needed.

Gas relay

Use this when the user wants to execute a transaction on another chain but doesn’t need to bridge tokens. The user might have ETH or USDC on Base and want to call a contract on Arbitrum without holding any gas on Arbitrum. The relayer fronts gas on the destination chain and claims repayment from the user’s balance on the source chain. No tokenRequests needed — the repayment token is chosen automatically.
import { arbitrum } from 'viem/chains'

const transaction = await rhinestoneAccount.prepareTransaction({
  targetChain: arbitrum,
  calls: [
    {
      to: CONTRACT_ADDRESS,
      data: encodeFunctionData({
        abi: contractAbi,
        functionName: 'someFunction',
        args: [arg1, arg2],
      }),
    },
  ],
})
To restrict which chain the gas repayment is taken from, provide sourceChains:
import { base, arbitrum } from 'viem/chains'

const transaction = await rhinestoneAccount.prepareTransaction({
  sourceChains: [base],
  targetChain: arbitrum,
  calls: [
    {
      to: CONTRACT_ADDRESS,
      data: encodeFunctionData({
        abi: contractAbi,
        functionName: 'someFunction',
        args: [arg1, arg2],
      }),
    },
  ],
})

Source calls

You can also run executions on the source side, before the claim. Source calls are bundled into the intent at routing time and covered by the user’s mandate signature. Use them for pre-bridge actions on the source chain — ERC-20 approvals, ETH→WETH wraps, unstaking, or pulling funds out of a vault you need to spend.
import { base, arbitrum } from 'viem/chains'
import { encodeFunctionData, parseUnits } from 'viem'

const prepared = await rhinestoneAccount.prepareTransaction({
  sourceChains: [base],
  targetChain: arbitrum,
  tokenRequests: [
    { address: usdcArbitrum, amount: parseUnits('100', 6) },
  ],
  sourceCalls: {
    [base.id]: [
      {
        to: USDC_BASE,
        data: encodeFunctionData({
          abi: erc20Abi,
          functionName: 'approve',
          args: [VAULT_BASE, parseUnits('100', 6)],
        }),
      },
    ],
  },
})

Declaring tokens the source call provides

If the source call hands over tokens (an unwrap, an unstake, withdrawing from a vault), declare them via provides. Routing then treats those tokens as available, the same way auxiliaryFunds does:
sourceCalls: {
  [base.id]: [
    {
      to: VAULT_BASE,
      data: encodeFunctionData({ abi: vaultAbi, functionName: 'withdraw', args: [shares] }),
      provides: [
        { token: USDC_BASE, amount: parseUnits('100', 6) },
      ],
    },
  ],
},

Caveats

  • Source calls only fire when the orchestrator creates an element on that chain. Sponsored or no-op fills with no source movement skip the source element entirely, and the sourceCalls keyed on that chain are silently dropped.
  • The chain id must appear in sourceChains (cross-chain intents) or equal the same-chain chain.
  • For same-chain intents, source calls run before the destination calls on the same chain.

Next steps

Unified balance

Let users spend their full cross-chain balance in a single intent.

Multi-input bridge

Combine tokens from multiple chains into one destination transaction.