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

# 快速入门

> 在 5 分钟内执行你的第一笔 Composer 交易。通过单次 API 调用将 USDC 存入 Base 上的 Morpho vault。

本快速入门将带你完成一笔同链 Composer 交易，将 USDC 存入 Base 上的 Morpho vault。读完之后，你将理解请求报价、授权代币、执行交易和跟踪状态的完整 flow。

Composer 使用标准的 LI.FI 端点。将 `toToken` 设置为受支持协议的代币地址，路由引擎就会自动返回一条 Composer 路由。

<Tip>
  本快速入门涵盖的是同链存入。Composer 也支持跨 EVM 链的
  跨链存入。有关这些 flow，请参阅[跨链 Composer
  模式](/composer/lifi-api/guides/cross-chain-compose)。
</Tip>

## 前置条件

* 一个在 Base 上持有 USDC 的钱包地址（哪怕只有 1 USDC 这样的小额也可以）
* Node.js 18+（用于 TypeScript 示例）或 `curl`

***

<Steps>
  <Step title="Get a Composer quote">
    请求报价，并将 `toToken` 设置为 vault 代币地址：

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

    **关键参数：**

    | 参数           | 值               | 说明                |
    | ------------ | --------------- | ----------------- |
    | `fromChain`  | `8453`          | Base 链 ID         |
    | `toChain`    | `8453`          | Base（同链存入）        |
    | `fromToken`  | `0x8335...2913` | Base 上的 USDC      |
    | `toToken`    | `0x7BfA...34A`  | Morpho vault 代币地址 |
    | `fromAmount` | `1000000`       | 1 USDC（6 位小数）     |

    <Tip>
      `toToken` 始终是目标协议的 **vault 代币地址**。你可以在协议自己的应用或文档中找到 vault 代币地址。
    </Tip>

    响应中包含 `transactionRequest`（一笔可直接签署的 EVM 交易），以及预估的输出金额和所使用的工具。对于 Composer 路由，`tool` 字段将为 `"composer"`。

    <Tip>
      报价反映的是当前市场状况。如果用户查看某个报价超过 30 秒，请在签署前重新获取报价，以得到最新的定价和模拟结果。
    </Tip>
  </Step>

  <Step title="Set token allowance">
    在执行之前，请确保 LI.FI Diamond 合约已获得花费你代币的授权。授权地址在报价响应的 `quote.estimate.approvalAddress` 中返回。它指向 [LI.FI Diamond](https://etherscan.io/address/0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE)，这是一个部署在所有受支持链上的、经过审计的合约。

    <Note>
      如果你发送的是原生代币（例如 ETH），请跳过此步骤。原生代币不需要授权。
    </Note>

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

      const checkAndSetAllowance = async (
        publicClient: PublicClient,
        walletClient: WalletClient,
        tokenAddress: Address,
        approvalAddress: Address,
        amount: bigint,
      ) => {
        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 });
          console.log("Approval set.");
        } else {
          console.log("Allowance already sufficient.");
        }
      };

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

    <Warning>
      如果已发送授权交易，请在执行前重新获取报价。原始报价中返回的 `transactionRequest` 包含 gas 估算，而这些估算在授权确认时可能已经过时。使用相同的参数再次调用 `GET /v1/quote`，以获得一笔新的交易。
    </Warning>
  </Step>

  <Step title="Execute the transaction">
    使用报价响应中的 `transactionRequest` 对象发送交易。

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

      const [account] = await walletClient.getAddresses();

      const hash = await walletClient.sendTransaction({
        account,
        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),
        chain: walletClient.chain,
      });
      console.log("Transaction sent:", hash);

      const receipt = await publicClient.waitForTransactionReceipt({ hash });
      console.log("Transaction confirmed:", receipt.transactionHash);
      ```
    </CodeGroup>

    就是这样。Composer 会在单笔原子交易中处理交换和存入。
  </Step>

  <Step title="Track the status">
    对于同链交易，交易一经确认即告完成。对于跨链转账，请轮询 `/status` 端点，直到转账到达 `DONE` 或 `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(
            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      | 含义           |
    | ----------- | ------------ |
    | `NOT_FOUND` | 交易已提交但尚未被索引  |
    | `INVALID`   | 哈希未与所请求的工具关联 |
    | `PENDING`   | 交易正在进行中      |
    | `DONE`      | 成功完成         |
    | `FAILED`    | 交易失败         |

    有关完整的状态参考，请参阅[交易状态跟踪](/introduction/user-flows-and-examples/status-tracking)。
  </Step>
</Steps>

***

## 完整可运行示例

复制粘贴这个完整示例，运行你的第一笔 Composer 交易：

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

  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 account = privateKeyToAccount(PRIVATE_KEY as Hex);
  const publicClient = createPublicClient({ chain: base, transport: http(RPC_URL) });
  const walletClient = createWalletClient({ account, chain: base, transport: http(RPC_URL) });

  // --- 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 ensureAllowance = async (
    tokenAddress: Address,
    approvalAddress: Address,
    amount: bigint
  ) => {
    const allowance = await publicClient.readContract({
      address: tokenAddress,
      abi: erc20Abi,
      functionName: 'allowance',
      args: [account.address, approvalAddress],
    });

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

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

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

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

    // 3. Execute
    console.log('Sending transaction...');
    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),
    });
    console.log('Tx hash:', hash);

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

  run().catch(console.error);
  ```
</CodeGroup>

***

## 刚才发生了什么？

在幕后，Composer 完成了：

1. **确定了最优路径。** LI.FI 的路由引擎确定了将 USDC 转换为 Morpho vault 代币的最佳方式。
2. **从 `toToken` 激活了 Composer。** vault 代币作为目标发出了一个支持 Composer 的路由信号。
3. **编译了 eDSL 指令。** Composer 编译器为链上 VM 生成了字节码。
4. **模拟了执行。** 在返回报价之前，会对完整路径进行模拟，以确保其能够成功。
5. **原子式执行。** 你的这一笔交易在一个原子操作中完成了 USDC 的交换和存入 Morpho。

***

## 后续步骤

<CardGroup cols={2}>
  <Card title="Cross-Chain Composer" icon="bridge" href="/composer/lifi-api/guides/cross-chain-compose">
    跨 EVM 链的桥接 + 存入以及交换 + 桥接 + 存入
  </Card>

  <Card title="Withdrawals Guide" icon="clock" href="/composer/lifi-api/guides/withdrawals">
    通过提款 flow 补齐用户旅程的另一半
  </Card>

  <Card title="SDK Integration" icon="cube" href="/composer/lifi-api/guides/sdk-integration">
    使用 LI.FI SDK 进行带有 hook 和事件的托管式执行
  </Card>

  <Card title="Vault Deposit Recipes" icon="book" href="/composer/lifi-api/recipes/vault-deposits">
    针对 Morpho、Aave、Euler 等的可复制粘贴 recipe
  </Card>
</CardGroup>
