From 6e1dc24678773eb75308111bfeb0de1ae29247ca Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 23 Mar 2026 10:42:59 +0000 Subject: [PATCH 1/3] docs: rewrite swaps page to reflect automatic swap behaviour --- smart-wallet/chain-abstraction/swaps.mdx | 105 +++++++++++------------ 1 file changed, 49 insertions(+), 56 deletions(-) diff --git a/smart-wallet/chain-abstraction/swaps.mdx b/smart-wallet/chain-abstraction/swaps.mdx index d1fac7a7..c8e5a6cc 100644 --- a/smart-wallet/chain-abstraction/swaps.mdx +++ b/smart-wallet/chain-abstraction/swaps.mdx @@ -1,101 +1,94 @@ --- title: "Swaps" -description: "Bridge and swap in a single transaction. Use Warp's built-in swaps or supply your own swap calldata." +description: "Send and receive any token across chains. The orchestrator handles swaps automatically when needed." --- -Rhinestone supports two swap modes: +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. -- **Swaps**: Warp handles the swap using solver liquidity. Simpler to integrate, best for common tokens. -- **Injected swaps**: you supply the calldata for a swap from an external API (1inch, 0x, etc.). More setup, but full token coverage and best-price routing. +**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. + -## Swaps +## Automatic swaps + +Specify any token as the destination token. If it isn't a settlement token, the swap is handled automatically. -Specify the token you want on the destination chain. If it differs from what the user holds, Warp handles the bridge and swap automatically. +The example below sends an arbitrary ERC-20 from Base to an arbitrary ERC-20 on Optimism. ```ts -import { getTokenAddress } from '@rhinestone/sdk' -import { baseSepolia, arbitrumSepolia } from 'viem/chains' +import { baseSepolia, optimismSepolia } from 'viem/chains' -const ethTarget = getTokenAddress('ETH', arbitrumSepolia.id) -const ethAmount = 2n -const receiver = '0xd8da6bf26964af9d7eed9e03e53415d37aa96045' +// Any token the user holds on the source chain +const inputToken = '0xYOUR_INPUT_TOKEN_ADDRESS' +const inputAmount = parseUnits('10', 18) + +// Any token to receive on the destination chain +const outputToken = '0xYOUR_OUTPUT_TOKEN_ADDRESS' const transaction = await rhinestoneAccount.sendTransaction({ sourceChains: [baseSepolia], - targetChain: arbitrumSepolia, + targetChain: optimismSepolia, calls: [ { to: receiver, - value: ethAmount, + data: encodeFunctionData({ + abi: erc20Abi, + functionName: 'transfer', + args: [receiver, outputAmount], + }), }, ], tokenRequests: [ { - address: ethTarget, - amount: ethAmount, + address: outputToken, + amount: outputAmount, }, ], }) ``` -If the user has USDC on Base Sepolia, Warp will swap it to ETH, bridge to Arbitrum Sepolia, and execute the transfer. +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. -The token address in `calls` and `tokenRequests` must correspond to the _target_ chain. +## Custom swap calldata -## Injected swaps +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. -For full token coverage or best-price routing, supply swap calldata from a DEX aggregator and include it as destination chain calls. +**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 makes a WETH to cbBTC swap on Base using funds from Arbitrum, via 1inch. +The example below bridges to Base and executes a custom swap from WETH to cbBTC using an external aggregator. -**1. Initialize the 1inch client:** +**1. Fetch approval and swap calldata from your aggregator:** ```ts const walletAddress = rhinestoneAccount.getAddress() const wethBase = '0x4200000000000000000000000000000000000006' const wethAmount = parseEther('0.1') const cbbtcBase = '0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf' -const oneInchApiKey = 'YOUR_1INCH_API_KEY' -const baseUrl = 'https://api.1inch.dev/swap/v6.0/8453' - -async function call1inchAPI( - endpointPath: string, - queryParams: Record -): Promise { - const url = new URL(baseUrl + endpointPath) - url.search = new URLSearchParams(queryParams).toString() - const response = await fetch(url.toString(), { - headers: { Accept: 'application/json', Authorization: `Bearer ${oneInchApiKey}` }, - }) - if (!response.ok) throw new Error(`1inch API error ${response.status}`) - return response.json() as Promise -} -``` - -**2. Fetch approval and swap data:** - -```ts -const approveTx = await call1inchAPI('/approve/transaction', { - tokenAddress: wethBase, - amount: wethAmount.toString(), -}) -const swapTx = await call1inchAPI('/swap', { +// Fetch from your preferred DEX aggregator API +const { approveTx, swapTx } = await fetchSwapCalldata({ src: wethBase, dst: cbbtcBase, - amount: wethAmount.toString(), + amount: wethAmount, from: walletAddress, - slippage: '1', - disableEstimate: 'false', - allowPartialFill: 'false', }) ``` -**3. Submit as a crosschain transaction:** +**2. Submit as a crosschain transaction:** ```ts const transaction = await rhinestoneAccount.sendTransaction({ @@ -103,7 +96,7 @@ const transaction = await rhinestoneAccount.sendTransaction({ targetChain: base, calls: [ { to: approveTx.to, data: approveTx.data, value: approveTx.value }, - { to: swapTx.tx.to, data: swapTx.tx.data, value: swapTx.tx.value }, + { to: swapTx.to, data: swapTx.data, value: swapTx.value }, ], tokenRequests: [ { address: wethBase, amount: wethAmount }, @@ -111,17 +104,17 @@ const transaction = await rhinestoneAccount.sendTransaction({ }) ``` -This bridges from Arbitrum to get 0.1 WETH on Base, then executes the 1inch swap to cbBTC. +This bridges from Arbitrum to get WETH on Base, then executes your custom swap to cbBTC. -## Swaps +## Automatic swaps -When you request a token on the destination chain that differs from the user's source token, Warp routes through the solver market to bridge and swap in a single operation. No additional parameters are required — the quote response tells you what will be spent and what will be received. +When the input or output token differs from a settlement token, the orchestrator automatically includes a swap in the intent. No extra parameters are needed — the quote response reflects what will actually be spent and received. -In a swap, the `tokensSpent` and `tokensReceived` will show different tokens: +A swap intent will show different tokens in `tokensSpent` and `tokensReceived`: ```json { From 3c7b730ef5b61fe841d19b2b5ccc42065c0cf508 Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 23 Mar 2026 10:58:25 +0000 Subject: [PATCH 2/3] docs: use real token addresses in swaps example (DEGEN on Base, OP on Optimism) --- smart-wallet/chain-abstraction/swaps.mdx | 30 ++++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/smart-wallet/chain-abstraction/swaps.mdx b/smart-wallet/chain-abstraction/swaps.mdx index c8e5a6cc..7fb88079 100644 --- a/smart-wallet/chain-abstraction/swaps.mdx +++ b/smart-wallet/chain-abstraction/swaps.mdx @@ -22,35 +22,39 @@ Warp supports arbitrary input and output tokens. You don't need to configure any Specify any token as the destination token. If it isn't a settlement token, the swap is handled automatically. -The example below sends an arbitrary ERC-20 from Base to an arbitrary ERC-20 on Optimism. +The example below sends DEGEN (Base) and receives OP (Optimism) — neither is a settlement token. ```ts -import { baseSepolia, optimismSepolia } from 'viem/chains' +import { base, optimism } from 'viem/chains' +import { parseUnits } from 'viem' -// Any token the user holds on the source chain -const inputToken = '0xYOUR_INPUT_TOKEN_ADDRESS' -const inputAmount = parseUnits('10', 18) +// DEGEN on Base +const degenBase = '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed' +const degenAmount = parseUnits('1000', 18) -// Any token to receive on the destination chain -const outputToken = '0xYOUR_OUTPUT_TOKEN_ADDRESS' +// OP token on Optimism +const opOptimism = '0x4200000000000000000000000000000000000042' +const opAmount = parseUnits('10', 18) + +const receiver = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' const transaction = await rhinestoneAccount.sendTransaction({ - sourceChains: [baseSepolia], - targetChain: optimismSepolia, + sourceChains: [base], + targetChain: optimism, calls: [ { - to: receiver, + to: opOptimism, data: encodeFunctionData({ abi: erc20Abi, functionName: 'transfer', - args: [receiver, outputAmount], + args: [receiver, opAmount], }), }, ], tokenRequests: [ { - address: outputToken, - amount: outputAmount, + address: opOptimism, + amount: opAmount, }, ], }) From 5be3a098fa5f6a8a2a0f0923efa401a2f2c736f9 Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 23 Mar 2026 11:06:53 +0000 Subject: [PATCH 3/3] fix: deduplicate automatic swaps heading in API tab --- smart-wallet/chain-abstraction/swaps.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smart-wallet/chain-abstraction/swaps.mdx b/smart-wallet/chain-abstraction/swaps.mdx index 7fb88079..55b6ec1d 100644 --- a/smart-wallet/chain-abstraction/swaps.mdx +++ b/smart-wallet/chain-abstraction/swaps.mdx @@ -114,7 +114,7 @@ This bridges from Arbitrum to get WETH on Base, then executes your custom swap t -## Automatic swaps +## How swaps appear in the API response When the input or output token differs from a settlement token, the orchestrator automatically includes a swap in the intent. No extra parameters are needed — the quote response reflects what will actually be spent and received.