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

# Quickstart

> Execute your first Composer transaction in under 5 minutes. Deposit USDC into a Morpho vault on Base with a single API call.

This quickstart walks you through a same-chain Composer transaction, depositing USDC into a Morpho vault on Base. By the end, you'll understand the full flow of requesting a quote, approving tokens, executing the transaction, and tracking status.

Composer uses standard LI.FI endpoints. Set `toToken` to a supported protocol token address and the routing engine returns a Composer route automatically.

<Tip>
  This quickstart covers a same-chain deposit. Composer also supports
  cross-chain deposits across EVM chains. See [Cross-Chain Composer
  Patterns](/composer/lifi-api/guides/cross-chain-compose) for those flows.
</Tip>

## Prerequisites

* A wallet address with USDC on Base (even a small amount like 1 USDC works)
* Node.js 18+ (for the TypeScript examples) or `curl`

***

<Steps>
  <Step title="Get a Composer quote">
    Request a quote with `toToken` set to the vault token address:

    <CodeGroup>
      ```bash curl theme={"system"}
      curl -X GET 'https://li.quest/v1/quote?fromChain=8453&toChain=8453&fromToken=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&toToken=0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A&fromAddress=0xYOUR_WALLET_ADDRESS&toAddress=0xYOUR_WALLET_ADDRESS&fromAmount=1000000'
      ```

      ```ts TypeScript theme={"system"}
      import axios from "axios";

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

      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,
            toAddress: fromAddress,
          },
        });
        return result.data;
      };

      const quote = await getQuote(
        8453, // Base
        8453, // Base (same-chain)
        "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
        "0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A", // Morpho vault token
        "1000000", // 1 USDC (6 decimals)
        "0xYOUR_WALLET_ADDRESS",
      );

      console.log(quote);
      ```
    </CodeGroup>

    **Key parameters:**

    | Parameter    | Value           | Description                |
    | ------------ | --------------- | -------------------------- |
    | `fromChain`  | `8453`          | Base chain ID              |
    | `toChain`    | `8453`          | Base (same-chain deposit)  |
    | `fromToken`  | `0x8335...2913` | USDC on Base               |
    | `toToken`    | `0x7BfA...34A`  | Morpho vault token address |
    | `fromAmount` | `1000000`       | 1 USDC (6 decimals)        |

    <Tip>
      The `toToken` is always the **vault token address** of the target protocol. You can find vault token addresses on the protocol's own app or documentation.
    </Tip>

    The response includes `transactionRequest`, a ready-to-sign EVM transaction, along with estimated output amounts and the tools used. The `tool` field will be `"composer"` for Composer routes.

    <Tip>
      Quotes reflect current market conditions. If the user reviews a quote for more than 30 seconds, re-fetch before signing to get up-to-date pricing and simulation results.
    </Tip>
  </Step>

  <Step title="Set token allowance">
    Before executing, ensure the LI.FI Diamond contract is approved to spend your tokens. The approval address is returned in the quote response at `quote.estimate.approvalAddress`. This points to the [LI.FI Diamond](https://etherscan.io/address/0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE), an audited contract deployed on all supported chains.

    <Note>
      If you're sending a native token (e.g., ETH), skip this step. Native tokens don't require approval.
    </Note>

    <CodeGroup>
      ```ts TypeScript theme={"system"}
      import { ethers } from "ethers";

      const ERC20_ABI = [
        "function approve(address spender, uint256 amount) returns (bool)",
        "function allowance(address owner, address spender) view returns (uint256)",
      ];

      const checkAndSetAllowance = async (
        signer: ethers.Signer,
        tokenAddress: string,
        approvalAddress: string,
        amount: string,
      ) => {
        const erc20 = new ethers.Contract(tokenAddress, ERC20_ABI, signer);
        const signerAddress = await signer.getAddress();
        const allowance = await erc20.allowance(signerAddress, approvalAddress);

        if (allowance < BigInt(amount)) {
          const tx = await erc20.approve(approvalAddress, amount);
          await tx.wait();
          console.log("Approval set.");
        } else {
          console.log("Allowance already sufficient.");
        }
      };

      await checkAndSetAllowance(
        signer,
        quote.action.fromToken.address,
        quote.estimate.approvalAddress,
        quote.action.fromAmount,
      );
      ```
    </CodeGroup>

    <Warning>
      If an approval transaction was sent, re-fetch the quote before executing. The `transactionRequest` returned in the original quote contains gas estimates that can become stale by the time the approval confirms. Call `GET /v1/quote` again with the same parameters to get a fresh transaction.
    </Warning>
  </Step>

  <Step title="Execute the transaction">
    Send the transaction using the `transactionRequest` object from the quote response.

    <CodeGroup>
      ```ts TypeScript theme={"system"}
      const tx = await signer.sendTransaction(quote.transactionRequest);
      console.log("Transaction sent:", tx.hash);

      const receipt = await tx.wait();
      console.log("Transaction confirmed:", receipt.hash);
      ```
    </CodeGroup>

    That's it. Composer handles the swap and deposit in a single atomic transaction.
  </Step>

  <Step title="Track the status">
    For same-chain transactions, the transaction is complete once confirmed. For cross-chain transfers, poll the `/status` endpoint until the transfer reaches `DONE` or `FAILED`:

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

      // For cross-chain transfers, poll until complete
      if (quote.action.fromChainId !== quote.action.toChainId) {
        let status;
        do {
          status = await getStatus(
            tx.hash,
            quote.action.fromChainId,
            quote.action.toChainId,
          );
          console.log("Status:", status.status, status.substatus);

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

        console.log("Final status:", status.status);
      }
      ```
    </CodeGroup>

    | Status      | Meaning                                   |
    | ----------- | ----------------------------------------- |
    | `NOT_FOUND` | Transaction submitted but not yet indexed |
    | `INVALID`   | Hash is not tied to the requested tool    |
    | `PENDING`   | Transaction is in progress                |
    | `DONE`      | Completed successfully                    |
    | `FAILED`    | Transaction failed                        |

    For the full status reference, see [Transaction Status Tracking](/introduction/user-flows-and-examples/status-tracking).
  </Step>
</Steps>

***

## Full Working Example

Copy-paste this complete example to run your first Composer transaction:

<CodeGroup>
  ```ts TypeScript theme={"system"}
  import { ethers } from 'ethers';
  import axios from 'axios';

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

  // --- Configuration ---
  const PRIVATE_KEY = 'YOUR_PRIVATE_KEY';
  const RPC_URL = 'https://mainnet.base.org';
  const FROM_CHAIN = 8453; // Base
  const FROM_TOKEN = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC on Base
  const TO_TOKEN = '0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A'; // Morpho vault token
  const FROM_AMOUNT = '1000000'; // 1 USDC

  // --- Setup ---
  const provider = new ethers.JsonRpcProvider(RPC_URL);
  const signer = new ethers.Wallet(PRIVATE_KEY, provider);

  // --- Helpers ---
  const getQuote = async (fromAddress: string) => {
  const result = await axios.get(`${API_URL}/quote`, {
  params: {
  fromChain: FROM_CHAIN,
  toChain: FROM_CHAIN,
  fromToken: FROM_TOKEN,
  toToken: TO_TOKEN,
  fromAmount: FROM_AMOUNT,
  fromAddress,
  toAddress: fromAddress,
  },
  });
  return result.data;
  };

  const ERC20_ABI = [
  'function approve(address spender, uint256 amount) returns (bool)',
  'function allowance(address owner, address spender) view returns (uint256)',
  ];

  const ensureAllowance = async (
  tokenAddress: string,
  approvalAddress: string,
  amount: string
  ) => {
  const erc20 = new ethers.Contract(tokenAddress, ERC20_ABI, signer);
  const address = await signer.getAddress();
  const allowance = await erc20.allowance(address, approvalAddress);

  if (allowance < BigInt(amount)) {
  console.log('Setting allowance...');
  const tx = await erc20.approve(approvalAddress, amount);
  await tx.wait();
  console.log('Allowance set.');
  }
  };

  // --- Main ---
  const run = async () => {
  const address = await signer.getAddress();
  console.log('Wallet:', address);

  // 1. Get quote
  console.log('Requesting Composer quote...');
  const quote = await getQuote(address);
  console.log('Quote received. Tool:', quote.tool);
  console.log('Estimated output:', quote.estimate.toAmount);

  // 2. Approve
  await ensureAllowance(
  quote.action.fromToken.address,
  quote.estimate.approvalAddress,
  quote.action.fromAmount
  );

  // 3. Execute
  console.log('Sending transaction...');
  const tx = await signer.sendTransaction(quote.transactionRequest);
  console.log('Tx hash:', tx.hash);

  const receipt = await tx.wait();
  console.log('Confirmed in block:', receipt.blockNumber);
  console.log('Done! USDC deposited into Morpho vault.');
  };

  run().catch(console.error);

  ```
</CodeGroup>

***

## What Just Happened?

Behind the scenes, Composer:

1. **Identified the optimal path.** LI.FI's routing engine determined the best way to convert USDC into the Morpho vault token.
2. **Activated Composer from `toToken`.** The vault token destination signaled a Composer-capable route.
3. **Compiled eDSL instructions.** The Composer compiler generated bytecode for the onchain VM.
4. **Simulated the execution.** The full path was simulated before returning the quote, ensuring it will succeed.
5. **Executed atomically.** Your single transaction swapped USDC and deposited into Morpho in one atomic operation.

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Cross-Chain Composer" icon="bridge" href="/composer/lifi-api/guides/cross-chain-compose">
    Bridge + deposit and swap + bridge + deposit across EVM chains
  </Card>

  <Card title="Withdrawals Guide" icon="clock" href="/composer/lifi-api/guides/withdrawals">
    Add the other half of the user journey with withdraw flows
  </Card>

  <Card title="SDK Integration" icon="cube" href="/composer/lifi-api/guides/sdk-integration">
    Use the LI.FI SDK for managed execution with hooks and events
  </Card>

  <Card title="Vault Deposit Recipes" icon="book" href="/composer/lifi-api/recipes/vault-deposits">
    Copy-paste recipes for Morpho, Aave, Euler, and more
  </Card>
</CardGroup>

```
```
