Cross-chain swaps let you convert tokens across different blockchain networks in a single transaction. They're built natively into Smart Wallets and you can integrate in minutes.
Cross-chain swaps work just like any other Smart Wallet transaction, so you can sponsor gas to do gasless swaps, or pay for gas in an ERC-20 token.
Cross-chain swaps are in alpha. Note that there may be changes in the future to simplify the endpoint/sdk. We will let you know if/when that happens.
- Request a cross-chain swap quote
- Sign the prepared swap calls
- Send prepared calls
- Wait for cross-chain confirmation
Important: Cross-chain swaps do not support postCalls. You cannot batch
additional actions after a cross-chain swap completes (for now).
When requesting a cross-chain swap quote, you can specify either a fromAmount , or a minimumToAmount.
// Mode 1: Swap exact input amount
{
// Swap exactly 0.01 USDC (10000 in hex, 6 decimals)
fromAmount: "0x2710";
}
// Mode 2: Get minimum output amount
{
// Get at least 0.0001 ETH (18 decimals). The amount you need to spend is calculated to get at least your desired ETH amount.
minimumToAmount: "0x5AF3107A4000";
}Before you begin, ensure you have:
- An Alchemy API Key
- If you're sponsoring gas, then a Gas Manager policy
- A small amount of tokens for testing (~$1 worth is enough!)
- Important: You'll need to send these tokens to your smart wallet address to be able to swap!
- A signer to own the account and sign messages
Important: Cross-chain swaps do not support postCalls. You cannot batch
additional actions after a cross-chain swap completes.
You'll need the following env variables:
ALCHEMY_API_KEY: An Alchemy API KeyALCHEMY_POLICY_ID: A Gas Manager policy IDPRIVATE_KEY: A private key for a signer
import { swapActions } from "@account-kit/wallet-client/experimental";
import { client, signer } from "./client";
// Add the swap actions to the client
const swapClient = client.extend(swapActions);
// Request the cross-chain swap quote
// Note: toChainId specifies the destination chain for the swap
const { quote, callId, ...calls } = await swapClient.requestQuoteV0({
from: await signer.getAddress(), // Your wallet address
toChainId: "0x...", // Destination chain ID
fromToken: "0x...",
toToken: "0x...",
minimumToAmount: "0x...",
});
// Display the swap quote, including the minimum amount to receive and the expiry
console.log(quote);
console.log(`Cross-chain swap callId: ${callId}`);
// Assert that the calls are not raw calls.
// This will always be the case when requestQuoteV0 is used without the `returnRawCalls` option,
// the assertion is just needed for Typescript to recognize the result type.
if (calls.rawCalls) {
throw new Error("Expected user operation calls");
}
// Sign the quote, getting back prepared and signed calls
// The callId is automatically included in the signed calls
const signedCalls = await swapClient.signPreparedCalls(calls);
// Send the prepared calls
// The callId is passed through automatically
const { id } = await swapClient.sendPreparedCalls(signedCalls);
// Wait for the call to resolve
// Cross-chain swaps may take longer due to cross-chain messaging
const callStatusResult = await swapClient.waitForCallsStatus({
id,
});
// Filter through success or failure cases
// Cross-chain swaps have additional status codes:
// - 120: Cross-Chain In Progress
// - 410: Cross-chain Refund
if (
callStatusResult.status !== "success" ||
!callStatusResult.receipts ||
!callStatusResult.receipts[0]
) {
throw new Error(
`Cross-chain swap failed with status ${callStatusResult.status}, full receipt:\n ${JSON.stringify(callStatusResult, null, 2)}`,
);
}
console.log("Cross-chain swap confirmed!");
console.log(
`Transaction hash: ${callStatusResult.receipts[0].transactionHash}`,
);Chains supported (for now) are: Arbitrum, Arbitrum Nova, Base, Berachain, Boba Network, BSC/BNB, Celo, Ethereum, Hyperliquid, Ink, Optimism, Plasma, Polygon, Shape, Soneium, Story, Unichain, World Chain, and Zora mainnets.
No, postCalls are not supported for cross-chain swaps (for now). You can only perform the swap itself across chains.
Cross-chain swaps typically take longer than single-chain swaps due to the need for cross-chain messaging and confirmation. The exact time depends on the source and destination chains involved in the swap.
Values are simply passed as hexadecimal strings. The Swap API doesn't add complexity to consider decimals, so 0x01 is always the smallest amount of a given asset.
1 ETH, or DAI (18 decimals) is 0xDE0B6B3A7640000
1 USDC (6 decimals) is 0xF4240
This removes any ambiguity— if it's numerical, it's a hex.
The expiry is an informational indicator of when you can expect to be able to process the swap request. If you're at/near the expiry, it might be a good time to request a new quote.
Cross-chain swaps may have additional status codes beyond standard transaction statuses to reflect the cross-chain nature of the transaction. These are:
- 120: Cross-chain in progress
- 410: Cross-chain refund
Any time you’re requesting a cross-chain quote via wallet_requestQuote_v0 , a callId is returned. This callId includes important data for cross-chain tracking. You can use this just like any other callId in wallet_getCallsStatus!