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

# Make a swap

> Swap into a requested token on the same chain or across chains.

Request the output token and amount on the target chain. Rhinestone selects source assets, quotes the swap and any bridge, and delivers the requested output in one intent.

## Choose the intent shape

| Outcome                           | Token requests                    | Calls                               |
| --------------------------------- | --------------------------------- | ----------------------------------- |
| Plain swap                        | Requested output token and amount | None                                |
| ERC-20 payment                    | Token needed on the target chain  | Real `ERC20.transfer` calldata      |
| Contract action funded by a trade | Tokens consumed by the contract   | Real approval and application calls |

For a plain swap, use an empty calls array. Do not add dummy calldata, a self-transfer, or `{ data: "0x" }`. Placeholder calls change the request and can make the route unsupported.

Use target-chain token addresses in `tokenRequests`. Omit source constraints to route from the user's available [unified balance](/transactions/multichain/unified-balance), or add constraints when the user selected an input chain or asset.

## Request a token-only swap

This example requests 100 USDC on Arbitrum and restricts funding to WETH on Base:

```ts theme={null}
import { encodeFunctionData, erc20Abi, parseUnits } from "viem";
import { arbitrum, base } from "viem/chains";

const wethOnBase = "0x4200000000000000000000000000000000000006";
const usdcOnBase = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
const usdcOnArbitrum = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831";
const amount = parseUnits("100", 6);
```

For a token-only request, the amount can be treated as the target's final balance when existing destination funds are also in source scope. Both examples restrict the source to Base, so any USDC already on Arbitrum is outside that scope and the full 100 USDC is delivered.

<Tabs>
  <Tab title="Embedded wallet">
    Use the initialized `oneAuth` client and authenticated `accountAddress` from [Accounts](/wallets/embedded-wallets/accounts). These fields are available in `@rhinestone/1auth@0.10.1`:

    ```ts {6} theme={null}
    const result = await oneAuth.sendIntent({
      accountAddress,
      targetChain: arbitrum.id,
      sourceChainId: base.id,
      sourceAssets: [wethOnBase],
      tokenRequests: [{ token: usdcOnArbitrum, amount }],
      calls: [],
    });

    if (!result.success) throw new Error(result.error?.message);
    ```

    Embedded wallets use numeric chain IDs and `tokenRequests[].token`. `sourceChainId` applies the `sourceAssets` address to Base. Omit both source constraints to let the orchestrator choose across available mainnet balances. `sendIntent()` manages quote preparation and does not expose the custom signer SDK's ranked `prepared.quotes` object.

    Sponsorship is required by default. Reuse the funding, review, and completion options from [Send a transaction](/transactions/multichain/send-a-transaction), and keep any returned `intentId` before retrying an uncertain submission.

    <Note>
      The embedded wallet SDK 0.10.1 requires an amount in each token request. It
      does not expose the custom signer SDK's exact-input and max-output request
      shape. Use a fixed output amount or the custom signer SDK instead.
    </Note>
  </Tab>

  <Tab title="Custom signer">
    Use `account` from the [Custom signer quickstart](/wallets/custom-signer/quickstart) with `@rhinestone/sdk@2.16.1`:

    ```ts {9,13,15} theme={null}
    const prepared = await account.prepareTransaction({
      sourceChains: [base],
      sourceAssets: [wethOnBase],
      targetChain: arbitrum,
      tokenRequests: [{ address: usdcOnArbitrum, amount }],
      calls: [],
    });

    const quote = prepared.quotes.best;
    console.log(quote.cost.input, quote.cost.output, quote.cost.fees);

    const signed = await account.signTransaction(prepared, {
      intentId: quote.intentId,
    });
    const submitted = await account.submitTransaction(signed);
    const result = await account.waitForExecution(submitted);

    console.log(result.status, result.operations);
    ```

    The SDK uses chain objects and `tokenRequests[].address`. `sourceChains` restricts funding to Base, and `sourceAssets` restricts the input token to WETH. Omit `sourceAssets` to allow other assets on Base, or omit both source fields to allow available balances across chains.

    Review the selected quote's input, output, fees, and expiry before signing. The requested output is not reduced by fees; the quote includes fees in its input cost. This example is user-paid. Set `sponsored: true` during preparation if your application pays the fees.

    If no route is available, check the balance, source restrictions, output amount, supported tokens, and liquidity. Prepare and sign a new route after expiry or a price-related execution failure. If submission has an uncertain result, query the submitted intent before creating another one.

    ### Spend an exact input amount

    Use the exact-input form of `sourceAssets` and omit the output amount to spend a fixed WETH amount:

    ```ts theme={null}
    const prepared = await account.prepareTransaction({
      sourceChains: [base],
      sourceAssets: [
        {
          chain: base,
          address: wethOnBase,
          amount: parseUnits("0.05", 18),
        },
      ],
      targetChain: arbitrum,
      tokenRequests: [{ address: usdcOnArbitrum }],
      calls: [],
    });
    ```

    A token request without an amount is the max-output form and must be the only token request. Read the selected quote before signing to show the resulting output and fees.

    ### Pin the swap venue

    The orchestrator sources swap routes from every venue the chain supports. `quoters` restricts that set:

    ```ts theme={null}
    const prepared = await account.prepareTransaction({
      sourceChains: [base],
      sourceAssets: [wethOnBase],
      targetChain: arbitrum,
      tokenRequests: [{ address: usdcOnArbitrum, amount }],
      calls: [],
      quoters: { include: ["1inch", "0x"] },
    });
    ```

    Supported venues are `1inch`, `0x`, `velora`, `kyberswap`, `fynd`, `fynd-hosted`, `bebop`, and `relay`. `{ exclude: [...] }` inverts the filter against the full set, and an empty allow-list fails the request rather than falling back to an unconstrained route.

    A venue-scoped session derives this pin on its own; see [Swap sessions](/wallets/session-keys/custom-setup/swap-sessions#pinning-venues-without-a-session) for how the two interact.

    ### Deliver the output elsewhere

    A plain swap credits the account itself. Set `recipient` to deliver the output to another address instead:

    ```ts theme={null}
    const prepared = await account.prepareTransaction({
      sourceChains: [base],
      sourceAssets: [wethOnBase],
      targetChain: arbitrum,
      tokenRequests: [{ address: usdcOnArbitrum, amount }],
      calls: [],
      recipient: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    });
    ```

    Pass a `RhinestoneAccountConfig` instead of an address to deliver into another smart account. `recipient` moves the delivered assets and the execution context; it does not rewrite addresses already encoded in `calls`. When the trade funds calls, leave it unset and encode the receiving address in the calldata, as `shareRecipient` does below.

    Settlement layer filters, ranked-quote selection, declared incoming balances, and destination gas limits work the same way here — see [Send a transaction](/transactions/multichain/send-a-transaction#choose-a-different-route).
  </Tab>
</Tabs>

## Trade and deposit

When the output feeds another action, include the actual calls and the matching token request in one intent. This example swaps WETH into 100 USDC on Base, approves the Steakhouse Prime USDC ERC-4626 vault, and deposits for `shareRecipient`:

```ts {23-24,32-33} theme={null}
const vault = "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9";
const shareRecipient = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";

const vaultAbi = [
  {
    type: "function",
    name: "deposit",
    stateMutability: "nonpayable",
    inputs: [
      { name: "assets", type: "uint256" },
      { name: "receiver", type: "address" },
    ],
    outputs: [{ name: "shares", type: "uint256" }],
  },
] as const;

const fundedCalls = [
  {
    to: usdcOnBase,
    value: 0n,
    data: encodeFunctionData({
      abi: erc20Abi,
      functionName: "approve",
      args: [vault, amount],
    }),
  },
  {
    to: vault,
    value: 0n,
    data: encodeFunctionData({
      abi: vaultAbi,
      functionName: "deposit",
      args: [amount, shareRecipient],
    }),
  },
] as const;
```

<Tabs>
  <Tab title="Embedded wallet">
    Submit the shared calls with the embedded wallet's field names:

    ```ts {7} theme={null}
    const result = await oneAuth.sendIntent({
      accountAddress,
      targetChain: base.id,
      sourceChainId: base.id,
      sourceAssets: [wethOnBase],
      tokenRequests: [{ token: usdcOnBase, amount }],
      calls: [...fundedCalls],
    });
    ```
  </Tab>

  <Tab title="Custom signer">
    Submit the same calls with the custom signer SDK's field names:

    ```ts theme={null}
    const prepared = await account.prepareTransaction({
      sourceChains: [base],
      sourceAssets: [wethOnBase],
      targetChain: base,
      tokenRequests: [{ address: usdcOnBase, amount }],
      calls: [...fundedCalls],
    });

    const quote = prepared.quotes.best;
    const signed = await account.signTransaction(prepared, {
      intentId: quote.intentId,
    });
    const submitted = await account.submitTransaction(signed);
    const result = await account.waitForExecution(submitted);
    ```
  </Tab>
</Tabs>

The output delivery and calls belong to one intent. The approval and deposit execute in order and revert together if either call fails. This is not a queue of separate trades.

See [Sponsor fees](/transactions/sponsorship/sponsor-fees) for funding modes. Continue to the [end-to-end transaction flow](/transactions/multichain/end-to-end-transaction-flow) for status and recovery details.
