Same-chain swaps (Alpha)

Swaps let you convert any token to any other token onchain. They’re built natively into Smart Wallets and you can integrate in minutes.

Smart Wallets also allow you to add actions after the swap completes. For example, you can deposit your newly swapped tokens into a DeFi protocol.

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 live! Send your first cross-chain swap now!

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.

The Swap flow

Flow

  1. Request a swap quote
  2. Sign the prepared swap calls (including any post swap action)
  3. Send prepared calls
  4. Wait for onchain confirmation

Swap options

When requesting a swap quote, you can specify either an amount in, or a minimum amount out.

1// Mode 1: Swap exact input amount
2{
3 fromAmount: "0x2710";
4} // Swap exactly 0.01 USDC (10000 in hex, 6 decimals)
5
6// Mode 2: Get minimum output amount
7{
8 minimumToAmount: "0x5AF3107A4000";
9} // Get at least 0.0001 ETH (18 decimals). We calculate how much USDC you need to spend to get at least your desired ETH amount.

Prerequisites

Before you begin, ensure you have:

  • An Alchemy API Key
  • If you’re sponsoring gas, then a Gas Manager policy
  • A small amount of USDC 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
Required SDK version: ^v4.70.0

Use the usePrepareSwap hook to request swap quotes and the useSignAndSendPreparedCalls hook to execute token swaps on the same chain.

Prerequisites

swapTokens.tsx
1import {
2 useSmartAccountClient,
3 usePrepareSwap,
4 useSignAndSendPreparedCalls,
5 useWaitForCallsStatus,
6 useUser,
7} from "@account-kit/react";
8
9// Token addresses on Arbitrum
10const TOKENS = {
11 NATIVE: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEee", // ETH
12 USDC: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
13} as const;
14
15export default function SwapTokens() {
16 const user = useUser();
17 const { client } = useSmartAccountClient({
18 accountParams: { mode: "7702" },
19 });
20
21 const { prepareSwapAsync, isPreparingSwap } = usePrepareSwap({
22 client,
23 });
24
25 const {
26 signAndSendPreparedCallsAsync,
27 isSigningAndSendingPreparedCalls,
28 signAndSendPreparedCallsResult,
29 } = useSignAndSendPreparedCalls({ client });
30
31 const {
32 data: statusResult,
33 isLoading: isWaitingForConfirmation,
34 error,
35 } = useWaitForCallsStatus({
36 client,
37 id: signAndSendPreparedCallsResult?.preparedCallIds[0],
38 });
39
40 const handleSwap = async () => {
41 if (!client?.account.address) {
42 throw new Error("No account connected");
43 }
44
45 try {
46 // Step 1: Request swap quote
47 const result = await prepareSwapAsync({
48 from: client.account.address,
49 fromToken: TOKENS.NATIVE,
50 toToken: TOKENS.USDC,
51 fromAmount: "0x5af3107a4000", // 0.0001 ETH
52 // postCalls: [{
53 // to: "0x...",
54 // data: "0x...",
55 // value: "0x0"
56 // }], // Optional: batch additional calls after the swap
57 });
58
59 const { quote, ...calls } = result;
60 console.log("Swap quote:", quote);
61
62 // Ensure we have user operation calls
63 if (calls.rawCalls) {
64 throw new Error("Expected user operation calls");
65 }
66
67 // Step 2: Sign and send the prepared calls
68 const callIds = await signAndSendPreparedCallsAsync(calls);
69
70 console.log("Transaction sent");
71 console.log("Call ID:", callIds?.preparedCallIds[0]);
72 } catch (error) {
73 console.error("Swap failed:", error);
74 }
75 };
76
77 if (!user) {
78 return <div>Please log in to use swap functionality</div>;
79 }
80
81 return (
82 <div>
83 <button onClick={handleSwap} disabled={!client}>
84 {isPreparingSwap
85 ? "Requesting quote..."
86 : isSigningAndSendingPreparedCalls
87 ? "Signing and sending..."
88 : "Swap ETH to USDC"}
89 </button>
90
91 {signAndSendPreparedCallsResult && (
92 <p>
93 {isWaitingForConfirmation
94 ? "Waiting for confirmation..."
95 : error
96 ? `Error: ${error}`
97 : statusResult?.statusCode === 200
98 ? "Swap confirmed!"
99 : `Status: ${statusResult?.statusCode}`}
100 </p>
101 )}
102 </div>
103 );
104}

How it works

  1. Request quote: usePrepareSwap requests a swap quote and prepares the transaction calls
  2. Destructure result: Extract quote for display and calls for signing
  3. Sign and send: useSignAndSendPreparedCalls signs and submits the transaction
  4. Track status: useWaitForCallsStatus monitors the transaction until confirmation

The quote includes the minimum amount you’ll receive and the expiry time for the quote.

Swap options

You can specify either an exact input amount or a minimum output amount:

1// Mode 1: Swap exact input amount
2await prepareSwapAsync({
3 from: address,
4 fromToken: "0x...",
5 toToken: "0x...",
6 fromAmount: "0x2710", // Swap exactly 0.01 USDC (10000 in hex, 6 decimals)
7});
8
9// Mode 2: Get minimum output amount
10await prepareSwapAsync({
11 from: address,
12 fromToken: "0x...",
13 toToken: "0x...",
14 minimumToAmount: "0x5AF3107A4000", // Get at least 0.0001 ETH (18 decimals)
15});

FAQs

What chains are supported?

Chains supported (for now) are: Ethereum, Arbitrum, Base, Berachain, BSC/BNB, Ink, Monad, Optimism, Polygon, Unichain and World Chain mainnets.

Does the Swap API support cross-chain swaps?

Currently, the Swap API supports only single-chain swaps. Cross-chain swaps are coming soon!

How do you encode values?

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.

What is the expiry?

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.

What if I’m using React?

Use the @account-kit/react package to access a smartWalletClient. This package automatically uses the Signer for user authentication & signing. Check out the docs for getting started with React to learn more.