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

# Execute crosschain calls

> Execute any onchain action on a destination chain, with or without bridging tokens.

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.

```ts theme={null}
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)
```

<Note>
  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.
</Note>

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

```ts theme={null}
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`:

```ts theme={null}
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.

```ts theme={null}
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:

```ts {6-8} theme={null}
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

<CardGroup cols={2}>
  <Card title="Unified balance" icon="wallet" href="../../smart-wallet/chain-abstraction/unified-balance">
    Let users spend their full cross-chain balance in a single intent.
  </Card>

  <Card title="Multi-input bridge" icon="arrow-right-left" href="../use-cases/multi-input-bridge">
    Combine tokens from multiple chains into one destination transaction.
  </Card>
</CardGroup>
