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

# 支持的协议与链

> Composer 在两种集成路径中路由进入的每一个协议、vault 和链。

export const ComposeEdges = () => {
  const CHAIN_NAMES = {
    1: "Ethereum",
    10: "Optimism",
    14: "Avalanche",
    25: "Cronos",
    30: "Rootstock",
    50: "XDC",
    56: "BNB",
    100: "Gnosis",
    122: "Fuse",
    130: "Unichain",
    137: "Polygon",
    143: "Monad",
    146: "Sonic",
    148: "ShimmerEVM",
    196: "X Layer",
    242: "Plinga",
    250: "Fantom Opera",
    252: "Fraxtal",
    288: "Boba",
    324: "zkSync",
    480: "World Chain",
    988: "Stable",
    999: "HyperEVM",
    1088: "Metis Andromeda",
    1101: "Polygon zkEVM",
    1135: "Lisk",
    1284: "Moonbeam",
    1285: "Moonriver",
    1329: "Sei",
    1501: "BEVM Canary",
    1625: "Gravity Alpha",
    1923: "Swellchain",
    2741: "Abstract",
    4326: "MegaETH",
    5000: "Mantle",
    8217: "Kaia",
    8453: "Base",
    9745: "Plasma",
    13371: "Immutable zkEVM",
    33139: "ApeChain",
    34443: "Mode",
    42161: "Arbitrum",
    42220: "Celo",
    42793: "Etherlink",
    43114: "Avalanche",
    55244: "Superposition",
    57073: "Ink",
    59144: "Linea",
    60808: "BOB",
    80094: "Berachain",
    81457: "Blast",
    167000: "Taiko",
    534352: "Scroll",
    747474: "Katana",
    888888888: "Ancient8",
    1313161554: "Aurora"
  };
  const chainName = id => CHAIN_NAMES[id] || `Chain ${id}`;
  const [state, setState] = useState({
    data: null,
    error: null
  });
  const [filter, setFilter] = useState("");
  const [copied, setCopied] = useState(null);
  useEffect(() => {
    let cancelled = false;
    setState({
      data: null,
      error: null
    });
    const run = async () => {
      try {
        const response = await fetch("https://composer.li.quest/compose/zap-packs");
        if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`);
        const body = await response.json();
        if (!body || body.success !== true) {
          throw new Error(body?.error?.message ?? "Unexpected response shape");
        }
        if (!cancelled) setState({
          data: body.data,
          error: null
        });
      } catch (err) {
        if (!cancelled) setState({
          data: null,
          error: err.message ?? String(err)
        });
      }
    };
    run();
    return () => {
      cancelled = true;
    };
  }, []);
  if (state.error) {
    return <div style={{
      padding: "0.75rem 1rem",
      border: "1px solid #f5c6cb",
      background: "#f8d7da",
      color: "#721c24",
      borderRadius: "6px"
    }}>
        <strong>Failed to load routing edges.</strong>
        <div style={{
      fontFamily: "monospace",
      marginTop: "0.25rem",
      fontSize: "0.9em"
    }}>
          {state.error}
        </div>
      </div>;
  }
  if (!state.data) return <div>Loading routing edges…</div>;
  const packs = state.data;
  const byProtocol = {};
  for (const pack of packs) {
    if (!byProtocol[pack.protocol]) {
      byProtocol[pack.protocol] = {
        chains: new Set(),
        edgeCount: 0
      };
    }
    byProtocol[pack.protocol].edgeCount += pack.edges.length;
    for (const edge of pack.edges) {
      if (edge.in?.chainId) byProtocol[pack.protocol].chains.add(edge.in.chainId);
      if (edge.out?.chainId) byProtocol[pack.protocol].chains.add(edge.out.chainId);
    }
  }
  const protocolSummary = Object.entries(byProtocol).map(([protocol, info]) => ({
    protocol,
    chains: [...info.chains].sort((a, b) => a - b),
    edgeCount: info.edgeCount
  })).sort((a, b) => a.protocol.localeCompare(b.protocol));
  const rows = packs.flatMap(pack => pack.edges.map(edge => ({
    protocol: pack.protocol,
    type: edge.type,
    inAddress: edge.in?.address ?? "",
    inChainId: edge.in?.chainId ?? "",
    outAddress: edge.out?.address ?? "",
    outChainId: edge.out?.chainId ?? ""
  })));
  const needle = filter.trim().toLowerCase();
  const visible = needle ? rows.filter(row => [row.protocol, row.type, row.inAddress, String(row.inChainId), row.outAddress, String(row.outChainId)].some(field => field.toLowerCase().includes(needle))) : rows;
  const shortAddress = addr => addr && addr.length > 10 ? `${addr.slice(0, 6)}…${addr.slice(-4)}` : addr;
  const copyAddress = async (address, key) => {
    if (!address) return;
    try {
      if (navigator?.clipboard?.writeText) {
        await navigator.clipboard.writeText(address);
      } else {
        const el = document.createElement("textarea");
        el.value = address;
        el.setAttribute("readonly", "");
        el.style.position = "absolute";
        el.style.left = "-9999px";
        document.body.appendChild(el);
        el.select();
        document.execCommand("copy");
        document.body.removeChild(el);
      }
      setCopied(key);
      setTimeout(() => setCopied(current => current === key ? null : current), 1200);
    } catch {}
  };
  const addressCell = (address, chainId, rowIdx, side) => {
    const key = `${rowIdx}-${side}`;
    const isCopied = copied === key;
    return <>
        <code role="button" tabIndex={0} title={isCopied ? "Copied!" : `Click to copy ${address}`} onClick={() => copyAddress(address, key)} onKeyDown={e => {
      if (e.key === "Enter" || e.key === " ") {
        e.preventDefault();
        copyAddress(address, key);
      }
    }} style={{
      cursor: "pointer",
      background: isCopied ? "#d4edda" : undefined,
      color: isCopied ? "#155724" : undefined,
      textDecoration: "underline dotted",
      textDecorationColor: "#999",
      borderRadius: "3px",
      padding: isCopied ? "0 2px" : undefined
    }}>
          {isCopied ? "Copied!" : shortAddress(address)}
        </code>
        <span style={{
      opacity: 0.6
    }}> · {chainName(chainId)}</span>
      </>;
  };
  return <>
      <h3 style={{
    marginTop: "0",
    marginBottom: "0.5rem"
  }}>
        Supported protocols ({protocolSummary.length})
      </h3>
      <div style={{
    marginBottom: "0.75rem",
    fontSize: "0.9em",
    opacity: 0.75
  }}>
        Every protocol Composer routes into, with the chains it covers. See the
        vault-level detail in the table below.
      </div>
      <table>
        <thead>
          <tr>
            <th className="text-left">
              <strong>Protocol</strong>
            </th>
            <th className="text-left">
              <strong>Chains</strong>
            </th>
          </tr>
        </thead>
        <tbody>
          {protocolSummary.map(({protocol, chains}) => <tr key={protocol}>
              <td style={{
    textTransform: "capitalize"
  }}>
                <strong>{protocol}</strong>
              </td>
              <td>{chains.map(chainName).join(", ")}</td>
            </tr>)}
        </tbody>
      </table>

      <h3 style={{
    marginTop: "1.75rem",
    marginBottom: "0.5rem"
  }}>
        Vaults ({rows.length})
      </h3>
      <div style={{
    marginBottom: "0.5rem",
    fontSize: "0.9em",
    opacity: 0.75
  }}>
        Every vault, lending market, staking pod, and mint/burn pair Composer
        routes into, with input and output token addresses.
      </div>
      <div style={{
    marginBottom: "0.5rem"
  }}>
        <input type="text" value={filter} onChange={e => setFilter(e.target.value)} placeholder="Filter by protocol, type, address, chainId…" style={{
    width: "100%",
    padding: "0.5rem 0.75rem",
    border: "1px solid #ccc",
    borderRadius: "6px",
    fontSize: "0.95em"
  }} />
        <div style={{
    marginTop: "0.25rem",
    fontSize: "0.85em",
    opacity: 0.7
  }}>
          Showing <code>{visible.length}</code> of <code>{rows.length}</code>{" "}
          edges across <code>{packs.length}</code> protocols · click an address
          to copy
        </div>
      </div>
      <table>
        <thead>
          <tr>
            <th className="text-left">
              <strong>Protocol</strong>
            </th>
            <th className="text-left">
              <strong>Type</strong>
            </th>
            <th className="text-left">
              <strong>In</strong>
            </th>
            <th className="text-left">
              <strong>Out</strong>
            </th>
          </tr>
        </thead>
        <tbody>
          {visible.map((row, idx) => <tr key={`${row.protocol}-${row.type}-${row.inAddress}-${row.outAddress}-${idx}`}>
              <td>
                <code>{row.protocol}</code>
              </td>
              <td>
                <code>{row.type}</code>
              </td>
              <td>{addressCell(row.inAddress, row.inChainId, idx, "in")}</td>
              <td>{addressCell(row.outAddress, row.outChainId, idx, "out")}</td>
            </tr>)}
        </tbody>
      </table>
    </>;
};

本页列出了 Composer 路由进入的每一个协议 — vault、借贷市场、质押 pod 和收益策略 — 并附有确切的代币地址和链。用它来在集成前核实覆盖范围、制作供应商对比材料，或查找某个特定 vault 的地址。该列表同时适用于 Composer API 和 LI.FI API 集成。

此视图分为两部分：

* **支持的协议** — 每个协议一行的列表，展示各协议覆盖的链。
* **Vaults** — 一个可搜索的表格，包含每一个 vault、借贷市场、质押 pod 和 mint/burn 对，以及输入和输出代币地址。

<details>
  <summary><strong>边（edge）在底层如何工作</strong> — 面向工程师</summary>

  路由边是对旧版 zap pack 的 compose 原生替代：协议特定的降级处理，将一个通用的 `lifi.zap` 调用转换为具体的 VM 指令。每条边在特定链上声明一个输入代币和一个输出代币，外加一个边类型（`enter-position`、`exit-position`、`mint-burn`、…）。规划器在各条边之间搜索，以组合出多跳路由。实时数据从 `/compose/zap-packs` 获取。
</details>

<ComposeEdges />

## 请求集成新协议

Composer 集成满足以下两项要求的协议：

* 该协议必须位于**与 EVM 兼容的链**上。
* 该协议必须返回**代币化头寸**（例如 vault 代币、aToken、LST）。

请参阅[新协议接入](/composer/for-protocols/integration-guide)或联系 LI.FI 团队以启动该流程。
