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

# End-to-end Transaction Example

<Info>
  **Want to go beyond swaps and bridges?** With [Composer](/composer/overview), you can deposit into vaults, stake, and lend — all in a single transaction using the same API pattern shown below. See the [Composer Quickstart](/composer/quickstart).
</Info>

## Step by step

<Steps>
  <Step title="Requesting a quote or routes">
    <CodeGroup>
      ```ts TypeScript theme={"system"}
      import axios from 'axios';

      const getQuote = async (
        fromChain: number,
        toChain: number,
        fromToken: string,
        toToken: string,
        fromAmount: string,
        fromAddress: string,
      ) => {
        const result = await axios.get('https://li.quest/v1/quote', {
          params: {
            fromChain,
            toChain,
            fromToken,
            toToken,
            fromAmount,
            fromAddress,
          },
        });
        return result.data;
      };

      const fromChain = 42161; // Arbitrum
      const fromToken = 'USDC';
      const toChain = 100; // Gnosis
      const toToken = 'USDC';
      const fromAmount = '1000000';
      const fromAddress = '0xYOUR_WALLET_ADDRESS';

      const quote = await getQuote(fromChain, toChain, fromToken, toToken, fromAmount, fromAddress);
      ```
    </CodeGroup>
  </Step>

  <Step title="Choose the desired route if `/advanced/routes` was used and retrieve transaction data from `/advanced/stepTransaction`">
    <Note>
      This step is only needed if `/advanced/routes` endpoint was used. `/quote` already returns the transaction data within the response. Difference between `/quote` and `/advanced/routes` is described [here](/introduction/user-flows-and-examples/difference-between-quote-and-route)
    </Note>
  </Step>

  <Step title="Setting the allowance">
    Before any transaction can be sent, it must be made sure that the user is allowed to send the requested amount from the wallet. This example uses the classic `approve()` approach. To reduce approval transactions using off-chain EIP-712 signatures, see the [Permit & Permit2 Approval Flow](/introduction/user-flows-and-examples/permit2-approval-flow).

    <CodeGroup>
      ```ts TypeScript theme={"system"}
      import { erc20Abi, zeroAddress, type Address } from 'viem';
      import type { PublicClient, WalletClient } from 'viem';

      // Get the current allowance and update it if needed
      const checkAndSetAllowance = async (
        publicClient: PublicClient,
        walletClient: WalletClient,
        tokenAddress: Address,
        approvalAddress: Address,
        amount: bigint,
      ) => {
        // Transactions with the native token don't need approval
        if (tokenAddress === zeroAddress) {
          return;
        }

        const [account] = await walletClient.getAddresses();
        const allowance = await publicClient.readContract({
          address: tokenAddress,
          abi: erc20Abi,
          functionName: 'allowance',
          args: [account, approvalAddress],
        });

        if (allowance < amount) {
          const hash = await walletClient.writeContract({
            address: tokenAddress,
            abi: erc20Abi,
            functionName: 'approve',
            args: [approvalAddress, amount],
            account,
            chain: walletClient.chain,
          });
          await publicClient.waitForTransactionReceipt({ hash });
        }
      };

      await checkAndSetAllowance(
        publicClient,
        walletClient,
        quote.action.fromToken.address as Address,
        quote.estimate.approvalAddress as Address,
        BigInt(fromAmount),
      );
      ```
    </CodeGroup>
  </Step>

  <Step title="Sending the transaction">
    After receiving a quote, the transaction has to be sent to trigger the transfer.

    Firstly, the wallet has to be configured. The transaction executes on the source chain, so the following example connects your wallet to Arbitrum:

    <CodeGroup>
      ```ts TypeScript theme={"system"}
      import { createPublicClient, createWalletClient, http } from 'viem';
      import { mnemonicToAccount } from 'viem/accounts';
      import { arbitrum } from 'viem/chains';

      const account = mnemonicToAccount('YOUR_PERSONAL_MNEMONIC');
      const publicClient = createPublicClient({ chain: arbitrum, transport: http() });
      const walletClient = createWalletClient({ account, chain: arbitrum, transport: http() });
      ```
    </CodeGroup>

    Afterward, the transaction can be sent using the `transactionRequest` inside the previously retrieved quote:

    <CodeGroup>
      ```ts TypeScript theme={"system"}
      import type { Address, Hex } from 'viem';

      const hash = await walletClient.sendTransaction({
        to: quote.transactionRequest.to as Address,
        data: quote.transactionRequest.data as Hex,
        value: BigInt(quote.transactionRequest.value),
        gas: BigInt(quote.transactionRequest.gasLimit),
        gasPrice: BigInt(quote.transactionRequest.gasPrice),
      });
      await publicClient.waitForTransactionReceipt({ hash });
      ```
    </CodeGroup>
  </Step>

  <Step title="Executing second step if applicable">
    If two-step route was used, the second step has to be executed after the first step is complete. Fetch the status of the first step like described in next step and then request transactionData from the `/advanced/stepTransaction` endpoint.
  </Step>

  <Step title="Fetching the transfer status">
    To check if the token was successfully sent to the receiving chain, the /status endpoint can be called:

    <CodeGroup>
      ```ts TypeScript theme={"system"}
      const getStatus = async (
        bridge: string,
        fromChain: number,
        toChain: number,
        txHash: string,
      ) => {
        const result = await axios.get('https://li.quest/v1/status', {
          params: {
            bridge,
            fromChain,
            toChain,
            txHash,
          },
        });
        return result.data;
      };

      const result = await getStatus(quote.tool, fromChain, toChain, hash);
      ```
    </CodeGroup>
  </Step>
</Steps>

## Full example

<CodeGroup>
  ```ts TypeScript theme={"system"}
  import axios from 'axios';
  import {
    createPublicClient,
    createWalletClient,
    erc20Abi,
    http,
    zeroAddress,
    type Address,
    type Hex,
  } from 'viem';
  import { mnemonicToAccount } from 'viem/accounts';
  import { arbitrum } from 'viem/chains';

  const API_URL = 'https://li.quest/v1';

  // Get a quote for your desired transfer
  const getQuote = async (
    fromChain: number,
    toChain: number,
    fromToken: string,
    toToken: string,
    fromAmount: string,
    fromAddress: string,
  ) => {
    const result = await axios.get(`${API_URL}/quote`, {
      params: {
        fromChain,
        toChain,
        fromToken,
        toToken,
        fromAmount,
        fromAddress,
      },
    });
    return result.data;
  };

  // Check the status of your transfer
  const getStatus = async (
    bridge: string,
    fromChain: number,
    toChain: number,
    txHash: string,
  ) => {
    const result = await axios.get(`${API_URL}/status`, {
      params: {
        bridge,
        fromChain,
        toChain,
        txHash,
      },
    });
    return result.data;
  };

  const fromChain: number = 42161; // Arbitrum
  const fromToken = 'USDC';
  const toChain: number = 100; // Gnosis
  const toToken = 'USDC';
  const fromAmount = '1000000';

  // Set up your wallet on the source chain
  const account = mnemonicToAccount('YOUR_PERSONAL_MNEMONIC');
  const publicClient = createPublicClient({ chain: arbitrum, transport: http() });
  const walletClient = createWalletClient({ account, chain: arbitrum, transport: http() });

  // Get the current allowance and update it if needed
  const checkAndSetAllowance = async (
    tokenAddress: Address,
    approvalAddress: Address,
    amount: bigint,
  ) => {
    // Transactions with the native token don't need approval
    if (tokenAddress === zeroAddress) {
      return;
    }

    const allowance = await publicClient.readContract({
      address: tokenAddress,
      abi: erc20Abi,
      functionName: 'allowance',
      args: [account.address, approvalAddress],
    });

    if (allowance < amount) {
      const approveHash = await walletClient.writeContract({
        address: tokenAddress,
        abi: erc20Abi,
        functionName: 'approve',
        args: [approvalAddress, amount],
      });
      await publicClient.waitForTransactionReceipt({ hash: approveHash });
    }
  };

  const run = async () => {
    const quote = await getQuote(
      fromChain,
      toChain,
      fromToken,
      toToken,
      fromAmount,
      account.address,
    );

    await checkAndSetAllowance(
      quote.action.fromToken.address as Address,
      quote.estimate.approvalAddress as Address,
      BigInt(fromAmount),
    );

    const hash = await walletClient.sendTransaction({
      to: quote.transactionRequest.to as Address,
      data: quote.transactionRequest.data as Hex,
      value: BigInt(quote.transactionRequest.value),
      gas: BigInt(quote.transactionRequest.gasLimit),
      gasPrice: BigInt(quote.transactionRequest.gasPrice),
    });

    await publicClient.waitForTransactionReceipt({ hash });

    // Only needed for cross chain transfers
    if (fromChain !== toChain) {
      let result;
      do {
        result = await getStatus(quote.tool, fromChain, toChain, hash);

        if (result.status !== 'DONE' && result.status !== 'FAILED') {
          await new Promise((resolve) => setTimeout(resolve, 5000)); // Wait 5s
        }
      } while (result.status !== 'DONE' && result.status !== 'FAILED');
    }
  };

  run().then(() => {
    console.log('DONE!');
  });
  ```
</CodeGroup>
