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

# 比特币提供商

> 比特币架构

export const SupportedTools = ({chainId}) => {
  const [chains, setChains] = useState(null);
  const [tools, setTools] = useState(null);
  const [error, setError] = useState(null);
  useEffect(() => {
    const fetchChains = async () => {
      try {
        const response = await fetch('https://li.quest/v1/chains?chainTypes=EVM,SVM,UTXO,MVM,TVM');
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const jsonData = await response.json();
        setChains(jsonData.chains);
      } catch (err) {
        setError(err.message);
      }
    };
    fetchChains();
  }, []);
  useEffect(() => {
    const fetchTools = async () => {
      try {
        const response = await fetch('https://li.quest/v1/tools');
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const jsonData = await response.json();
        setTools(jsonData);
      } catch (err) {
        setError(err.message);
      }
    };
    fetchTools();
  }, []);
  const parseBridges = (bridges, selectedChainId) => bridges.map(bridge => {
    const fromChainIds = bridge.supportedChains.filter(connection => connection.toChainId === selectedChainId).map(connection => connection.fromChainId);
    const toChainIds = bridge.supportedChains.filter(connection => connection.fromChainId === selectedChainId).map(connection => connection.toChainId);
    const connectedChainIds = [...new Set([...fromChainIds, ...toChainIds])];
    return {
      ...bridge,
      fromChainIds,
      toChainIds,
      connectedChainIds
    };
  }).filter(bridge => bridge.connectedChainIds.length).sort((a, b) => b.connectedChainIds.length - a.connectedChainIds.length);
  const parseExchanges = (exchanges, selectedChainId) => exchanges.filter(exchange => exchange.supportedChains.includes(selectedChainId));
  const renderChains = chains => <div className="p-2">
      <div className="flex flex-wrap gap-4">
        {chains.map(chain => <div key={chain.key} className="relative group flex-shrink-0">
            <img src={chain.logoURI} alt={chain.name} className="w-10 h-10 rounded-full object-cover not-prose" />
            <div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 hidden group-hover:block bg-gray-800 text-white text-xs rounded py-1 px-2 whitespace-nowrap z-10">
              {chain.name}
            </div>
          </div>)}
      </div>
    </div>;
  const renderTools = (tools, chains) => {
    const bridges = parseBridges(tools.bridges, Number(chainId));
    const exchanges = parseExchanges(tools.exchanges, Number(chainId));
    return <div>
      <h2>Supported Bridges</h2>
      <ul>
        {bridges.map(bridge => <li>
            {bridge.name} (<code>{bridge.key}</code>) connects to:
            {renderChains(chains.filter(chain => bridge.connectedChainIds.includes(chain.id)))}
          </li>)}
      </ul>

      <h2>Supported Exchanges</h2>
      <ul>
        {exchanges.map(exchange => <li>{exchange.name} (<code>{exchange.key}</code>)</li>)}
        {exchanges.length === 0 ? '-' : ''}
      </ul>
    </div>;
  };
  if (error) return <div>Error: {error}</div>; else if (chains && tools) return renderTools(tools, chains); else return <div>Loading...</div>;
};

LI.FI 提供原生比特币、主要 EVM 链和 Solana 之间的无缝原生比特币桥接和交换。

<SupportedTools chainId="20000000000001" />

## 请求报价

可以使用与 EVM 相同的端点请求比特币的报价。唯一的区别是当源链是比特币时的交易数据。

<Note>
  当源链是比特币时，`fromAddress` 需要有足够的 UTXO 来覆盖请求的交易金额，否则不会返回报价。

  此检查是为了在比特币上构建交易，其中检查并以最有效的方式组合用户钱包的 UTXO。
</Note>

## 执行交易

### 交易数据

在检索报价后，需要将资金发送到响应中提供的 BTC 金库地址，并附上备注。

* 备注功能：类似于 Thorchain，LI.FI 使用备注进行 BTC 到 EVM 的交换。BTC 交易中的备注指定交换的目标地址和链。

* 交易处理：从 BTC 离开并转到 EVM 的交易需要发送到 EVM 地址。备注确保交换详细信息由验证器正确处理。

<Note>
  注意：仅在合理时间内（\~30 分钟）发送交易。始终建议请求最新的报价以确保获得最新信息。
</Note>

<Warning>
  **修改比特币交易数据的风险**

  修改从我们 API 收到的 PSBT 或原始比特币交易数据（例如删除输出、更改金额或编辑操作码/脚本）可能会使签名或支出条件无效，并导致资金的不可逆转损失。

  除非您是专家并且已经明确与我们确认您打算进行的修改，否则请勿更改 PSBT。
</Warning>

<Note>
  transactionRequest 对象中的 `data` 是 PSBT（部分签名的比特币交易），需要通过解码 PSBT 来检索备注。
</Note>

### 从 PSBT 检索备注

可以使用任何库（如 [bitcoinjs](https://github.com/bitcoinjs/bitcoinjs-lib) 或 [scure-btc-signer](https://github.com/paulmillr/scure-btc-signer)）解码 PSBT。

这是使用 `bitcoinjs` 的示例：

```typescript theme={"system"}
const psbtHex = transactionRequest.data;

// 从十六进制数据创建 PSBT 对象
const psbt = Psbt.fromHex(psbtHex, { network: networks.bitcoin });

// 在交易输出中查找 OP_RETURN 输出
const opReturnOutput = psbt.txOutputs.find((output) => {
  if (output?.script) {
    // 将输出脚本转换为十六进制字符串以进行检查
    const scriptHex = Array.from(output.script)
      .map((b) => b.toString(16).padStart(2, "0"))
      .join("");

    // 检查脚本是否以 OP_RETURN 操作码（0x6a）开头
    return scriptHex.startsWith("6a");
  }
  return false;
});

// 如果存在 OP_RETURN 输出，则将其脚本数据解码为 UTF-8 文本（备注）
const memo = opReturnOutput?.script
  ? new TextDecoder().decode(
      new Uint8Array(Object.values(opReturnOutput.script))
    )
  : undefined;
```
