Skip to main content
你可以接入任何能够验证来自输出链的有效载荷的验证层。LI.FI intent 支持 Open Intents Framework (OIF) 所支持的任何预言机。OIF 预言机验证层需要支持以下内容:
  1. 一个提交接口,供求解器提交其已填充的输出。它应接受任意的待验证载荷,然后调用相关的输出结算合约以检查这些载荷是否有效。如果预言机能够自行证明合约调用、事件或存储,则提交是自动的,此要求不适用。 预言机实现可以通过 IAttester 接口来验证输出是否已被填充:
  2. 实现验证接口,使输入链能够检查某个输出是否已被填充:
在实现预言机系统时,请注意以下几点:
  • 提交或接收接口不必标准化,但你必须记录文档,以便求解器能够实现它们。
  • 预言机系统可以采用自动中继或手动中继。
  • 预言机系统的信任假设必须明确,以便意图签发者和求解器能够做出明智的决策。
有关更多文档,请参阅 OIF 贡献指南。这些要求规定了一个合规的 OIF 兼容预言机系统应如何运作。

已实现的验证接口

有三种类型的验证接口:
  1. 自助式(Self-serve):在此类验证接口中,提交有效载荷会生成一个链下证明,该证明必须被收集并提交到输入链上。
    • Polymer —— EVM、Tron 和 Solana
    • Wormhole —— EVM 和 Solana(当前主网部署见下文)
  2. 自动式(Automatic):在此类验证接口中,提交有效载荷会自动将相关证明送达输入链上。
  3. 同链式(Same Chain):使用输出结算为其自身生成预言机证明,从而跳过专用的预言机系统。
选择预言机系统由意图签发者决定。预言机的安全性正是交付背后的支撑:它证明输出确实已经抵达目标链,然后才会将用户的输入释放给求解器。较弱的预言机可能会让输入在一次从未真正发生的交付上被支付出去。 不同预言机达成这一证明的方式各不相同,在信任模型、速度和成本上各有权衡,请选择权衡取舍适合你的意图的那一个。无论你选择哪一个,求解器都必须支持该预言机,否则意图将无法被填充。
速度与价格
某些预言机系统比其他预言机系统快得多。虽然速度不影响用户的资产交付,但它对求解器很重要。选择还款速度快的预言机系统能带来更便宜的意图,因为求解器可以更快地周转其资金。
如果预言机网络更新了其文档而未通知 LI.FI 团队,则以下文档可能已过时。请始终参考预言机系统的官方文档,以获取最准确、最新的信息。

OutputSettlerSimple

你可以将 OutputSettlerSimple——一个输出结算器——用作同链意图的预言机。要使用它,请在填充输出后调用 setAttestation。
如果输出结算器同时被配置为输入预言机和输出预言机,调用 setAttestation 将会公开已填充的输出,供输入结算进行验证。

Solana 上的同链意图

在 Solana 上,不存在 setAttestation 这一步——填充本身就会生成证明。output_settler_simple::fill(或 native_fill)会在填充成功时创建一个 LocalAttestation PDA,而 finalise 会直接验证该账户。整个过程不涉及任何预言机程序,也没有回调流程。作为替代,单输出订单可以使用先释放输入的结算:托管程序向求解器付款,紧凑填充在同一笔交易中交付输出,全程不产生任何认证。账户模型请参阅认证。

Polymer

Polymer 是一个基于事件的系统,以低成本提供实时证明。其安全性基于通过 Polymer 打包的状态,区块头来源于定序器的预确认。
  • gas 成本低。
  • 延迟低,可实现快速结算。
  • Polymer 一次只能证明一个输出,因此求解器必须自行实现批量证明才能保持效率。
  • 广播(Broadcast)
用于验证的目标事件是 OutputFilled,它在输出被填充时发出。
使用 Polymer 的 Prove API,你可以生成该事件的证明,然后将其提交到 receiveMessage。 有关获取 Polymer 证明的集成示例,请参阅 lintent.org/polymer 或所进行的 receiveMessage 调用。

Solana 上的 Polymer

Solana 上的 Polymer 预言机是 oracle_polymer 这个 Anchor 程序(部署名称:polymer;IDL:polymer.json)。其流程与 EVM 版本一致——验证填充、发出可证明的日志、在输入链上验证证明——但机制上是 Solana 式的:
  • 输出侧(Solana 上的一次填充):填充之后,求解器通过以下两条指令之一导出该填充。两者都会针对每个描述对应的 LocalAttestation 进行验证,并发出一行 Prove: 日志,供 Polymer 在输入链上进行证明:
    • submit(source, fill_descriptions) —— 无需许可,且不消费认证。它唯一的具名账户是 oracle_polymer 状态 PDA;remaining_accounts 为每个载荷放入一个只读认证。认证会原样保留,因此如果日志丢失或未能被证明,可以直接再次提交。
    • submit_consume(source, fill_descriptions) —— 还会消费每个认证,即关闭它并退还其租金(如果记录中的支付方是可执行账户,该认证只会被标记为已消费,租金则被推迟退还)。具名账户:submitter(签名者)、oracle_polymer、Polymer 的 local_consumer PDA(2m1udXnYmaxjSXBBE88yZZcwrtT127DqLcwK9B3WCAus)以及 intents_protocol。remaining_accounts 为交错排列的可写账户对 [attestation_0, rent_refund_0, attestation_1, rent_refund_1, …]。对于每个填充载荷,submitter 必须是该认证中记录的租金支付方——即签署该次填充的 Solana 账户,而不是 filler_data 中的求解器 id——否则会以 UnauthorizedFillSubmitter 失败。因此,一个消费型批次不能混合来自不同填充者的填充。未填充载荷则任何人都可以提交。
    已消费的认证不再是有效证明,因此两条指令都无法再次导出它。请只在不再需要重新提交时才进行消费——例如,在某次 submit 的日志已经被证明之后。
  • 输入侧(等待远程证明的 Solana 订单):接收流程最终都会以 receive_attest() 结束,它从证明器的结果账户读取已验证的事件,通过 ChainMapping 映射源链 id,并注册认证。
对于 Solana 输出,证明流水线如下:
1

在 Solana 上填充,然后提交

fill 会创建 LocalAttestation;oracle_polymer::submit(或 submit_consume)对其进行验证并发出 Prove: 日志。二者可以是两笔独立的交易,如果放得下也可以合并为一笔;关键在于哪笔交易携带了 Prove: 日志。
2

请求 submit 交易的证明

请 Polymer 的 Prove API 证明包含 Prove: 日志的那笔交易——即包含 submit 的交易,而不是只有填充的交易。如果对不含该日志的交易请求证明,会失败并提示 “transaction does not contain a Polymer Prove log”。将 srcChainId 设为 2。这是 Polymer 自己为 Solana 分配的协议 id,并非 LI.FI 的规范链 id——参见链 id 命名空间。
3

在输入链上交付给 receiveSolanaMessage

源自 Solana 的证明要交付给 EVM 预言机的 receiveSolanaMessage,而不是 receiveMessage。这两个入口点解析不同的载荷格式,并写入不同的存储,因此如果证明交付给错误的入口点会导致回滚。请根据输出链的类型选择正确的入口点。
对于接收远程证明的 Solana 输入,真实的 Polymer 证明通常有几千字节大——超过一笔 Solana 交易的容量——因此常见的做法是分块加载证明:
  1. 调用 create_polymer_accounts()。
  2. 每个分块调用一次 load_proof_chunk(chunk)。
  3. 以约 1,000,000 CU 的计算预算调用 validate_event()。
  4. 调用 receive_attest()。
对于能放入单笔交易的、未分块的证明,可以改用两笔交易:receive_load_proof(proof),然后是 receive_attest()。在这两条路径中,receive_attest() 都会关闭证明器的临时 PDA,并将其租金返还给出资签名者。
证明器的临时账户仅由签名者派生,因此同一签名者下两个并发的接收流程会发生冲突。第二次调用 create_polymer_accounts 会因账户已存在而失败,交错的分块加载也会写入同一个证明缓冲区并使其损坏。请按签名者串行化接收流程,或为并行的证明使用不同的签名者。
身份陷阱:在远程链上,Polymer 预言机通过其程序 id 来标识(Polymer 证明的是发出日志的程序的 returnedProgramId),而 Wormhole 预言机则通过其预言机 PDA(VAA emitter)来标识。在设置 MandateOutput.oracle 和配置远程侧预言机时,请使用正确的标识符——完整的字段规则见哪个身份对应哪个字段。

Wormhole

Wormhole 是一个基于消息传递的系统,允许合约广播 VAA(Verified Action Approvals,已验证操作批准)。VAA 是由 Wormhole Guardian 网络签名的消息。
  • 通过去中心化的 guardian 集合来保障安全。
  • 支持在单个 VAA 中批量证明多个输出。
  • 与替代方案相比速度较慢。
  • 广播(Broadcast)
Wormhole 的实现基于 Wormhole 的广播功能。消息必须通过 submit 接口提交到 Wormhole Implementation。消息必须被编码为 FillDescription 然后提交:
该消息随后会被发送给 Wormhole guardian 集合。一旦相关证明可用,求解器就可以将证明提交到输入链上的 receiveMessage 来验证其意图。 获取 Wormhole VAA 有很多方式,例如 Wormholescan api 或 Wormhole SPY。
Wormhole 的实现出于节省 gas 的目的,使用了比 Wormhole 的 Implementation.sol 效率高得多的验证算法。

部署

Wormhole oracle 已部署在 EVM 主网上;请参阅 Wormhole Oracle Mainnet 地址表。Solana 实现已部署在主网上;请参阅 Solana 程序地址。
  • 在 EVM 上,submit 以 consistency level 15 发布 Wormhole 消息。
  • 通过 Wormholescan API 获取已签名的 VAA。
  • 投递不是自动的:求解器(或任何调用者)必须将 VAA 提交到输入链上的 receiveMessage。

Solana 上的 Wormhole

Solana 上的 Wormhole 预言机实现是 oracle_wormhole 这个 Anchor 程序(程序名称:wormhole;IDL:wormhole.json)。其主网部署地址见部署概览:
  • 输出侧:submit(source, fill_descriptions) 会验证并消费各笔填充的 LocalAttestation 账户,然后发出一条覆盖所有这些填充的 Wormhole 消息(finality:finalized)。remaining_accounts 为交错排列的可写账户对 [attestation_0, rent_refund_0, …],且批次不能为空。由具名的 submitter 和一个全新的 post_message 账户签名;没有填充者签名的要求。VAA emitter 即为该预言机的 PDA。
  • 输入侧:receive 指令验证一条已发布的 VAA,通过 ChainMapping 映射 emitter 链,并为每个载荷创建一个 Attestation。remaining_accounts 为每个解码出的载荷放入一个可写的目标认证 PDA,按载荷顺序排列,不含租金退还槽位;由具名签名者为其创建付费。

Hyperlane

Hyperlane 是一个高度可定制的、基于消息传递的系统,允许合约通过选定的链间安全模块(ISM)发送和接收经过验证的消息。
  • 高度可定制的安全模块。
  • 支持在单条消息中批量证明多个输出。
  • 强抗审查保证。
  • 自动或手动中继。
Hyperlane 的实现基于 Hyperlane mailbox 上的 dispatch 函数。要配置 Hyperlane 中继,请在调用 submit 时提供相关的 customHook 和 customHookMetadata。
有关 dispatch hooks 的更多信息,请参阅 Hyperlane 文档。 由于 Hyperlane 是一个高度可定制的系统,你实际需要做的事情会因所选的 ISM 和中继方式而有很大差异。有关更多信息,请参阅 Hyperlane 文档。 Chainlink CCIP 是一个基于消息传递的系统,允许合约通过 Chainlink 预言机发送和接收经过验证的消息。
  • 通过 Chainlink 的预言机网络来保障安全。
  • 支持在单条消息中批量证明多个输出。
  • 符合 SOC2 和 ISO 27001 标准
Chainlink CCIP 的实现基于带有 EVM2AnyMessage 的 CCIP router ccipSend。要将已填充的输出提交到 CCIP 预言机,首先调用报价端点 getFee 来估算消息传递费用。然后在输出链上连同所需费用调用 submit,将消息发送到输入链。如果使用 ERC20 代币支付费用,请先批准代币转账。
CCIP 是自动的,一旦提交,Chainlink 就会将消息中继到输入链。

Axelar

Axelar 是一个基于消息传递的系统,允许合约发送和接收由 Axelar 的 Proof of State 网络验证的消息。
  • 通过 Axelar 的 Proof of State 网络来保障安全。
  • 支持在单条消息中批量证明多个输出。
  • 自动中继。
Axelar 的实现基于 Axelar Gateway 的 callContract 函数。要将已填充的输出提交到 Axelar 预言机,首先使用 gasService 来估算费用。然后在输出链上连同所需费用调用 submit,将消息发送到输入链。
Axelar 是自动的,一旦提交,Axelar 就会将消息中继到输入链。

LayerZero

LayerZero 是一个安全性可定制的、基于消息传递的系统,允许合约通过一组选定的去中心化验证网络(DVN)发送和接收经过验证的消息。
  • 可定制的安全性
  • 广受欢迎且链兼容性广泛。
  • 支持在单条消息中批量证明多个输出。
  • 费用较高
LayerZero 的实现基于 LayerZero endpoint 上的 send。要将已填充的输出提交到 LayerZero 预言机,首先使用 quote 函数来估算费用,然后在输出链上连同所需费用调用 submit,将消息发送到输入链。
LayerZero 是自动的,一旦提交,LayerZero 就会将消息中继到输入链。

Bitcoin

LI.FI Intents 拥有一个 Bitcoin 简化支付验证(SPV)客户端实现。它既可作为输出结算,也可作为验证层。 Bitcoin SPV 客户端需要持续维护。为了正确验证交易,需要大约每 10 分钟更新一次链,并在需要证明某笔交易时随时更新。 要生成交易证明,请参阅下面的代码: