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

# Swap

> Generate and broadcast a swap or bridge transaction.

export const Callout = ({type = 'info', children}) => {
  const borderColor = ({
    info: '#007acc',
    warning: '#FFC263',
    danger: '#d9534f',
    success: '#00C256'
  })[type] || '#ccc';
  const bgColor = ({
    info: '#f3f9ff',
    warning: '#FFF2DE',
    danger: '#f7f2f1',
    note: '#f8f8f8',
    tip: '#f1f1f8',
    success: '#E0FFEE'
  })[type] || '#f3f9ff';
  return <div style={{
    borderLeft: `4px solid ${borderColor}`,
    backgroundColor: bgColor,
    padding: '0.5rem',
    borderRadius: '12px',
    margin: '1rem 0',
    fontSize: '14px'
  }}>
      {children}
    </div>;
};

**API Reference:** [Get Action](/swap-api-reference/get-action#swap)

Submitting transactions differs by source chain VM. Reference available transaction types [here](/swap-api-reference/get-action#response-tx).

**Examples below demonstrate the swap process for each VM.**

<code>VmId</code>: <code>evm</code>, <code>solana</code>, <code>
alt-vm
</code> (any non-named VM - e.g., Bitcoin, Ripple).

## Create a swap transaction

<Steps>
  <Step title="Configure the transaction request">
    <Callout type="info">
      The Swap action type covers same chain swaps, bridge, and cross-chain swap
      transactions. Swaps are all just swaps!
    </Callout>

    **The `recipient` should only be different from the `sender` if you are:**

    1. Swapping across VMs that use incompatible key formats or cryptographic curves (e.g., `EVM <> Solana`)
    2. Sending funds to another address

    This example prepares a swap from [USDC on Base](https://basescan.org/token/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) to [USDT on Arbitrum](https://arbiscan.io/token/0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9). To transact across non-EVM networks, simply update the chainId and token fields to your desired pairs, no further customizations required.

    ```typescript theme={null}
    import { sendTransaction } from "@wagmi/core";
    import { useAccount } from "wagmi";

    const actionRequest = {
      actionType: "swap-action",
      sender: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", // generally the user's connected wallet address
      srcToken: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", // USDC on Base
      dstToken: "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", // USDT on Arbitrum
      srcChainId: 8453, // Base Chain ID
      dstChainId: 42161, // Arbitrum Chain ID
      slippage: 100, // bps
      swapDirection: "exact-amount-in",
      amount: 10000000n, // denominated in srcToken decimals
      recipient: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    };
    ```
  </Step>

  <Step title="Generate the transaction">
    Please visit the [Get Action](/swap-api-reference/get-action) API reference for type definitions, including the `ActionRequest` and `ActionResponse`. This API endpoint will return a transaction object we will use to actually execute this transaction.

    ```typescript theme={null}
    import { sendTransaction } from "@wagmi/core";
    import { useAccount } from "wagmi";

    const actionRequest: ActionRequest = {
      actionType: "swap-action",
      sender: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", // generally the user's connected wallet address
      srcToken: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", // USDC on Base
      dstToken: "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", // USDT on Arbitrum
      srcChainId: 8453, // Base Chain ID
      dstChainId: 42161, // Arbitrum Chain ID
      slippage: 100, // bps
      swapDirection: "exact-amount-in",
      amount: 10000000n, // denominated in srcToken decimals
      recipient: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    };

    async function getAction({ actionRequest }): Promise<ActionResponse> {
      const url = new URL("https://api-v2.swaps.xyz/api/getAction");

      Object.entries(actionRequest).forEach(([key, value]) => {
        url.searchParams.set(key, String(value));
      });

      const requestOptions = {
        method: "GET",
        headers: { "x-api-key": SWAPS_API_KEY },
      };

      const response = await fetch(url.toString(), requestOptions);

      return await response.json();
    }
    ```

    <Callout type="success">
      **That's it!** You now have a transaction you can broadcast on your source
      chain.
    </Callout>
  </Step>
</Steps>

## Broadcast on EVM

In this step, we will broadcast the transaction we generated above on Base. This method will work for any EVM chain.

View the EVM transaction type [here](/swap-api-reference/get-action#evm-transaction).

<Steps>
  <Step title="Import the recommended libraries">
    This example uses [Wagmi](https://wagmi.sh/); however, you can use your preferred provider. The transaction we'll generate is compatible with any library.

    ```typescript theme={null}
    import { useAccount } from "wagmi";
    import { sendTransaction, estimateGas } from "@wagmi/core";
    import { wagmiConfig } from "../your_wagmi_config_path";
    ```
  </Step>

  <Step title="Broadcast the transaction">
    <Callout type="warning">
      If your source token is an ERC20, you will have to first submit a token
      approval. We recommend only approving the amount required for the transaction.

      <br />

      <br />

      Please follow the guide [here](/guides/token-approvals).
    </Callout>

    ```typescript theme={null}
    import { sendTransaction, estimateGas } from "@wagmi/core";
    import { arbitrum, base } from "viem/chains";
    import { wagmiConfig } from "../your_wagmi_config_path";

    export async function broadcastOnEvm({ actionRequest }): Promise<Hash> {
      const account = useAccount({ config: wagmiConfig });
      const { tx } = await getAction({ actionRequest });
      const gas = await estimateGas(wagmiConfig, { account, ...tx });

      const txHash = await sendTransaction(wagmiConfig, {
        ...tx,
        gas,
      });

      return txHash;
    }
    ```

    <Callout type="success"> **Transaction broadcasted!** Use [Basescan](https://basescan.org/) to verify your transaction. </Callout>
  </Step>
</Steps>

## Broadcast on Solana

Let's assume we set Solana as the source chain in the `actionRequest` used to generate our transaction. Let's see how we can broadcast it on Solana.

View the Solana transaction type [here](/swap-api-reference/get-action#solana-transaction).

<Steps>
  <Step title="Import the recommended libraries">
    We'll use `@solana/web3.js`, which is the standard SDK for interacting with the Solana blockchain.

    ```typescript theme={null}
    import {
      Connection,
      PublicKey,
      sendAndConfirmTransaction,
      Transaction,
    } from "@solana/web3.js";
    ```
  </Step>

  <Step title="Broadcast the transaction">
    <Callout type="warning">
      {" "}

      Ensure that the sender wallet has enough SOL to cover transaction fees. If the
      swap involves a token account that doesn't exist yet, your transaction must include
      an instruction to create it.{" "}
    </Callout>

    ```typescript theme={null}
    import {
      Connection,
      PublicKey,
      sendAndConfirmTransaction,
      Transaction,
    } from "@solana/web3.js";

    export async function broadcastOnSolana({ actionRequest }): Promise<string> {
      const { tx } = await getAction({ actionRequest });

      const connection = new Connection("https://api.mainnet-beta.solana.com");
      const senderKeypair = ... // Your wallet's keypair (use secure storage)

      const transaction = Transaction.from(Buffer.from(tx.rawTransaction, "base64"));

      const signature = await sendAndConfirmTransaction(connection, transaction, [senderKeypair]);

      return signature;
    }
    ```

    <Callout type="success"> **Transaction broadcasted!** Use a Solana explorer like [Solscan](https://solscan.io) to verify your transaction. </Callout>
  </Step>
</Steps>

## Broadcast on Alt VMs

Let's assume we set Bitcoin as the source chain in the `actionRequest` used to generate our transaction. Let's see how we can broadcast it on Bitcoin.

View the alt VM transaction type [here](/swap-api-reference/get-action#alt-vm-transaction).

Alt VM transactions are all deposits: the transaction will transfer funds from
the `sender` to the `to` field specified in the `AltVmTransaction` response.
This transfer triggers the swap transaction and funds will ultimately be
delivered to the `recipient`.

<Callout type="info">
  Please note XRP Ledger requires an extra memo tag to submit transactions. This
  is included in the `tx` response object.
</Callout>

<Callout type="info">
  Alt VM transactions require an extra step to track the status of transactions.
  **See the required step**
  [here](/guides/track-transactions#register-transaction).
</Callout>

<Steps>
  <Step title="Install recommended package">
    ```bash theme={null}
    pnpm i bitcoinjs-lib
    ```
  </Step>

  <Step title="Import the recommended libraries">
    We’ll use bitcoinjs-lib to sign the Bitcoin transaction.

    ```typescript theme={null}
    import * as bitcoin from "bitcoinjs-lib";
    ```
  </Step>

  <Step title="Broadcast the transaction">
    This example provides sample code to construct a Bitcoin transaction given the `actionResponse` transaction object and broadcast it to the network.

    <CodeGroup>
      ```typescript sendBtcTx.ts theme={null}
      import { prepareBtcTx } from "./prepareBtcTx";

      export async function sendTransaction() {
        const { tx } = await getAction({ actionRequest });
        const privateKeyWIF = "YOUR_PRIVATE_KEY_IN_WIF_FORMAT";

      const keyPair = ECPair.fromWIF(privateKeyWIF, NET);
      const payment = bitcoin.payments.p2wpkh({
      pubkey: keyPair.publicKey,
      network: NET,
      });

      const utxos = await getUtxosForAddress(payment.address);
      const rawTransaction = prepareBtcTx({
      tx,
      privateKeyWIF,
      utxos,
      feeRate: 15,
      });

      const response = await fetch("https://blockstream.info/api/tx", {
      method: "POST",
      headers: { "Content-Type": "text/plain" },
      body: rawTransaction,
      });

      if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`Broadcast failed: ${response.status} - ${errorText}`);
      }

      return await response.text();
      }

      ```

      ```typescript prepareBtcTx.ts theme={null}
      import * as bitcoin from "bitcoinjs-lib";
      import * as ecc from "tiny-secp256k1";
      import { ECPairFactory } from "ecpair";

      bitcoin.initEccLib(ecc);
      const ECPair = ECPairFactory(ecc);

      const NET = bitcoin.networks.bitcoin;
      const P2WPKH_DUST = 294;
      const RBF_SEQUENCE = 0xfffffffd;

      interface UTXO {
        txid: string;
        vout: number;
        value: number;
        address: string;
      }

      interface TxData {
        to: string;
        value: string;
        chainId: number;
        chainKey: string;
      }

      function estimateVsize(inputs: number, outputs: number): number {
        return 10 + 68 * inputs + 31 * outputs;
      }

      function selectUtxos(utxos: UTXO[], target: number) {
        const selected = [];
        let total = 0;
        for (const utxo of utxos) {
          selected.push(utxo);
          total += utxo.value;
          if (total >= target) break;
        }
        return { selected, total };
      }

      export function prepareBtcTx({
        tx,
        privateKeyWIF,
        utxos,
        feeRate = 15,
      }: {
        tx: TxData;
        privateKeyWIF: string;
        utxos: UTXO[];
        feeRate?: number;
      }): string {
        const outputValue = Number.parseInt(tx.value, 10);
        if (!Number.isFinite(outputValue) || outputValue <= 0) {
          throw new Error(`Invalid tx.value: ${tx.value}`);
        }

        // Validate destination address
        bitcoin.address.toOutputScript(tx.to, NET);

        // Derive change address
        const keyPair = ECPair.fromWIF(privateKeyWIF, NET);
        const payment = bitcoin.payments.p2wpkh({
          pubkey: keyPair.publicKey,
          network: NET
        });

        if (!payment.address) {
          throw new Error("Could not derive P2WPKH address from private key");
        }

        // Estimate fee and select UTXOs
        const vsize2 = estimateVsize(utxos.length, 2);
        const fee2 = Math.ceil(vsize2 * feeRate);
        let target = outputValue + fee2;
        let { selected, total } = selectUtxos(utxos, target);

        // If insufficient, try with single output (no change)
        if (total < target) {
          const vsize1 = estimateVsize(utxos.length, 1);
          const fee1 = Math.ceil(vsize1 * feeRate);
          const target1 = outputValue + fee1;
          const retry = selectUtxos(utxos, target1);

          if (retry.total < target1) {
            throw new Error(`Insufficient funds. Need ${target1} sats, have ${retry.total}`);
          }
          selected = retry.selected;
          total = retry.total;
        }

        // Calculate final outputs
        const inputsCount = selected.length;
        const feeTwoOut = Math.ceil(estimateVsize(inputsCount, 2) * feeRate);
        let change = total - outputValue - feeTwoOut;

        // Handle dust change
        if (change > 0 && change < P2WPKH_DUST) {
          const feeOneOut = Math.ceil(estimateVsize(inputsCount, 1) * feeRate);
          change = total - outputValue - feeOneOut;
          if (change < 0) {
            throw new Error("Fee calculation error");
          }
        }

        // Build transaction
        const psbt = new bitcoin.Psbt({ network: NET });

        // Add inputs
        for (const utxo of selected) {
          psbt.addInput({
            hash: utxo.txid,
            index: utxo.vout,
            sequence: RBF_SEQUENCE,
            witnessUtxo: {
              script: bitcoin.address.toOutputScript(utxo.address, NET),
              value: utxo.value,
            },
          });
        }

        // Add outputs
        psbt.addOutput({ address: tx.to, value: outputValue });

        if (change >= P2WPKH_DUST) {
          psbt.addOutput({ address: payment.address, value: change });
        }

        // Sign all inputs
        for (let i = 0; i < selected.length; i++) {
          psbt.signInput(i, keyPair);
        }

        psbt.finalizeAllInputs();
        return psbt.extractTransaction().toHex();
      }
      ```
    </CodeGroup>

    <Callout type="success"> **Transaction broadcasted!** Use a Bitcoin explorer like [Blockstream](https://blockstream.info) to verify your transaction. </Callout>
  </Step>
</Steps>

## Broadcast on HyperCore

Let's assume we set HyperCore as the source chain in the `actionRequest` used to generate our transaction. Let's see how we can broadcast it on HyperCore.

View the HyperCore transaction type [here](/swap-api-reference/get-action#hypercore-transaction).

<Steps>
  <Step title="Install recommended package">
    ```bash theme={null}
    pnpm i @nktkas/hyperliquid viem
    ```
  </Step>

  <Step title="Import the recommended libraries">
    We'll use the HyperLiquid SDK for interacting with the HyperCore network.

    ```typescript theme={null}
    import * as HyperLiquid from "@nktkas/hyperliquid";
    import { createWalletClient, custom } from "viem";
    ```
  </Step>

  <Step title="Broadcast the transaction">
    <Callout type="warning">
      Ensure that your wallet has sufficient balance to cover the transaction amount
      and any associated fees on the HyperCore network.
    </Callout>

    ```typescript theme={null}
    import * as HyperLiquid from "@nktkas/hyperliquid";
    import { createWalletClient, custom } from "viem";

    export async function broadcastOnHyperCore({ actionRequest }): Promise<string> {
      const { tx } = await getAction({ actionRequest });

      const [account] = await window.ethereum.request({
        method: "eth_requestAccounts",
      });
      const wallet = createWalletClient({
        account,
        transport: custom(window.ethereum),
      });

      const transport = new HyperLiquid.HttpTransport();
      const exchClient = new HyperLiquid.ExchangeClient({ wallet, transport });

      return await exchClient.usdSend({
        destination: tx.destination,
        amount: tx.amount,
      });
    }
    ```

    <Callout type="success"> **Transaction broadcasted!** Use the [HyperLiquid explorer](https://app.hyperliquid.xyz/explorer) to verify your transaction. </Callout>
  </Step>
</Steps>
