> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Building a swap UI

> Quote a token swap, show the estimate, execute it from the user's wallet, and track it to completion.

## Prerequisites

Before this page: a connected or embedded wallet (see [Connecting external wallets](/docs/javascript/building-ui/connecting-external-wallets)).

## What you'll build

A swap flow has four steps:

1. **Quote** — ask for a route between two tokens.
2. **Show** the estimated amount received and gas cost.
3. **Execute** — sign and submit from the user's wallet.
4. **Track** — poll until the swap is done.

Amounts are always in the token's **smallest unit** (for example, `1000000` for 1 USDC with 6 decimals).

## Quoting a swap

`getSwapQuote` takes a `from` and `to` side. Set `from.amount` to quote by input (or `to.amount` to quote by desired output — they're mutually exclusive). The response includes the `signingPayload` you'll execute and the estimated `to.amount`.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { getSwapQuote, type WalletAccount } from '@dynamic-labs-sdk/client';

    async function quoteUsdcToWeth(walletAccount: WalletAccount) {
      const quote = await getSwapQuote({
        from: {
          address: walletAccount.address,
          chain: walletAccount.chain,
          networkId: '1',
          tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
          amount: '1000000',
        },
        to: {
          address: walletAccount.address,
          chain: walletAccount.chain,
          networkId: '1',
          tokenAddress: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
        },
        slippage: 0.005,
      });

      // showEstimate is your implementation.
      showEstimate(quote.to.amount, quote.gasCostUSD);
      return quote;
    }
    ```
  </Tab>

  <Tab title="React">
    ```tsx theme={"system"}
    import { type WalletAccount } from '@dynamic-labs-sdk/client';
    import { useGetSwapQuote } from '@dynamic-labs-sdk/react-hooks';

    function useUsdcToWethQuote(walletAccount: WalletAccount, amount: string) {
      // useGetSwapQuote is a query hook -- it fetches (and refetches) whenever
      // the params change, so pass amount from your own input state to keep
      // the quote live as the user types.
      return useGetSwapQuote({
        from: {
          address: walletAccount.address,
          chain: walletAccount.chain,
          networkId: '1',
          tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
          amount,
        },
        to: {
          address: walletAccount.address,
          chain: walletAccount.chain,
          networkId: '1',
          tokenAddress: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
        },
        slippage: 0.005,
      });
    }
    ```

    Use the returned `{ data: quote, isLoading }` to render the estimate — `quote` is `undefined` until the first fetch resolves.
  </Tab>
</Tabs>

## Executing the swap

Pass the quote's `signingPayload` to `executeSwapTransaction`. ERC-20 swaps may need a separate approval first — `onStepChange` tells you which step is running so you can update the UI.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { executeSwapTransaction } from '@dynamic-labs-sdk/client';
    import type { SwapQuoteResponse, WalletAccount } from '@dynamic-labs-sdk/client';

    async function executeSwap(
      walletAccount: WalletAccount,
      quote: SwapQuoteResponse,
    ) {
      if (!quote.signingPayload) return;

      const { transactionHash } = await executeSwapTransaction({
        walletAccount,
        signingPayload: quote.signingPayload,
        onStepChange: (step) => {
          // updateStep is your implementation — 'approval' or 'transaction'.
          updateStep(step);
        },
      });

      return transactionHash;
    }
    ```
  </Tab>

  <Tab title="React">
    ```tsx theme={"system"}
    import type { SwapQuoteResponse, WalletAccount } from '@dynamic-labs-sdk/client';
    import { useExecuteSwapTransaction } from '@dynamic-labs-sdk/react-hooks';

    function SwapButton({
      walletAccount,
      quote,
    }: {
      walletAccount: WalletAccount;
      quote: SwapQuoteResponse;
    }) {
      const { mutate: execute, isPending } = useExecuteSwapTransaction();

      const onSwap = () => {
        if (!quote.signingPayload) return;

        execute({
          walletAccount,
          signingPayload: quote.signingPayload,
          // updateStep is your implementation — step is 'approval' or 'transaction'.
          onStepChange: (step) => updateStep(step),
        });
      };

      return (
        <button type="button" onClick={onSwap} disabled={isPending}>
          Swap
        </button>
      );
    }
    ```
  </Tab>
</Tabs>

## Tracking status

A swap can take time to settle — especially across chains. Poll `getSwapStatus` with the transaction hash until the status is `DONE` or `FAILED`.

<Tabs>
  <Tab title="TypeScript">
    Re-check on an interval and resolve only once the swap reaches a terminal status.

    ```typescript theme={"system"}
    import { getSwapStatus } from '@dynamic-labs-sdk/client';

    const POLL_INTERVAL_MS = 3000;

    const delay = (ms: number) =>
      new Promise((resolve) => setTimeout(resolve, ms));

    // Resolves only once the swap settles — DONE or FAILED.
    async function trackSwap(txHash: string): Promise<'DONE' | 'FAILED'> {
      while (true) {
        const { status } = await getSwapStatus({ txHash });

        // status is 'PENDING' | 'DONE' | 'FAILED' | 'NOT_FOUND'.
        if (status === 'DONE' || status === 'FAILED') {
          return status;
        }

        // Not settled yet — wait, then check again.
        await delay(POLL_INTERVAL_MS);
      }
    }
    ```
  </Tab>

  <Tab title="React">
    Drive polling with `refetchInterval` — return the interval while the swap is in flight, and `false` once it settles so the hook stops refetching.

    ```tsx theme={"system"}
    import { useGetSwapStatus } from '@dynamic-labs-sdk/react-hooks';

    const POLL_INTERVAL_MS = 3000;

    function SwapStatus({ txHash }: { txHash: string }) {
      const { data } = useGetSwapStatus({
        txHash,
        queryParams: {
          refetchInterval: (query) => {
            const status = query.state.data?.status;
            const settled = status === 'DONE' || status === 'FAILED';
            return settled ? false : POLL_INTERVAL_MS;
          },
        },
      });

      // data.status is 'PENDING' | 'DONE' | 'FAILED' | 'NOT_FOUND'.
      return <span>{data?.status ?? 'PENDING'}</span>;
    }
    ```
  </Tab>
</Tabs>

## Handling errors

| Error / status                                  | Cause                                                         | What to do                                                                                    |
| ----------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `TokenSwapError`                                | `getSwapStatus` couldn't check the swap's status              | Show the message and let the user retry the status check — the swap itself may still be fine. |
| `getSwapQuote`/`executeSwapTransaction` rejects | No route, stale quote, or the user rejected the wallet prompt | Show the message and let the user re-quote — routes and prices change.                        |
| Status `FAILED`                                 | The swap didn't settle                                        | Surface the failure; the `substatus` gives more context.                                      |
| Status `PENDING`                                | Still settling                                                | Keep polling; show an in-progress state.                                                      |

## See also

* [Transaction confirmation & simulation](/docs/javascript/building-ui/transaction-confirmation) — confirm the underlying transaction
* [Token balances & display](/docs/javascript/building-ui/token-balances-display) — reflect balances after the swap
* [Funding flows](/docs/javascript/building-ui/funding-flows) — get tokens to swap
