Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
X (Twitter): https://x.com/AlongHudson
Email: frank.kong@smartcontract.com
Chainlink CCIP 概述
互操作性 是指在不同系统或网络之间交换信息的能力,即使它们是不兼容的。不同网络上的共享概念确保了各方理解并信任交换的信息。它还考虑了最终性的概念,通过验证信息的准确性和完整性来建立对交换信息的信任。
Web3生态系统已经变得多链化,随着第一层(layer-1)区块链和像应用链(appchains)、子网(subnets)等第二层(layer-2)扩展解决方案的兴起,每个网络都有其自己的可扩展性、安全性和信任度的方法。
然而,区块链是孤立的网络,它们独立运行且不能本地通信。要创建一个真正可互操作的Web3生态系统,数据和价值必须在链之间无缝移动。这就是跨链桥(bridges)的作用所在。
传统的跨链桥(bridges)是当今Web3生态系统中最大的问题之一,因为它们是中心化的。当您使用跨链桥将资金从一条链转移到另一条链时,您实际上是在将您的资金交给某个中心化的实体,并信任他们这些资金将出现在另一侧。令人惊讶的是,到目前为止已经发生了多起跨链桥被黑客攻击的事件。
Chainlink 跨链互操作性协议(CCIP)提供了一个简洁的单一接口,通过这个接口,去中心化应用(dApps)和 Web3 创业者可以安全地满足他们所有的跨链需求,包括代币转账和任意消息传递。
Chainlink CCIP 通过“通道”(lanes)连接区块链网络。“通道”是源区块链到目标区块链路径的唯一组合,例如,从区块链 A 到区块链 C,要使用一个通过。而如果要使用 Chainlink CCIP 反向传输消息,即从区块链 C 到区块链 A,您将需要使用不同的通道,即专属于 C 到 A 路径的通道。
Chainlink CCIP 将始终支持未来添加的每个新链的双向通道。逻辑上的问题是如何让 CCIP 知道通过哪个通道传输 CCIP 跨链消息。实际上这相当简单——每个由 CCIP 支持的区块链都有一个唯一的链选择器。
我们多次提到,通过使用 Chainlink CCIP,您可以 发送跨链消息。但是跨链消息可以包含哪些内容呢?使用 Chainlink CCIP,您可以:
转账(支持的)代币
发送任何种类的数据
同时发送代币和数据
CCIP 的发送者可以是:
外部拥有的账户(EOA)
任何智能合约
CCIP 的接收者可以是:
外部拥有的账户(EOA)
任何实现了 CCIPReceiver.sol
的智能合约
注意:如果您向外部拥有的账户(EOA)发送消息和代币,只有代币会到达。
目前,您可以将 CCIP 视为一个“黑盒”组件,并且只需了解路由器合约。我们将在接下来的章节中解释 Chainlink CCIP 的架构。
路由合约(Router) 是 CCIP 用户交互的主要合约。该合约负责启动跨链交互。每个链都有一个路由合约。在转账代币时,调用者必须批准路由合约能够“花费”(转移)调用者的代币。当消息在目标链上接收时,路由器是将代币“交付”到用户账户或将消息传递给接收者(receiver)合约。
Chainlink 跨链互操作性协议提供了一个单一的接口,以安全和去中心化的方式在多个链之间传输代币和数据。
Transporter介绍
Transporter是一个高度安全的接口,可以让您在不同的区块链之间转移代币,而且非常安心。Transporter基于行业领先的跨链协议Chainlink CCIP构建,提供深度防御的安全性。Transporter是一个跨链桥接应用,允许用户在不同的区块链上转移数字资产,并体验每个链所提供的最佳功能。只需几次点击,您就可以在确信Transporter由Chainlink CCIP的五级安全支持的情况下,跨链转移您的代币。Transporter还提供实时交易跟踪,并全天候提供支持服务。
热身练习:让我们桥接一些测试网代币
视频教程:
点击 "Connect wallet " 按钮连接你的钱包
选择你的源网络和目标网络。我将从 Avalanche Fuji 向 Ethereum Sepolia 发送代币。
然后选择你要发送的代币(我们也可以发送消息)。我将使用 USDC。
如果 USDC 无法获取,可以获取 Chainlink 测试代币 CCIP-BnM,用 CCIP-BnM 测试。你可以从下面的链接中获取无限多个 CCIP-BnM 代币。
然后选择您要发送的代币数量。我将选择 1 USDC。
并通过点击“Approve USDC”按钮,授权 Transporter 代表你转移该数量的代币。
下一步是选择您希望用于支付 CCIP 费用的代币。默认情况下,会选择源区块链的本地代币(例如 AVAX),但我将使用 LINK,因为它更便宜。
最后,点击 “Send” 按钮开始转账。
您现在可以坐下来放松会儿,并实时监控您的第一次跨链转账!
请收藏此网址,因为我们在整个训练营期间会经常使用它。
要导航到 Chainlink CCIP 区块浏览器,请单击 “View transaction” 按钮。
一旦源区块链上的交易完成,Chainlink DONs 就可以继续进行跨链转账。最终确定性是区块链技术中一个非常重要的安全概念,因此请确保在本次训练营结束前熟悉它。
最后,我们只需等待目标交易被包含在下一个 Ethereum Sepolia 区块中。
就是这样!传输完成,就这么简单!
当然,您随时可以回到 Chainlink CCIP 区块浏览器查看有关转账的更多详细信息。
Chainlink的 RWA(真实世界资产)代币化训练营为Web3开发者提供了一段激动人心的旅程,通过使用Chainlink 的基础设施,提升您将现实世界资产上链的技能。
在别人之前锁定您的名额。
👉
加入我们为期三天的训练营,您将在链上代币化一个房产。您将学习用不同的数据信息强化资产,安全地在链间转移它,并开发者其超越传统系统的潜力。
您将学到:
创建和丰富代币化资产的步骤
代币化资产链上开发的关键阶段
最大化代币化资产潜力的策略
为代币化资产提供统一的黄金记录
请确保在第 1 天之前您已完成以下步骤:
(获取测试代币,作业练习的时候需要)
如果你通过以上的途径无法获得,
密码: BigMac777
如果你有任何的问题,请在 discord 中询问: ,
或者扫描以下二维码加入微信群:
Chainlink SmartCon 是区块链和金融世界的交汇点,在这里,数字资产的行业先驱、DeFi 的头部项目和区块链开发者汇聚一堂,共同创造全球市场和 Web3 的未来。
👉
与众多开发者一起通过区块链技术开发真实世界的应用,探索如何将区块链技术的应用扩展到全球数十亿用户。
从全球金融机构、头部 DeFi 协议和极具创新的初创公司获得前沿的洞见。
Eric Schmidt, 前 CEO, Google
Jonathan Ehrenfeld Solé, 战略主管, SWIFT
Balaji Srinivasan, 天使投资人 & 前 CTO, Coinbase
Laurence Moroney, AI 部门领导, Google
Joseph Chan, 财经事务及库务局, 香港特别行政区
Oliver Roucloux, 业务分析师 & 产品负责人, Euroclear
Rashmi Misra, 人工智能与新兴技术业务发展总经理, Microsoft
术语 | 意义 |
---|
导航至
如果你需要测试网 USDT,你可以从 获取 10 个
一旦您的交易在源区块链(例如本例中的 Avalanche Fuji)上完成,您还可以在 Chainlink CCIP 区块浏览器上监控它,网址是 。
Interoperability (互操作性) | 在不同系统或网络之间交换信息的能力,即使它们不兼容 |
Chainlink CCIP | 允许你跨不同区块链发送代币和任意消息的协议 |
Lane (通道) | 源区块链到目标区块链路径的独特组合 |
Chain Selector (链选择器) | Chainlink CCIP 中区块链的唯一标识符 |
CCIP Message (CCIP 消息) | 您可以通过 CCIP 通道跨区块链发送的消息,该消息可以由代币和任意数据组成 |
Sender (发送者合约) | 发送 CCIP 消息的智能合约(用户的)或外部拥有的账户(EOA)。 |
Source blockchain (源链) | CCIP 消息发送来源的区块链 |
Receiver (接收者合约) | 接收CCIP消息的智能合约或外部拥有的账户(EOA)。 |
Destination blockchain (目标链) | CCIP 消息被发送至的区块链。 |
我们将要实现的另一个最佳实践称为防御性示例(Defensive Example),这是一种模式,它允许我们在不强制原始交易失败的情况下重新处理失败的消息。让我们解释一下它的工作原理。
接收和处理消息
当目标区块链接收到消息时,CCIP路由器会调用ccipReceive
函数。此函数作为合约处理传入CCIP消息的入口点,通过onlyRouter
和onlyAllowlisted
修饰符实施关键的安全检查。
以下是该过程的逐步分解:
通过ccipReceive
进入:
ccipReceive
函数被调用,传入一个包含待处理消息的Any2EVMMessage
结构体。
安全检查确保调用来自授权的路由器、已允许的源链和已允许的发送者。
处理消息:
ccipReceive
调用processMessage
函数,该函数是外部的,以利用Solidity的try/catch错误处理机制。注意:onlySelf
修饰符确保只有合约本身可以调用此函数。
在processMessage
内部,使用s_simRevert
状态变量检查模拟回滚条件。这个模拟由只有合约所有者可调用的setSimRevert
函数来切换。
如果s_simRevert
为假,processMessage
调用_ccipReceive
函数进行进一步的消息处理。
_ccipReceive
中的消息处理:
_ccipReceive
从消息中提取并存储各种信息,如messageId
、解码后的sender
地址、代币数量和数据。
然后发出一个MessageReceived
事件,表示消息处理成功。
错误处理:
如果在处理过程中发生错误(或触发了模拟回滚),ccipReceive
内的catch块将被执行。
失败消息的messageId
被添加到s_failedMessages
中,消息内容被存储在s_messageContents
中。
发出一个MessageFailed
事件,允许以后识别和重处理失败的消息。
失败消息的重新处理
retryFailedMessage
函数提供了一种机制,如果CCIP消息处理失败,可以用来恢复资产。它专门设计用来处理消息数据问题导致整个处理无法进行,但允许代币恢复的场景:
初始化:
只有合约所有者可以调用此函数,提供失败消息的messageId
和代币恢复用的tokenReceiver
地址。
验证:
使用s_failedMessages.get(messageId)
检查消息是否已失败。如果没有,就回滚交易。
状态更新:
将消息的错误代码更新为RESOLVED
,以防止重复进入和多次重试。
代币恢复:
使用s_messageContents[messageId]
检索失败的消息内容。
将与失败消息关联的锁定代币转移到指定的tokenReceiver
,作为一个逃生舱口,而无需再次处理整个消息。
事件发出:
发出一个MessageRecovered
事件,以信号代币已成功恢复。
这个函数展示了一个优雅的资产恢复解决方案,即使在消息处理遇到问题时也能保护用户的价值。
请注意,只有完成全部三天作业的参与者才有资格获得结业证书。
请在训练营结束前回答这 15 个问题。
通过 此表单 提交您的答案
简单:
什么是 CCIP Lane?
什么是 CCIP Chain Selector? 它与 Chain ID 有何不同?
CCIP 消息中的 gasLimit 有什么用途?
如何实时监控 CCIP 消息?
Chainlink CCIP 的三大主要功能是什么?请提供利用这些功能的潜在用例示例。
中等:
详细说明在智能合约中验证传入 CCIP 消息完整性的安全最佳做法。在 ccipReceive 函数中应实施哪些特定检查,为什么这些验证对于跨链 dApp 的安全性至关重要?
Chainlink CCIP 支持哪些代币转移机制?
描述风险管理网络在 Chainlink CCIP 中的作用,并解释“blessing”和“cursing”的过程。
讨论“最终性”概念在 Chainlink CCIP 中的重要性。源链的最终性如何影响 CCIP 中端到端交易时间?
讨论 CCIP 消息中 gasLimit 设置的最佳做法。开发人员如何准确估算和优化 gas 限制,以确保跨链交易的可靠执行?
困难:
解释 DefensiveExample 模式以及如何优雅地处理 CCIP 消息故障。
列出并解释需要使用 Chainlink CCIP Explorer 手动执行的情景。
解释为什么在 Chainlink CCIP 中将 extraArgs 设置为可变是最佳做法,并描述开发人员如何在他们的智能合约中实现这一点。
什么是 CCIP 速率限制?开发人员在设计应用程序以在这些限制内运行时应考虑哪些因素?
如果你使用 Chainlink CCIP 将任意数据与代币一起发送到一个外部拥有账户(Externally Owned Account),会发生什么?
Chainlink跨链互操作性协议(CCIP)不仅仅是一个简单的代币桥接解决方案。它是一个通用的跨链消息协议,用于在单个跨链交易中传输代币(价值)、消息(数据)或同时传输代币和消息——这被称为可编程代币传输。
实际上,CCIP可编程代币传输使智能合约能够在跨链传输代币的同时,附上关于接收智能合约在目标链上接收到这些代币后应如何处理这些代币的指令。这种将价值和指令结合在一起的革命性概念,就是消息跨链的一种,允许代币化资产在到达目的地后自动和动态地进行交互,基于此可以有很多的创新型应用。
在去中心化金融(DeFi)中,CCIP可编程代币传输使创建跨链原生 dApp 成为可能,例如,一个智能合约可以自动跨链传输代币,并将它们存入收益率最高的借贷市场。在传统金融(TradFi)中,CCIP 可编程代币传输使高级用例成为可能,例如跨链交割对支付(DvP)交易,其中持有稳定币的机构在其私有区块链上可以购买在不同私有或公共链上发行的代币化资产。
重要的是,CCIP 可编程代币传输使机构能够在不需要与该区块链集成或直接与之交互的情况下,与其他区块链网络上的智能合约和代币化资产进行交互。他们所需做的只是向 CCIP 发送指令,说明如何与该链交互,大大减少了他们的开销和与每个区块链网络点对点集成相关的风险。
就像TCP/IP是支撑互联网的通用标准一样,Chainlink CCIP是支撑 合约互联网(Internet of Contracts) 的通用标准。为了支持DeFi和TradFi中存在的各种跨链用例,CCIP允许以多种方式跨区块链传输数据和/或价值。
CCIP对 任意消息传递 的支持使开发人员能够跨区块链网络传输任何任意数据(编码为字节)。开发人员利用CCIP的任意消息传递使他们的智能合约应用成为跨链原生。
有了CCIP,源链上的智能合约可以调用目标链上任何智能合约的任何任意函数来触发任何任意行为(如果需要,还可以在源链上接收回调)。开发人员可以在单个消息中编码多个指令,使他们能够协调复杂的多步骤、多链任务。
CCIP代币传输通过高度审计和安全审查的代币池合约启用了链间代币传输。交易可以由外部拥有的账户(EOA)直接发起,例如通过像 Transporter 这样的桥接应用程序从用户钱包,或者由智能合约直接发起。然后,代币可以发送到EOA或智能合约。
为确保最高级别的安全性和卓越的用户体验,代币发行者可以在其代币的智能合约内直接使用CCIP,使其成为跨链原生代币。因此,任何用户或开发人员都可以使用CCIP跨链传输该发行者的代币的官方(规范)版本。各种第一层区块链和第二层扩展解决方案,如Wemix和Metis,通过将 CCIP作为他们的官方跨链基础设施 来集成,构建了这一概念,以推动他们的规范代币桥接。通过CCIP传输到这些区块链网络上的每个代币都是该链上该代币的规范表示。
开发人员可以通过以下三种主要方式集成CCIP进行代币传输:
销毁和铸造 (Burn and mint) — 在源链上销毁代币,并在目标链上铸造等量代币。这使得跨链原生代币具有统一的供应量。CCIP 通过销毁和铸造代币传输方法支持Circle的USDC 。
锁定和铸造 (Lock and mint) — 代币在它们原本发行的链上被锁定,而在目标链上铸造完全抵押的“包装”(Wrapped)代币。这些包装代币可以通过销毁和铸造在其他非原生目标链上转移,或者被销毁以在原始发行源链上解锁代币。Truflation 的 TRUF 代币利用锁定和铸造进行其在 CCIP 上的代币传输。
锁定和解锁 (Lock and unlock) — 代币在源区块链上被锁定,相应数量的代币在目标区块链上被释放。这使得支持没有销毁/铸造功能的代币或如果被包装会带来挑战的代币,例如原生区块链燃气代币。CCIP通过锁定和解锁代币传输方法支持 原生ETH传输 。
可编程代币传输将代币传输与任意消息传递结合起来。这使开发人员能够在单个交易中跨链传输代币(价值)和有关如何处理这些代币的指令(数据)。重要的是,可编程代币传输是原生构建到CCIP中的,为用户提供了最佳的安全性、可靠性、用户体验(例如,可组合性)和风险管理。
CCIP 可编程代币转账功能能够实现跨链交换用例,任何代币都可以通过 CCIP 连接到源链和目标链上的流动性池/去中心化交易所(DEX),从而有效进行桥接。
例如,建立在 CCIP 之上的跨链交换应用程序允许用户在 Arbitrum 上持有的 Token A 交换为 Optimism 上的 Token B。操作过程是先将 Arbitrum 上的 Token A 交换为 USDC,然后将 USDC 连同交换数据一起桥接到 Optimism,并自动将 USDC 交换为 Token B,最后发送到用户的钱包。这就是 CCIP 对原生 USDC 支持 的强大之处;它不仅支持通过销毁和铸造方式进行原生 USDC 的跨链转账,而且还支持同时传输有关到达目标链后如何处理 USDC 的数据——这是 CCIP 可编程代币转账的独特功能。
XSwap 是一个跨链交换协议,也是 构建计划(BUILD Project)的参与者,它使用 CCIP 的可编程代币转账功能来实现区块链网络之间的跨链交换。XSwap 使用 USDC 作为流动性代币。自推出以来,XSwap 用户已经发起了超过 1.3 亿美元的 CCIP 可编程代币转账。
其他使用CCIP可编程代币转账的用户包括 Transporter、ChainSwap、WEMIX PLAY、Amino Rewards等。
CCIP 可编程代币转账为跨链质押和再质押解锁了创新。最终用户可以直接从二层网络进行质押/再质押,其中 CCIP 用于将原生资产转回第一层区块链,并附带指示(再)质押资产到指定的(再)质押协议的指令。这降低了用户的 gas 成本,并为他们提供了从任何链上进行(再)质押的便利。
例如,EigenPie 正在集成 Chainlink CCIP,以使其用户能够直接将ETH存入他们的二层合约中,以接收相应的LRT(egETH),而无需离开链。一旦用户将ETH存入二层合约,CCIP的可编程代币转账将把代币桥接到以太坊,并附带指示将它们再质押到 Eigenlayer 的指令。然后,CCIP 被用来锁定在以太坊上铸造的egETH,并将其桥接回 L2,在那里进行铸造并发送到最终用户的钱包地址。
你可以在博客上阅读更多关于Chainlink对质押和再质押的支持:Chainlink 平台如何解锁 DeFi 中的 LST 和 LRT 采用。了解如何在 CCIP大师课:跨链质押版中实现Chainlink CCIP可编程代币转账,以获得更深入的技术洞察。
CCIP可编程代币转账对于实现跨链 货银兑付(DvP)交易 至关重要。在传统金融中,DvP指的是资产交付(例如,证券)和这些资产的支付必须同时发生(即,原子结算)。DvP 是一个重要的特性,可以避免交易双方,一方履行交易义务,但是另一方却没有履行的情况。
澳大利亚和新西兰银行集团有限公司(ANZ)展示了 CCIP 可编程代币转账如何实现跨境、跨链、跨货币的DvP 交易。在一次单一的跨链交易中,一种由本地货币支持的稳定币(NZ$DC)被转换为另一种不同国家货币的稳定币(A$DC),从买方的源链转移到卖方的目标链,并附带购买代币化资产(例如,珊瑚信用)的指令,随后这些资产被发送回客户在源链上的钱包。
想要了解更多信息,请查看与ANZ合作编写的案例研究 使用CCIP进行代币化资产的跨链结算,以及Chainlink联合创始人Sergey Nazarov和ANZ银行服务负责人Nigel Dobson在 Sibos小组讨论 中的对话。
发送和接收 CCIP 消息所需的最少代码
CCIP 最简架构
总结一下,通过 Chainlink CCIP,可以:
转移(支持的)代币
发送任何类型的数据
发送代币和数据
CCIP 接收者可以是:
EOA(外部拥有的账户)
任何实现了 CCIPReceiver.sol 的智能合约
注意:如果您向 EOA 发送消息和代币,只有代币会到达。
目前,您可以将 CCIP 视为一个“黑盒”组件,并且只需关注 Router 合约。我们将在后续章节中解释 Chainlink CCIP 的架构。
您可以使用任何区块链开发框架来使用 Chainlink CCIP。对于本次大师班,我们准备了 Hardhat、Foundry 和 Remix IDE 的步骤。
让我们创建一个新项目。
确保您已安装 Foundry。要检查,请运行以下命令:
创建一个新文件夹并命名为 ccip-masterclass
:
切换该文件夹:
通过运行以下命令创建一个新的 Foundry 项目:
导航到 https://remix.ethereum.org/ 并点击“Create new Workspace”按钮
或者,您可以克隆以下项目:
要使用 Chainlink CCIP,你需要与 @chainlink/contracts-ccip NPM 包中的 Chainlink CCIP 专用合约进行交互。
要安装它,请按照针对您将用于本次大师班的开发环境的特定步骤进行操作。
选项 1)
我们不能使用 git 子模块来安装 @chainlink/contracts-ccip
,因为这个包的内容并没有作为一个单独的 GitHub 仓库提供。这意味着我们不能运行 forge install
命令。
以下是解决方法:
在 .gitignore
文件中添加以下行
然后在终端中运行以下命令:
最后,在 foundry.toml
文件中添加以下几行:
选项 2) [2023年10月更新]
你可以运行
之后在 foundry.toml
或 remappings.txt
文件中设置 remappings:
创建一个新的 Solidity 文件,并粘贴以下内容。这是一个空合约,只是导入了 @chainlink/contracts-ccip
包中的一个合约。
编译它。如果编译成功并生成了新的 .deps/npm/@chainlink/contracts-ccip
文件夹,这意味着我们已将 @chainlink/contracts-ccip
包导入到 Remix IDE 工作区。
尽管如前所述,CCIP 的发送者和接收者可以是 EOA(外部拥有的账户)和智能合约,并且所有组合都是可能的,我们将要介绍最复杂的情况,即 CCIP 的发送者和接收者都是位于不同区块链上的智能合约。
要发送 CCIP 消息,源区块链上的智能合约必须调用 ccipSend()
函数,该函数定义在IRouterClient.sol
接口中。
正在发送的CCIP消息是来自Client
库中的EVM2AnyMessage
Solidity 结构体。
现在让我们理解一下我们正在发送的EVM2AnyMessage
结构体的每个属性代表什么以及如何使用它。
receiver (接收者)
接收者合约的地址。它可以是智能合约或外部拥有的账户(EOA)。使用abi.encode(receiver)
将地址编码为 Solidity 的字节数据类型。
data (数据)
通过 CCIP 消息发送的数据。这正是我们所说的 CCIP 消息可以携带的“任意类型的数据”。这些数据可以简单到像是“Hello, world!”这样的文本,也可以复杂到 Solidity 编写的结构体或者函数的签名。
tokenAmount (代币数量)
源链上表示的代币及其数量。这里我们指定了我们正在发送的代币(出自支持的代币)以及数量。这是一个EVMTokenAmount
结构体数组,它只包含两个属性:
token(代币)
- 我们在本地(源)区块链上发送的代币的地址
amount(
数量)
我们正在发送的代币数量。发送者必须批准CCIP路由器代表发送者花费这个数量,否则对ccipSend
函数的调用将会回滚。
目前,单一 CCIP 发送交易中可以发送的代币数量最多为5个。
feeToken (费用代币)
feeToken
是费用代币的地址。CCIP 支持使用 LINK 以及包括原生区块链原生代币(能支付gas fee的代币)和它们的 ERC20 包装版本在内的替代资产来支付费用。对开发者而言,这意味着你可以在源链上轻松支付费用,而 CCIP 将负责在目标链上执行操作。若要使用原生代币支付,例如在以太坊上的 ETH 或者在 Polygon 上的 MATIC,可以将 feeToken
设置为address(0)
。即便使用原生资产支付费用,Chainlink 去中心化预言机网络(DON)中的节点只会得到 LINK 作为奖励
extraArgs (额外参数)
用户填写EVMExtraArgsV1
结构体,然后使用_argsToBytes
函数将其编码为字节。该结构体由两个属性组成:
gasLimit
- CCIP 在目标区块链上的合约执行ccipReceive()
可以消耗的最大 gas 数量。未用完的 gas 不会退还。这意味着,例如,如果你将代币发送到 EOA,你应该把gasLimit
值设为0,因为EOA 无法实现ccipReceive()
(或任何其他)函数。为了估计目标合约的准确 gas limit(要使用的 gas 数量),请考虑利用 Ethereum 客户端 RPC 通过在receiver.ccipReceive()
函数上应用eth_estimateGas
,或者使用 Hardhat插件进行gas测试 ,或者进行 Foundry gas测试 。
strict
- 用于严格排序。你应该将其设置为false
。CCIP将始终按发送顺序处理来自特定发送者到特定目标区块链的消息。如果你在消息的extraArgs
部分设置strict: true
,并且如果ccipReceive
失败(回滚),它将阻止来自同一发送者的任何后续消息被处理,直到当前消息成功执行。使用此功能时应该非常小心,以避免无意中停止发送者的消息被处理。严格排序功能目前是实验性的,未来不保证其维护或进一步开发。
如果未指定 extraArgs
,即设置为 extraArgs: ""
,默认情况下将应用 200_000 的 gasLimit
并且不启用严格顺序。在生产环境中部署时,请确保 extraArgs
是可更改的。这允许您在链下构建此参数,并在需要时通过函数调用传递或存储在可更新的变量中。这样的设计使得 extraArgs
能够适应未来对CCIP的任何升级。
目标区块链上的智能合约要接收 CCIP 消息,必须实现IAny2EVMMessageReceiver
接口。@chainlink/contracts-ccip NPM 包提供了正确实现此接口的合约,称为 CCIPReceiver.sol
,但在接下来的章节中我们会更多地讨论它。现在,让我们理解在一般场景下必须实现的IAny2EVMMessageReceiver
接口的哪些函数。
如您所见,IAny2EVMMessageReceiver
接口中的ccipReceive()
函数接受来自 Client 库的Any2EVMMessage
结构体对象。这个结构体是接收到的CCIP消息在Solidity中的表现形式。请注意,这个Any2EVMMessage
结构体与我们在源区块链上用于发送的结构体——EVM2AnyMessage
——是不同的。它们并不相同。
现在让我们理解一下我们接收到的Any2EVMMessage
结构体的每个属性代表什么以及如何使用它:
messageId
- CCIP消息ID,在源链生成。
sourceChainSelector
- 源链选择器。
sender
- 发送者地址。如果源链是EVM链,使用abi.decode(sender, (address))
进行解码。
data
- CCIP消息中发送的有效载荷。例如,"Hello, world!"。
tokenAmounts
- 接收到的代币及其在目标链上的表示形式的金额。
回顾一下,下面是所需的最小架构图,用于发送和接收Chainlink CCIP消息:
您可能已经注意到,由于存在一些额外的摩擦点——例如设置钱包、获取测试网代币、等待跨链交易完成等,直接在测试网络上使用 CCIP 构建并不理想。这是因为测试网络用于测试,而本地环境(如 Foundry、Hardhat 或 Remix IDE)用于构建和单元测试。
为了解决这个问题,我们创建了 。在本章中,您将学习如何在本地模拟跨链交易,并以比在测试网络上工作快 2000 倍的速度使用 Chainlink CCIP 构建项目!
Chainlink Local 是一个可安装的依赖项,例如 OpenZeppelin。它提供了一个工具(Chainlink Local 模拟器),开发人员可以将其导入到他们的 Foundry、Hardhat 或 Remix IDE 项目中。这个工具在本地运行 ,这意味着开发人员可以在本地环境中快速探索、设计原型和迭代 CCIP dApps,只有在准备好在实际环境中测试时再将项目移动至测试网。
最重要的是,用 Chainlink Local 测试的智能合约可以在不做任何修改的情况下部署到测试网络(假设通过构造函数传入了网络特定的合约地址,如 Router 合约和 LINK 代币地址)。
要查看更详细的文档和更多示例,请访问 和 。
模拟器支持两种模式:
本地模式:使用在本地运行的开发区块链节点上的模拟合约,运行在 localhost
上。
分叉模式:使用已部署的 Chainlink CCIP 合约,通过多个进行工作。
在这个示例中,我们将使用分叉模式。
在作业中,您必须使用本地模式。
在本地模拟模式下工作时,模拟器会将一组智能合约预部署到一个空白的 Hardhat/Anvil 网络的EVM 状态,并通过调用 configuration()
函数公开它们的详细信息。尽管应该存在两个 Router 合约(sourceRouter
和 destinationRouter
),开发人员通过这两个不同的 Router 路由跨链消息,但在本地模式中它们都是在本地区块链节点上运行的同一个合约。
运行以下命令创建一个新的Foundry工程:
接下来,我们需要安装以下依赖项。如果您不想在每次安装后提交更改,请在运行以下每个命令后附加--no-commit
标志。
@chainlink/contracts
@chainlink/contracts-ccip
@openzeppelin/contracts
@chainlink/local
然后在您的 foundry.toml
或 remappings.txt
文件中设置以下 remappings:
删除 src/Counter.sol
、test/Counter.t.sol
和 script/Counter.s.sol
文件。在 src
文件夹中创建一个新的 XNFT.sol
文件,并粘贴上一页/练习中的 XNFT 智能合约内容。运行 forge build
来编译该合约。如果您正确安装了依赖项会编译成功。
然后在您的 foundry.toml
文件中添加 rpc_endpoints
部分。其最终版如下所示:
在上一个练习中,我们在两个不同的测试网络上执行了 7 个步骤:从部署、铸造、转移 XNFT 到另一个账户。使用 Chainlink Local,您可以编写一个测试文件在本地环境中执行相同的操作。测试中使用与测试网络上完全相同的 CCIP 合约,并且速度更快。
创建一个新的 test/XNFT.t.sol
文件。粘贴以下内容:
如果您尝试编译此合约,可能会遇到错误,因为缺少上一个练习中的 EncodeExtraArgs.sol
辅助智能合约。通过创建一个新文件 test/utils/EncodeExtraArgs.sol
并粘贴以下代码来修复此问题。
让我们分析一下目前的代码。我们从 @chainlink/local
包中导入了 CCIPLocalSimulatorFork
,这意味着我们使用的是分叉模式。我们创建了一个新的 CCIPLocalSimulatorFork 实例,并在所有不同的分叉之间使其保持持久性,使它能正常工作。
在作业中,您需要从 @chainlink/local
包中导入 CCIPLocalSimulator
。
我们还创建了两个分叉网络(即最新区块的区块链网络副本——最好固定这个区块号,而不是总是使用最新的区块号)Ethereum Sepolia 和 Arbitrum Sepolia 测试网络。现在我们可以使用 Foundry 在多个分叉网络之间切换,但我们选择 Ethereum Sepolia 作为开始的源区块链。
Foundry 使用分叉 ID 管理同一测试中不同的分叉网络,通常是 1、2、3 等这样的数字。分叉 ID 与 block.chainid
并不相同——后者是可以从Solidity 中获取的区块链网络的实际 Chain ID,例如,Ethereum Sepolia 的 Chain ID 是 11155111。
Foundry 中的 CCIPLocalSimulatorFork
提供了Register
辅助合约,其中包含 NetworkDetails
结构,如下所示:
在该结构体中已预先设置了一些测试网络的详细信息,并且其中特意不包含主网络。因此最好的做法是始终验证这些信息——否则模拟将无法工作:
我们将在 setup()
函数中直接执行前一个练习的步骤 1)和 2),如下所示:
在一个新的test
函数中直接执行前一个练习的其余步骤,如下所示:
最终的完整代码:
接下来,我们将通过几个步骤来看一下 CCIP 消息的工作流程。
发送方准备一条 CCIP 消息 (EVM2AnyMessage),用于其到目标区块链(chainSelector)的跨链交易。一条CCIP 消息包括以下信息:
接收方
数据载荷
代币地址和金额
费用代币
其他参数(gasLimit,strict)
发送方调用 Router.getFee()
获得支付给 CCIP 的总费用(包括 gas 和溢价),并授权这部分费用金额。
发送方调用 Router.ccipSend()
,提供他们想要发送的 CCIP 消息及其期望的目标链chainSelector
。考虑到要进行代币的转移,这部分金额必须被授权给 Router。
Router 验证接收到的消息(例如,支持的目标链IDchainId
和支持的目标链上的代币)。
Router 接收手续费并将其转给 OnRamp。
Router 接收代币并将其转到对应的代币池。如果发送方未将此代币授权给 Router,此操作将失败。
Router 将消息转发给正确的 OnRamp(基于目标链选择器chainSelector
)进行处理:
验证消息(代币数量、gasLimit、数据长度等)。
[对于代币转移] 确保转移的金额不超过该通道的总速率限制。
使用序列号sequence number对消息进行排序。
对于消息中包含的每种代币:通知代币池锁定/销毁代币。这也将验证该通道的代币池速率限制。
OnRamp 抛出包含已排序消息的事件。这会触发 DONs 处理消息。
生成一个 messageId 并返回给发送方。
Commiting DON中的节点监听已准备好发送的消息事件。
考虑到消息的安全性,为了防止重组攻击,消息必须处于finalized状态。
当 OnRamp 中的消息队列达到一定数量或时间时,将触发Committing DON 创建一个包含所有发送消息的带有承诺的Report(以批次形式)。此承诺以 Merkle Root的形式表示。
在Committing DON 达成共识后,包含 Merkle Root的Report被传送给目标链上的 CommitStore 合约。
风险管理网络(Risk Managemendt Network)通过CommitStore合约中“批准”该Merkle Root,以确保它可以正确代表 OnRamp 中经过排序的消息。
Merkle Root:Merkle Tree的根哈希值。它是对树中所有叶子节点(消息 M1-M4)的承诺。树中的每个节点都是其下方节点的哈希值。
Merkle Proof:为了证明消息 M1 被包含在 Merkle Root(承诺)中,证明者向仅持有Merkle Root的Verifier提供以下元素作为证明:
M1
H(M2)
H(H(M3),H(M4))
使用这个Merkle Proof,Verifier可以轻松地验证M1确实包含在其拥有的承诺(Merkle Root)中。
执行 DON 中的节点监听准备发送的消息事件,该过程类似于Committing DON。
考虑到消息的安全性,为了防止重组攻击,消息必须处于finalized状态。
除了监控时间或OnRamp
中消息队列中的消息数量外,Executing DON还监控CommitStore
合约,以确保消息已准备好在目标链上执行,即消息是否包含在被批准的链上承诺中。
如果条件满足,Executing DON将创建一个包含所有准备发送的消息的Report(以批次形式)。它在批处理逻辑中说明每条消息的 gasLimit。它还为每条消息计算相关的Merkle Proof,以证明该消息包含在Committing DON向CommitStore合约提交的Merkle Root中。注意,Executing DON 的批次可以是Committing DON批次的任何子集。
达成共识后,Report将被传送到目标链上的OffRamp
合约。
对于接收到的批次中的每条消息,OffRamp
合约使用提供的Merkle Proof验证该交易是否包含在CommitStore
合约中被批准的承诺中。
如果交易中包含代币,OffRamp
合约将验证通道的总速率限制并去匹配对应的代币池。
OffRamp
调用代币池的unlock
/mint
函数。这将验证代币池的速率限制,并解锁或铸造代币并将其转移给指定的接收方。
如果接收方是一个合约地址并且实现了正确的接口,OffRamp
使用Router来调用接收方的 ccipReceive()
函数。
接收方处理消息,并被告知此消息的来源(区块链 + 发送方)、转移的代币和数据载荷(含相关指令)。
根据数据载荷,接收方可能将代币转移给最终接收者(终端用户)。
那么,你是否需要了解 CCIP消息处理的具体过程呢?完全不需要。你只需通过与Router合约交互来发送跨链消息,Chainlink CCIP会将其投递出去。这就像寄包裹一样——你只需将包裹交给邮局,标明收件人并支付费用即可。
就像国际航班中涉及各种检查站和相应程序确保从一个国家到另一个国家的旅程安全一样,Chainlink CCIP确保了不同区块链网络之间在被验证状态下的安全通信。
在任何飞机起飞之前,都需要进行一系列严格的检查——类似于CCIP 的初始化阶段。这里,Router充当了我们的值机柜台,用户在这里提交他们的跨链请求,就像乘客确认他们的航班细节一样。
旅行者检查他们的行李,然后由机场工作人员处理。就像长途航班上的任何行李一样,跨链消息也要遵循安全协议。同样在CCIP中,OnRamp合约检查目标区块链地址的有效性、消息大小、gas 限制和序列号,确保“行李”(或数据载荷)已为其旅程做好准备。
护照和签证的验证非常像 Commit Store 智能合约,它存储finalized状态消息的Merkle Root,并确保这些消息在目标链上执行之前会通过风险管理网络的“批准”来保证其真实性和安全性。
在飞行过程中,乘客和行李处于运输状态,类似于跨链消息在网络之间传播。Executing DON类似于幕后工作的机组人员,确保跨链交互的顺利进行。
正如我们所知,飞行的持续时间可能会有所不同——向东飞行可能会追逐太阳,缩短我们感受到的白天,而向西飞行则会延长它。同样,跨链消息的交付时间取决于源区块链的最终确定性。我们从 CCIP大师班 #1 中了解到,最终确定性是指区块链上交易的不可逆性和永久记录状态。
到达时,OffRamp合约通过验证已提交和批准的Merkle Root来确保消息的真实性。
就像风险管理网络在幕后进行的最终检查(乘客看不到)一样,行李或我们的跨链消息也要经过各种安全检查,确保其符合目标区块链的规则及规范。
最后,目标链上的Router扮演边境管制的角色,将消息数据和/或代币传递到接收方地址,就像旅客拿到盖了章的护照进入新国家一样。
尽管整个过程很复杂,但对于终端用户来说大多是不可见的,他们仅仅体验到数字资产从一个区块链到另一个区块链的无缝转移。从值机到取回行李的喜悦,通过CCIP进行跨链消息传递的整个过程可谓是现代密码学的奇迹,它确保我们收到的东西就是之前我们交付给网络的。
在分叉模式下,您需要创建多个本地运行的区块链网络(您需要一个归档节点,该节点具有固定块中的历史网络状态且您已经从中进行本地分叉 - 请参见)并与中提供的合约地址进行交互。
Chainlink Local 速度非常快,因为它不需要启动任何用于跨链转账的链下组件。这就是为什么 CCIP Local 模拟器分叉(Foundry 的智能合约、 Hardhat 的 TypeScript 脚本)暴露了在分叉网络之间切换的功能并需要开发人员直接将消息路由到目标区块链上 - 所以不要忘记这一步。
完整的例子参见:
如果命令执行失败,请确保您已。
通过复制 .env.example
文件创建一个新文件并将其命名为 .env
。在其中加入 Ethereum Sepolia 和 Arbitrum Sepolia 的 RPC URL。这里可以使用本地归档节点或提供归档数据的服务,例如 或 。
如果没有预置的网络详细信息(例如主网络),您必须使用 函数手动添加这些信息。
请注意,只有完成所有三天作业的参与者才有资格获得结业证书
通过以下表单提交包含您的练习的公共 GitHub 仓库
使用 chainlink-local 为项目 https://github.com/smartcontractkit/ccip-cross-chain-name-service 编写测试。您可以参考 https://cll-devrel.gitbook.io/chainlink-local-documentation 获取帮助。
在测试中,您必须:
创建 一个CCIPLocalSimulator.sol 智能合约的实例
调用 configuration() 函数获取 Router 合约地址
创建 CrossChainNameServiceRegister、CrossChainNameServiceReceiver 和 CrossChainNameServiceLookup 智能合约的实例,并在需要时调用 enableChain() 函数
调用 register() 函数,并提供 “alice.ccns” 和 Alice 的 EOA 地址作为函数参数
调用 lookup() 函数,并提供 “alice.ccns” 作为函数参数。断言判断返回的地址就是 Alice 的 EOA 地址
发送指向公共 GitHub 仓库的 URL
请注意,只有完成前三天所有作业的参与者才有资格获得结业证书。
通过此表单提交您的公共GitHub仓库以完成以下练习
按照 https://docs.chain.link/ccip/tutorials/ccipreceive-gaslimit 指南,测量 ccipReceive 函数的燃气消耗。一旦得到该数值,将其增加10%,并将其作为 transferUsdc 函数的 gasLimit 参数,而不是当前硬编码的500,000。
使用此Gitbook作为指导
转移代币与数据 - 防御性示例
本教程扩展了可编程代币传输的示例。它使用 Chainlink CCIP 在不同区块链上的智能合约之间传输代币和任意数据,专注于接收合约中的防御性编码。如果在 CCIP 消息接收期间发生指定错误,合约将锁定代币。锁定代币允许所有者根据需要恢复和重定向它们。防御性编码至关重要,因为它使得恢复锁定的代币成为可能,并确保了用户资产的保护。
开始之前
您应该了解如何编写、编译、部署和资助智能合约。如果您需要复习基础知识,请阅读本教程,它将指导您使用 Solidity 编程语言,在 MetaMask 钱包中交互以及在 Remix 开发环境中工作。
您的账户必须在 Avalanche Fuji 上有一些 AVAX 和 LINK 代币,在以太坊 Sepolia 上有一些 ETH 代币。了解如何 获取测试网 LINK。
了解如何 获取 CCIP 测试代币。按照本指南操作后,您应该拥有 CCIP-BnM 代币,并且 CCIP-BnM 应该出现在 MetaMask 中您的代币列表中。
了解如何 资助您的合约。本指南展示了如何使用 LINK 资助您的合约,但您可以使用相同的指南为合约资助任何 ERC20 代币,只要它们出现在 MetaMask 的代币列表中。
按照前一个教程:使用 数据传输代币 来学习如何使用 CCIP 进行可编程代币传输。
编码时间!
在这个练习中,我们将从 Avalanche Fuji 上的智能合约启动交易,向 以太坊 Sepolia 上的另一个智能合约发送字符串文本和 CCIP-BnM 代币,使用 CCIP。然而,在到达接收合约时,处理逻辑将故意失败。本教程将展示一种优雅的错误处理方法,允许合约所有者恢复被锁定的代币。
正确估计您的 gas 限制
彻底测试所有场景以准确估计所需的 gas 限制至关重要,包括失败场景。请注意,用于执行失败场景的错误处理逻辑的 gas 可能高于成功场景。
要使用此合约:
编译您的合约。
部署并在 Avalanche Fuji 上资助您的发送者合约,并启用向以太坊 Sepolia 发送消息的功能:
打开 MetaMask 并选择网络 Avalanche Fuji。
在 Remix IDE 中,点击 Deploy & Run Transactions,从环境列表中选择 Injected Provider - MetaMask。然后 Remix 将与您的 MetaMask 钱包交互,以与 Avalanche Fuji 通信。
点击 transact
按钮。确认交易后,合约地址将出现在 已部署合约 列表中。记下您的合约地址。
打开 MetaMask 并用 CCIP-BnM 代币为您的合约注资。您可以向您的合约转账 0.002
CCIP-BnM。
启用您的合约以向 以太坊 Sepolia 发送 CCIP 消息:
在 Remix IDE 中,转到 Deploy & Run Transactions,在您部署在 Avalanche Fuji 上的智能合约的函数列表中。
调用 allowlistDestinationChain
,传入 16015286601757825753
作为目标链选择器,以及 true
设置为允许。每个链选择器都可以在 支持的网络页面 上找到。
在 以太坊 Sepolia 上部署您的接收者合约,并启用从您的发送者合约接收消息:
打开 MetaMask 并选择网络 以太坊 Sepolia。
在 Remix IDE 中,转到 Deploy & Run Transactions,确保环境仍然是 Injected Provider - MetaMask。
点击 transact
按钮。确认交易后,合约地址将出现在 已部署合约列表 中。记下您的合约地址。
启用您的合约以接收来自 Avalanche Fuji 的 CCIP 消息::
在 Remix IDE 中,转到 Deploy & Run Transactions,打开您部署在 以太坊 Sepolia 上的智能合约的函数列表。
调用 allowlistSourceChain
,传入 14767482510784806043
作为源链选择器,以及 true
设置为允许。每个链选择器都可以在 支持的网络页面 上找到。
启用您的合约以接收来自您部署在 Avalanche Fuji 上的合约的 CCIP 消息:
在 Remix IDE 中,转到 Deploy & Run Transactions,打开您部署在 以太坊 Sepolia 上的智能合约的函数列表。
调用 allowlistSender
,传入您部署在 Avalanche Fuji 上的合约地址,并设置为 true
。
调用 setSimRevert
函数,传入 true
作为参数,然后等待交易确认。将 s_simRever
t 设置为 true
会在处理接收到的消息时模拟失败。更多详情请参阅解释部分。
此时,您在 Avalanche Fuji 上有一个发送者合约,在 以太坊 Sepolia 上有一个接收者合约。作为安全措施,您已启用发送者合约向 以太坊 Sepolia 发送 CCIP 消息,以及接收者合约接收来自 Avalanche Fuji 上发送者的 CCIP 消息。接收者合约无法处理消息,因此,它不会抛出异常,而是会锁定接收到的代币,使所有者能够恢复它们。
注意:另一项安全措施强制只有路由器可以调用 _ccipReceive 函数。更多详情请参阅解释部分。
转移 0.001 CCIP-BnM 和一些文本。使用 CCIP 的 CCIP 费用将以 LINK 支付。
打开 MetaMask 并连接到 Avalanche Fuji。用 LINK 代币为您的合约注资。您可以向您的合约转账 0.5
LINK。在此示例中,LINK 用于支付 CCIP 费用。
从 Avalanche Fuji 发送带有代币的字符串数据::
打开 MetaMask 并选择网络 Avalanche Fuji.
在 Remix IDE 中,转到 Deploy & Run Transactions,打开您部署在 Avalanche Fuji 上的智能合约的函数列表。
填写 sendMessagePayLINK 函数的参数:
点击 transact
并在 MetaMask 上确认交易。
交易成功后,请记录交易哈希。以下是在Avalanche Fuji
上进行交易的一个示例。
注意
在gas价格飙升期间,您的交易可能会失败,需要超过 0.5 LINK 的gas费才能继续。如果您的交易失败,请向您的合约中注入更多的 LINK 代币,并尝试重新提交交易。
打开CCIP浏览器,通过交易哈希搜索你的跨链交易。
CCIP交易在状态被标记为“Success”时完成。在这个例子中,CCIP消息ID是0x120367995ef71f83d64a05bd7793862afda9d04049da4cb32851934490d03ae4。
检查目标链上的接收方合约:
打开 MetaMask 并选择 Ethereum Sepolia 网络。
在 Remix IDE 中,在“部署与运行交易”(Deploy & Run Transactions)部分,打开您在以太坊 Sepolia 上部署的智能合约的函数列表。
调用 getFailedMessages
函数,使用偏移量(offset)为 0
和限制(limit)为 1
,以检索第一个失败的消息。
请注意返回的值有:0x120367995ef71f83d64a05bd7793862afda9d04049da4cb32851934490d03ae4(消息ID)和 1(表示失败的错误代码)。
为了恢复被锁定的代币,请调用 retryFailedMessage
函数:
确认交易后,您可以在区块链浏览器中打开它。请注意,被锁定的资金已转移到 tokenReceiver
地址。
再次调用 getFailedMessages
函数,使用偏移量(offset)为 0
和限制(limit)为 1
,以检索第一个失败的消息。请注意,现在错误代码是 0,表示该消息已经解决。
注意:这些示例合约被设计为可以双向工作。作为练习,您可以使用它们将带有数据的代币从Avalanche Fuji转移到以太坊Sepolia,也可以从以太坊Sepolia转回Avalanche Fuji。
本教程中展示的智能合约旨在与CCIP交互,以传输和接收代币和数据。该合约代码与 传输代币及数据教程 中的代码相似,因此,您可以参阅 其代码解释。我们只将解释主要的不同之处。
sendMessagePayLINK
函数与 传输代币及数据 教程中的 sendMessagePayLINK
函数类似。主要的区别在于增加了gas限制,以适应处理错误逻辑所需的额外gas。
当目标区块链接收到消息时,CCIP路由器会调用ccipReceive
函数。此函数作为合约处理传入CCIP消息的入口点,通过onlyRouter
和onlyAllowlisted
修饰符实施关键的安全检查。
以下是该过程的逐步分解:
通过ccipReceive
进入:
ccipReceive
函数被调用,传入一个包含待处理消息的Any2EVMMessage结构体。
安全检查确保调用来自授权的路由器、已允许的源链和已允许的发送者。
处理消息:
ccipReceive
调用processMessage
函数,该函数是外部的,以利用Solidity的try/catch错误处理机制。注意:onlySelf
修饰符确保只有合约本身可以调用此函数。
在processMessage
内部,使用s_simRevert
状态变量检查是否模拟回滚条件。这个模拟由合约所有者调用的setSimRevert
函数来切换。 如
果s_simRevert
为false,processMessage
调用_ccipReceive
函数进行进一步的消息处理。
在_ccipReceive
中处理消息:
_ccipReceive
从消息中提取并存储各种信息,如messageId、解码后的发送者地址、代币数量和数据。
然后发出一个MessageReceived
事件,表示消息处理成功。
错误处理:
如果处理过程中发生错误(或触发模拟回滚),ccipReceive
内的catch块将被执行。
失败消息的messageId被添加到s_failedMessages
中,消息内容被存储在s_messageContents
中。
发出一个MessageFailed
事件,允许以后识别和重处理失败的消息。
retryFailedMessage
函数提供了一种机制,用于在 CCIP 消息处理失败时恢复资产。它专门设计用于处理那些消息数据问题导致整个处理过程受阻,但仍然允许代币恢复的场景:
初始化:
只有合约所有者可以调用此函数,提供失败消息的 messageId
和用于代币恢复的 tokenReceiver
地址。
验证:
它使用 s_failedMessages.get(messageId)
检查消息是否已失败。如果没有,它将回滚交易。
状态更新:
将消息的错误代码更新为 RESOLVED
,以防止重复条目和多次重试。
代币恢复:
使用 s_messageContents[messageId]
检索失败的消息内容。
将与失败消息关联的锁定代币转移到指定的 tokenReceiver
,作为一个逃生舱口,而无需再次处理整个消息。
事件发出:
发出一个 MessageRecovered
事件,用以标示代币恢复操作已成功完成。
这个函数展示了一个优雅的资产恢复解决方案,即使在消息处理遇到问题时也能保护用户的价值。
代码时间 🎉
参数 | Arbitrum Sepolia | Ethereum Sepolia |
---|---|---|
您可以使用任何区块链开发框架来使用Chainlink CCIP。对于本次训练营,我们准备了Hardhat、Foundry 和 Remix IDE 的步骤说明。
让我们创建一个新项目。
进入网站 https://remix.ethereum.org/ 并点击“Create new Workspace”按钮。选择“Blank”模板并将工作区命名为“CCIP Bootcamp day2”。
或者你可以克隆:
要使用Chainlink CCIP,您需要与 @chainlink/contracts-ccip NPM 包中的一些Chainlink CCIP特定合约进行交互。
安装此包,请按照您用于本x的开发环境进行操作。
我们还需要一个标准的 @chainlink/contracts NPM包用于本模块,所以在这里我们也可以通过运行以下命令来安装它:
最后,对于本次练习,我们还需要安装 @openzeppelin/contracts NPM包。为此,请运行以下命令:
运行:
然后在您的 foundry.toml
或 remappings.txt
文件中设置 remappings 为
我们还需要一个标准的 @chainlink/contracts NPM包用于本模块,所以趁现在我们通过运行以下命令来安装它:
并设置remappings为:
最后对于本次练习,我们还需要安装 @openzeppelin/contracts NPM包。为此,请运行以下命令:
并在 foundry.toml
或 remappings.txt
文件中将 remappings 设置为 @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/。
创建一个新的 Solidity 文件,并粘贴上以下内容。这是一个空合约,只导入了 @chainlink/contracts-ccip
包中的一个合约。
编译它。如果成功编译并生成了 .deps/npm/@chainlink/contracts-ccip
、.deps/npm/@chainlink/contracts
和 .deps/npm/@openzeppelin/contracts
文件夹,意味着我们已将所有必要的包导入到了 Remix IDE 工作区。
您可以使用 LINK 代币或给定区块链上的原生代币/wrapped原生代币来支付 CCIP 费用。在本次练习中,我们需要至少 1 个 LINK 或 Arbitrum Sepolia 测试网代币。要获取它,请前往https://faucets.chain.link/arbitrum-sepolia
在contracts
文件夹中创建新文件XNFT.sol
运行以下指令来编译:
在src
文件夹中创建新文件XNFT.sol
点击“Create new file”按钮来创建新的Solidity文件XNFT.sol
按照步骤添加必要的环境变量,以便部署这些合约并发送您的第一个 CCIP 消息。
该合约需要至少 0.8.20 的 Solidity 版本。因为在 0.8.20 版本的 Solc 中,默认的 EVM 版本为“上海”。在上海升级中,一个新的操作码 PUSH0 被添加到以太坊虚拟机中。
然而,除了以太坊外的大多数区块链尚未支持 PUSH0 操作码。
这意味着 PUSH0 操作码现在是合约字节码的一部分,如果您正在使用的链不支持它,它将会报出“无效操作码”错误。
为了了解更多信息,我们强烈建议您查看此 StackOverflow 的回复:
我们将使用 @chainlink/env-enc
包来提高安全性。它通过创建一个新的 .env.enc
文件来加密敏感数据,而不是将其以明文形式存储在 .env 文件中。虽然不建议将此文件上传到网络,但如果意外发生,您的机密信息仍然会被加密。 通过运行以下命令来安装该包:
设置一个密码用于加密和解密环境变量文件。您可以稍后通过输入相同的命令来更改密码。
现在设置以下环境变量:PRIVATE_KEY、源区块链的 RPC URL、目标区块链的 RPC URL。在此示例中,我们将使用 Arbitrum Sepolia 和 Ethereum Sepolia。
要设置以上变量,请输入以下命令并按照终端中的说明进行操作:
完成以上操作后,.env.enc 文件将自动生成。如果您想验证您的输入,可以随时运行以下命令:
最后,扩展 hardhat.config
文件以支持这两个网络:
创建一个新文件.env
。在其中填写您的钱包的 PRIVATE_KEY 以及至少两个区块链的 RPC URL。在此示例中,我们将使用 Arbitrum Sepolia 和 Ethereum Sepolia。
完成后,为了加载 .env 文件中的变量,请运行以下命令:
最后,扩展 foundry.toml
文件以支持这两个网络:
点击进入到“Solidity compiler”选项卡
展开“Advanced Configurations”下拉菜单
展开“EVM VERSION”下拉菜单并选择“paris”而不是“default”
点击进入“Deploy & run transactions”选项卡,并从“Environment”下拉菜单中选择“Injected Provider - Metamask”选项。
如果您使用的是 Metamask 钱包,其中已经预装了Ethereum Sepolia 网络。确保您添加了 Arbitrum Sepolia 网络。
访问 Chainlist.org 并搜索“arbitrum sepolia”。一旦看到 Chain ID 为 421614 的网络,点击“Add to Metamask”按钮。
准备 Ethereum Sepolia 的 Chain Selector 和 CCIP Router & LINK 代币合约地址。您可以在页面开头滚动查看以获取这些地址。#ccip-config-details
进入scripts
文件夹并创建一个名为 deployXNFT.ts
的新文件。
运行部署脚本:
准备 Ethereum Sepolia 的 Chain Selector 和 CCIP Router & LINK 代币合约地址。您可以在页面开头滚动查看以获取这些地址。#ccip-config-details
通过运行以下命令部署 XNFT.sol
在 scripts
文件夹下创建文件 XNFT.s.sol
请注意,此示例中的XNFT 的部署是直接硬编码 Ethereum Sepolia 的相关配置,但您可以重构以下部署脚本以支持其他网络。这里可参考 CCIP Starter Kit (Foundry version) 。
运行以下指令来部署XNFT.sol
:
准备 Ethereum Sepolia 的 Chain Selector 和 CCIP Router & LINK 代币合约地址。您可以在页面开头滚动查看以获取这些地址。#ccip-config-details
打开 Metamask 钱包,切换到 Ethereum Sepolia 网络。
打开 XNFT.sol 文件。
打开“Solidity Compiler”选项卡,然后点击“Compile XNFT.sol”按钮。
打开“Deploy & run Transaction”选项卡,然后在“Environment”下拉菜单中选择“Injected Provider - Metamask”选项。确保`chainId`切换为11155111(如果没有切换,你需要在浏览器中刷新Remix IDE页面)
在“Contract”下拉菜单中,确保被选中的是“XNFT - XNFT.sol”。
找到橘黄色的按钮“Deploy”。在 ccipRouterAddress
中填入0x0bf3de8c5d3e8a2b34d2beeb17abfcebaf363a59
,在 linkTokenAddress
中填入 0x779877A7B0D9E8603169DdbD7836e478b4624789
,在 currentChainSelector
中填入 16015286601757825753
。
点击橘黄色的“Deploy/Transact”按钮。
Metamask 通知会弹出来,对交易进行签名。
准备 Arbitrum Sepolia 的 Chain Selector 和 CCIP Router & LINK 代币合约地址。您可以在页面开头滚动查看以获取这些地址。#ccip-config-details
进入scripts
文件夹并创建一个名为 deployXNFTArbitrum.ts
的新文件。
运行部署脚本:
准备 Arbitrum Sepolia 的 Chain Selector 和 CCIP Router & LINK 代币合约地址。您可以在页面开头滚动查看以获取这些地址。#ccip-config-details
选项 1)
通过运行以下命令部署 XNFT.sol
在 scripts
文件夹下创建文件 XNFTArbitrum.s.sol
请注意,此示例中的XNFT 的部署是直接硬编码 Arbitrum Sepolia 的相关配置,但您可以重构以下部署脚本以支持其他网络。这里可参考 CCIP Starter Kit (Foundry version) 。
运行以下指令来部署XNFT.sol
:
准备 Arbitrum Sepolia 的 Chain Selector 和 CCIP Router & LINK 代币合约地址。您可以在页面开头滚动查看以获取这些地址。#ccip-config-details
打开您的 Metamask 钱包并切换到 Arbitrum Sepolia 网络。
打开 XNFT.sol 文件。
点击进入“Solidity Compiler”选项卡并点击“Compile XNFT.sol”按钮。
点击进入“Deploy & run transactions”选项卡,并从“Environment”下拉菜单中选择“Injected Provider - Metamask”选项。确保 chainId 切换到 421614(如果没有,您可能需要刷新浏览器中的 Remix IDE 页面)。
在“Contract”下拉菜单中,确保选择了“XNFT - XNFT.sol”。
找到橙色的“Deploy”按钮。提供以下信息:
ccipRouterAddress:0x2a9c5afb0d0e4bab2bcdae109ec4b0c4be15a165
linkTokenAddress:0xb1D4538B4571d411F07960EF2838Ce337FE1E80E
currentChainSelector:3478487238524512106
点击橙色的“Deploy”/“Transact”按钮。
Metamask 通知将弹出。签署交易。
需要准备:
您之前部署到 Ethereum Sepolia 的 XNFT.sol 智能合约地址;
您之前部署到 Arbitrum Sepolia 的 XNFT.sol 智能合约地址;
chainSelector
参数:3478487238524512106,这是 Arbitrum Sepolia 网络的 CCIP Chain Selector;
ccipExtraArgs
参数:0x97a657c90000000000000000000000000000000000000000000000000000000000030d40,这是 CCIP extraArgs 的bytes版本,其中gasLimit为默认值 200_000。
如果您想自己计算这个值,可以重用以下辅助智能合约:
在scripts
文件夹中创建TypeScript文件enableChain.ts
:
运行以下指令来调用enableChain方法:
需要准备:
您之前部署到 Ethereum Sepolia 的 XNFT.sol 智能合约地址;
您之前部署到 Arbitrum Sepolia 的 XNFT.sol 智能合约地址;
chainSelector
参数:3478487238524512106,这是 Arbitrum Sepolia 网络的 CCIP Chain Selector;
ccipExtraArgs
参数:0x97a657c90000000000000000000000000000000000000000000000000000000000030d40,这是 CCIP extraArgs 的bytes版本,其中gasLimit为默认值 200_000。
如果您想自己计算这个值,可以重用以下辅助智能合约。在scripts
文件夹内创建文件EncodeExtraArgs.s.sol
并在其中粘贴以下代码:
运行:
在“Deployed Contracts”部分,您应该能找到之前部署到 Ethereum Sepolia 的 XNFT.sol 合约。找到 enableChain
函数并提供以下参数:
chainSelector
参数:3478487238524512106,这是 Arbitrum Sepolia 网络的 CCIP Chain Selector;
您之前部署到 Arbitrum Sepolia 的 XNFT.sol 智能合约地址,作为 xNftAddress 参数;
ccipExtraArgs
参数:0x97a657c90000000000000000000000000000000000000000000000000000000000030d40,这是 CCIP extraArgs 的bytes版本,其中gasLimit为默认值 200_000。
点击橙色的“Transact”按钮。 如果您想自己计算这个值,可以重用以下辅助智能合约。创建 EncodeExtraArgs.sol
文件并粘贴以下代码:
需要准备:
您之前部署到 Arbitrum Sepolia 的 XNFT.sol 智能合约地址;
您之前部署到 Ethereum Sepolia 的 XNFT.sol 智能合约地址,作为 xNftAddress 参数;
chainSelector
参数:16015286601757825753,这是 Ethereum Sepolia 网络的 CCIP Chain Selector;
ccipExtraArgs
参数:0x97a657c90000000000000000000000000000000000000000000000000000000000030d40,这是 CCIP extraArgs 的bytes版本,其中gasLimit为默认值 200_000。
如果您想自己计算这个值,可以重用以下辅助智能合约:
在scripts
文件夹中创建TypeScript文件enableChainArbitrum.ts
:
运行以下指令来调用enableChain方法:
需要准备:
您之前部署到 Arbitrum Sepolia 的 XNFT.sol 智能合约地址;
您之前部署到 Ethereum Sepolia 的 XNFT.sol 智能合约地址,作为参数xNftAddress
;
chainSelector
参数:16015286601757825753
,这是 Ethereum Sepolia 网络的 CCIP Chain Selector;
ccipExtraArgs
参数:0x97a657c90000000000000000000000000000000000000000000000000000000000030d40,这是 CCIP extraArgs 的bytes版本,其中gasLimit为默认值 200_000。
如果您想自己计算这个值,可以重用以下辅助智能合约。在scripts
文件夹内创建文件EncodeExtraArgs.s.sol
并在其中粘贴以下代码:
运行:
在“Deployed Contracts”部分,您应该能找到之前部署到 Arbitrum Sepolia 的 XNFT.sol
合约。找到 enableChain
函数并提供以下参数:
chainSelector
参数:16015286601757825753,这是 Ethereum Sepolia 网络的 CCIP Chain Selector;
您之前部署到 Ethereum Sepolia 的 XNFT.sol
智能合约地址,作为 xNftAddress
参数;
ccipExtraArgs
参数:0x97a657c90000000000000000000000000000000000000000000000000000000000030d40,这是 CCIP extraArgs 的bytes版本,其中gasLimit为默认值 200_000。
点击橙色的“Transact”按钮。 如果您想自己计算这个值,可以重用以下辅助智能合约。创建 EncodeExtraArgs.sol
文件并粘贴以下代码:
为支付 CCIP 费用,向XNFT.sol
充值一定数量的 LINK。3 个 LINK 对于此演示应该绰绰有余。当然,为了实现完整的功能,您还应在其他区块链上为XNFT.sol
智能合约充值,以便在所有区块链之间执行跨链转账。
在scripts文件夹中创建
TypeScript文件mint.ts:
运行以下指令调用mint方法:
执行:
在“Deployed Contracts”部分,您应该能找到之前部署到 Arbitrum Sepolia 的 XNFT.sol
合约。找到 mint
函数,然后点击橙色的“Transact”按钮。
需要准备:
from
参数:您的 EOA 地址;
to
参数:您希望跨链转移您的 NFT 的另一个链上的 EOA 地址,可以是您的 EOA 地址;
tokenId
参数:您要跨链转移的 xNFT 的 ID;
destinationChainSelector
参数:16015286601757825753,这是 Ethereum Sepolia 区块链的 CCIP Chain Selector;
payFeesIn
参数:1,表示我们用 LINK 支付 CCIP 费用。
在 scripts
文件夹下创建一个 TypeScript 文件crossChainTransferFrom.ts
运行以下指令调用crossChainTransferFrom方法:
需要准备:
from
参数:您的 EOA 地址;
to
参数:您希望跨链转移您的 NFT 的另一个链上的 EOA 地址,可以是您的 EOA 地址;
tokenId
参数:您要跨链转移的 xNFT 的 ID;
destinationChainSelector
参数:16015286601757825753,这是 Ethereum Sepolia 区块链的 CCIP Chain Selector;
payFeesIn
参数:1,表示我们用 LINK 支付 CCIP 费用。
运行:
在“Deployed Contracts”部分,您应该能找到之前部署到 Arbitrum Sepolia 的 XNFT.sol
合约。找到 crossChainTransferFrom
函数并提供以下参数:
from
参数:您的 EOA 地址;
to
参数:您希望跨链转移您的 NFT 的另一个链上的 EOA 地址,可以是您的 EOA 地址;
tokenId
参数:您要跨链转移的 xNFT 的 ID;
destinationChainSelector
参数:16015286601757825753,这是 Ethereum Sepolia 区块链的 CCIP Chain Selector;
payFeesIn
参数:1,表示我们用 LINK 支付 CCIP 费用。
点击橙色的“Transact”按钮。
您现在可以在 CCIP Explorer 页面监控此次NFT的跨链转移。
一旦跨链 NFT 到达 Ethereum Sepolia,您可以在 Metamask 钱包中手动显示它。点击进入“NFT”选项卡并点击“Import NFT”按钮。
然后填写 Ethereum Sepolia 上的 XNFT.sol
智能合约地址和您收到的代币 ID(0)。
最后,您的 NFT 将显示在 Metamask 钱包中。
调试 CCIP dApps 的简要概述
欢迎来到关于调试 Chainlink CCIP dApps 和排查跨链消息的发送与接收问题的附加章节。编写代码只是开始,真正的技能往往在于有效地排查和解决出现的问题。
要了解问题出现的地方或可能出现的问题,您首先需要收藏整个跨链消息传输流程:Processing CCIP Messages。
第二个最重要的事情是理解 EVM 中的 ABI 编码/解码工作原理。当您编译 Solidity 代码时,两个主要输出是应用程序二进制接口(ABI)和 EVM 字节码。
EVM 字节码是以太坊虚拟机执行的机器码,该字节码会被部署到以太坊区块链上。它是一组底层的、十六进制编码的指令集,EVM 可以解释并运行这些指令。字节码以可执行的形式表示智能合约的实际逻辑。
ABI 本质上是一个 JSON 格式的文本文件,描述了您的智能合约中的函数和变量。当您想与已部署的合约交互时(例如,从 Web 应用程序调用一个函数),ABI 用于将函数调用编码成 EVM 可以理解的格式。它是高级应用程序(如 JavaScript 前端)和在以太坊上运行的低级字节码之间的接口。ABI 包括关于每个函数的名称、返回类型、可见性(公共、私有等)以及其参数类型的详细信息。
每当由于接收方的错误导致您的消息未送达时,CCIP浏览器将显示错误消息。然而,如果浏览器不知道 ABI(例如,您尚未在区块浏览器上验证智能合约的源代码),它将显示原始内容而不是人类可读的错误消息。如果我们了解 ABI 编码/解码的工作原理,这仍然没问题,因为有很多工具可以帮助我们。
所以,乍一看这个消息时,您可能会陷入恐慌,以为当下 CCIP 不工作了或出现类似情况。那么让我们解码错误消息,看看出了什么问题。
由浏览器可知错误代码是:0xbf3f9389000000000000000000000000cd936a39336a2e2c5a011137e46c8120dcae0d65 这本质上是一个携带很多信息的十六进制值。但如果我们知道接收方合约的 ABI便可以很简单地解码它。
您可以使用的一些工具:
一些在线解码器,例如这个相当不错:https://bia.is/tools/abi-decoder/
Foundry 的 cast abi-decode
在这个示例中,我们使用 https://bia.is/tools/abi-decoder/ 在线解码器。我们需要提供合约的 ABI和上述错误代码,然后点击解码以获得人类可读的错误消息。
解码后的输出明确地告诉我们:抛出了 Solidity 自定义错误 SenderNotWhitelisted()
,这很可能意味着我们忘记调用接收方智能合约的 allowlistSender()
函数。
如果您更具技术背景,可以使用官方 CCIP GitHub 仓库中的一个脚本来完成相同的任务,也还可以实现更多功能。脚本地址:https://github.com/smartcontractkit/ccip/blob/ccip-develop/core/scripts/ccip/ccip-revert-reason/main.go
在调试时,您需要提供一个错误代码字符串,或者提供 chainId、txHash 和 txRequester 以及 .env
文件中的 JSON RPC URL(推荐使用归档节点)。
如果您此时直接运行 go run main.go
,使用这些预定义值的输出应该是:If you access an array, bytesN or an array slice at an out-of-bounds or negative index (i.e. x[i] where i >= x.length or i < 0).
这意味着抛出了 EVM Panic 代码 0x32,这是一个非常有帮助的错误消息。
一个最常见的错误可能是:ReceiverError. This may be due to an out of gas error on the destination chain. Error code: 0x
这很可能意味着您使用了 extraArgs: ""
语法。默认情况下 gasLimit
为 200,000,而您的 ccipReceive
函数所消耗的 gas 要大于该默认值。
为了解决这个错误,您可能只需要连接您的钱包到 CCIP浏览器并设置“Gas limit override”字段为适当的值,并点击蓝色按钮手动执行此功能。
为了防止将来出现这个问题,您可以使用以下语法,例如将 gas 限制设置为 500,000 gas
请记住:
如果您只是转移代币,gasLimit
应设置为 0,因为没有 ccipReceive
函数。
如果您希望使您的 dApp 与未来的 CCIP 升级兼容,extraArgs
应该是可变的(参见:https://docs.chain.link/ccip/best-practices#using-extraargs)。
通常,当您想在测试网上实时调试或模拟交易时,Tenderly 是一个不错的选择。全追踪功能允许您查看函数实际revert的位置,这比区块浏览器中的错误消息要有帮助得多。
在处理未验证的智能合约时,一个最大的难题就是函数追踪。虽然这个智能合约没有被验证,但函数选择器仍然是已知的,因此 Polygonscan 能够显示它。这有时对调试非常有帮助。
函数选择器是函数签名的 Keccak-256 哈希值的前四个字节。函数签名包括函数名称和括号内的参数类型列表。当进行函数调用时,EVM 通过这前四个字节来确定合约中应该执行的具体函数。
所以调用失败的函数是在未验证的智能合约调用 CCIP Router 合约的过程中触发的,且其函数选择器是 0x96f4e9f9
。如果我们有该智能合约的 ABI,找到函数选择器将非常简单。但如果没有呢?我们可以使用 OpenChain,这个网站有一个已知函数签名的数据库,因此我们可以尝试搜索我们的函数签名。
看起来我们已经找到了revert的函数。现在我们可以查看其代码实现并找出问题所在。尽管从 Tenderly 的界面已经很清楚,问题在于 CCIP 发送方合约没有资金来支付 CCIP 费用——这是最常见的用户错误之一。
如何设计一个跨链NFT的智能合约
跨链 NFT 是一种可以存在于任何区块链上的智能合约,从而无需用户明确知道他们当前正在使用哪个区块链。
通常,NFT 从一个链移动到另一个链是件引人注目的事,但这一简单事实却带来了一个更大的问题,即 Web3 中间件基础设施的可靠性和安全性。在一个无缝的跨链世界中,将数字资产从一个链移动到另一个链应该像在同一区块链上提交交易一样正常。
当 NFT 从一个区块链转移到另一个区块链时,它就成为跨链 NFT。
从总体上看,NFT是区块链上的数字代币,具有与链上其他任何代币不同的唯一标识符。
本质上,任何NFT都是单一区块链上的智能合约实现的。智能合约可以说是这个等式中最重要的部分,因为它控制着NFT的实现:铸造的数量、铸造的时间、分发所需满足的条件等。这意味着任何跨链 NFT 的实现至少需要在两个区块链上部署两个智能合约,并且它们之间需要互联。
这就是跨链NFT看起来样子——存在于多个区块链上的等效 NFT。
考虑到这一点,跨链 NFT 可以通过以下三种方式实现:
销毁与铸造:一个NFT所有者将他的NFT放入源链上的智能合约并将其销毁,从而将该NFT从该区块链中移除。一旦完成以上过程,另一个等效的NFT会从目标区块链上的对应智能合约中被创建。这个过程是可逆的。
锁定与铸造:NFT所有者将其 NFT锁定在源链上的智能合约中,并在目标区块链上创建一个等效的 NFT。当所有者想要将其NFT移回到源链上时,他们可以销毁目标链上的NFT,从而解锁源区块链上的NFT。
锁定与解锁:同一个NFT系列在多个区块链上铸造。NFT所有者可以在源区块链上锁定其NFT,以解锁目标区块链上的等效NFT。这意味着即使在多个区块链上存在着该NFT的多个实例,同一时间也只有一个NFT是活跃可使用的。
在本次大师班中,我们将实现销毁与铸造机制。
在每种情况中都需要一个跨链消息协议来从一个区块链向另一个区块链发送数据指令。
使用Chainlink CCIP构建跨链NFT之前,首先让我们了解一下可以用Chainlink CCIP做什么。通过 Chainlink CCIP,我们可以:
转移(支持的)代币
发送任何类型的数据
同时发送代币和数据
CCIP的发送者可以是:
EOA账户
任何合约账户
CCIP的接受者可以是:
EOA账户
任何实现了CCIPReceiver.sol
的智能合约
通过Chainlink CCIP实现销毁-铸造模型,我们将在跨链转移功能中销毁源区块链(Arbitrum Sepolia)上的NFT,并使用Chainlink CCIP发送跨链消息。我们需要编码收发地址、NFT的 tokenId和tokenURI,以便在目标区块链接收到跨链消息后能够铸造完全相同的 NFT。
Arbitrum Sepolia 端:
Ethereum Sepolia端:
这种设计允许跨链双向转移,即使用完全相同的代码和销毁-铸造机制从Ethereum Sepolia返回到Arbitrum Sepolia。
对于这个跨链NFT,我们将使用托管在IPFS上的四个Chainlink Warriors作为元数据。
Chainlink Elf,可以在以下链接查看:https://ipfs.io/ipfs/QmTgqnhFBMkfT9s8PHKcdXBn1f5bG3Q5hmBaR4U6hoTvb1
Chainlink Knight,可以在以下链接查看:https://ipfs.io/ipfs/QmZGQA92ri1jfzSu61JRaNQXYg1bLuM7p8YT83DzFA2KLH
Chainlink Orc,可以在以下链接查看:https://ipfs.io/ipfs/QmW1toapYs7M29rzLXTENn3pbvwe8ioikX1PwzACzjfdHP
Chainlink Witch,可以在以下链接查看: https://ipfs.io/ipfs/QmPMwQtFpEdKrUjpQJfoTeZS1aVSeuJT6Mof7uV29AcUpF
本次练习中,我们将尝试遵循一些CCIP最佳实践。整个完整列表应参考Chainlink官方文档。
在本次练习中,确保跨链消息在跨链 NFT 智能合约之间发送是至关重要的。为此,我们需要使用 CCIP 链选择器在不同区块链上跟踪这些地址的记录。
在目标链上的合约中实现 ccipReceive
方法时,验证 msg.sender
是否为正确的Router地址。此验证确保只有Router合约可以调用接收合约上的 ccipReceive
函数,并为希望限制哪些账户可以调用ccipReceive
的开发人员使用。
设置gasLimit
gasLimit
指定了CCIP在目标区块链上的合约中执行 ccipReceive()
可以消耗的最大gas量。它是确定发送消息费用的主要因素。未使用的 gas不会退还。
extraArgs
extraArgs
是为了兼容未来的 CCIP 升级。为了获得这个特性,请确保extraArgs
在生产部署中是可变的。这允许您在链下构建它,并将其传递给函数调用或存储在可以按需更新的变量中。
如果extraArgs
为空值,系统将默认 gasLimit 为 200,000。 为了使extraArgs
可变,请在前面提及的enableChain
函数中设置它们。你可以创建一个合约作为工具来计算要传递的bytes值
,如下所示:
入门
您可以使用任何区块链开发框架与Chainlink CCIP进行交互。在本次大师班中,我们将使用Remix IDE。
首先,访问https://remix.ethereum.org/ 并点击“Create new Workspace”按钮来创建一个新项目。选择“Blank”模板,并将工作区命名为“CCIP Masterclass 4”。
或者,您可以克隆:
要使用Chainlink CCIP,您需要与@chainlink/contracts-ccip NPM 钱包中的Chainlink CCIP专用合约进行交互。
要安装它,请创建一个新的Solidity文件,并粘贴以下内容。这是一个空合约,仅导入了@chainlink/contracts-ccip和@openzeppelin/contracts包中的合约,我们将在整个大师班中使用这些合约。
该合约要求至少使用0.8.20版本的Solidity编译器。需要特别注意的是,在最新的Remix IDE版本中,默认的EVM版本设置为“Cancun”。在上海升级(发生在当前的Cancun升级之前)中,向以太坊虚拟机中添加了一个新的操作码PUSH0。
然而,除了以太坊,大多数区块链尚未包含PUSH0操作码。这意味着PUSH0操作码现在可以成为合约字节码的一部分,如果您正在使用的链不支持它,它将会出现“无效操作码”错误。我们希望将以太坊虚拟机版本降级到“Paris”。
要了解更多信息,我们强烈建议您查看这个StackOverflow回答:
要将EVM版本设置为“Paris”,请导航到“Solidity compiler”标签,然后按照以下步骤操作:
将“COMPILER”版本设置为0.8.20+commit.a1b79de6
切换“Advanced Configurations”下拉菜单
切换“EVM VERSION”下拉菜单,并选择“paris”而不是默认值
现在通过点击“Compile Empty.sol”按钮来编译智能合约。如果编译成功,返回“File explorer”标签,如果生成了新的.deps/npm/@chainlink/contracts-ccip和.deps/npm/@openzeppelin/contracts文件夹,这意味着我们已经成功地将所有必要的包导入到了Remix IDE工作区。
在本次大师班中,我们将把USDC从Avalanche Fuji测试网转移到以太坊Sepolia测试网。要在Avalanche Fuji测试网上获取一些测试网USDC,请导航到以下网站:https://faucet.circle.com/
要支付CCIP费用,您可以使用LINK代币或给定区块链上的原生/包装原生资产。在本次大师班中,我们需要至少3个LINK或Avalanche Fuji测试网的资产。要获取它,请导航到以下网站: https://faucets.chain.link/fuji
开发TransferUSDC智能合约
点击“Create new file”按钮创建一个新的Solidity文件,将其命名为TransferUSDC.sol,并粘贴以下Solidity代码。
导航到“Deploy & run transactions”标签,从“Environment”下拉菜单中选择“Injected Provider - Metamask”选项。
如果您使用的是Metamask钱包,请确保已添加Avalanche Fuji C-Chain和Ethereum Sepolia(默认情况下应已添加)网络。
访问 Chainlist.org 并搜索“avalanche fuji”。当您看到链ID为43113的网络时,点击“Add to Metamask”按钮。
以太坊Sepolia网络应默认已添加到您的Metamask钱包中。然而,如果您需要手动添加,您可以重复我们为Avalanche Fuji C-Chain所做的步骤。导航到 Chainlist.org 并搜索“sepolia”。当您看到链ID为11155111的网络时,点击“Add to Metamask”按钮。
打开您的Metamask钱包并切换到Avalanche Fuji网络。 打开TransferUSDC.sol文件。 导航到“Solidity Compiler”标签,并点击“Compile TransferUSDC.sol”按钮。 导航到“Deploy & run transactions”标签,从“Environment”下拉菜单中选择“Injected Provider - Metamask”选项。确保链ID已切换到43113(如果没有,您可能需要刷新浏览器中的Remix IDE页面)。 在“Contract”下拉菜单下,确保选择了“TransferUSDC - TransferUSDC.sol”
找到橙色的“Deploy”按钮。提供以下信息:
0xF694E193200268f9a4868e4Aa017A0118C9a8177 作为ccipRoute
0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846
作为linkToken
0x5425890298aed601595a70AB815c96711a31Bc65
作为usdcToken。
点击橙色的“Deploy”/“Transact”按钮。
Metamask通知将弹出。签署交易。
在“Deployed Contracts”部分,您应该能找到之前部署到Avalanche Fuji的TransferUSDC.sol合约。找到allowlistDestinationChain函数并提供以下信息:
16015286601757825753,这是以太坊Sepolia测试网络的CCIP链选择器,作为_destinationChainSelector参数。
true 作为_allowed参数
点击橙色的“Transact”按钮。
为了支付CCIP费用,请向TransferUSDC.sol合约充值一定数量的LINK,3个LINK对于本次演示来说应该足够。
访问 Avalanche Fuji Snowtrace Explorer并搜索USDC代币。找到“Contract”标签,然后点击“Write as Proxy”标签。将您的钱包连接到区块链浏览器。最后,找到“approve”函数。
我们希望批准TransferUSDC.sol代表我们支出1个USDC。为此,我们必须提供以下信息:
之前部署的TransferUSDC.sol智能合约地址,作为spender参数
1000000,作为value参数。
因为USDC代币有6位小数,1000000意味着我们将批准支出1个USDC。
点击“Write”按钮。Metamask弹窗将出现。签署交易。
在“Deployed Contracts”部分,您应该能找到之前部署到Avalanche Fuji的TransferUSDC.sol合约。找到transferUsdc函数并提供以下信息:
16015286601757825753,这是以太坊Sepolia测试网络的CCIP链选择器,作为_destinationChainSelector参数。
您的钱包地址,作为_receiver参数,
1000000,作为_amount参数
0,作为_gasLimit参数
将_gasLimit参数设置为0,因为我们将代币发送到一个EOA(外部拥有账户),因此在目标端执行ccipReceive函数没有费用。
点击橙色的“Transact”按钮。
现在,您可以将交易哈希复制到 Chainlink CCIP Explorer的搜索栏中,以监控跨链消息的实时状态。
了解更多 Web3 生态,参加10月28日至31日在香港举行的SmartCon——Chainlink的年度大会。
👉 在这里报名
RWA代币化是区块链行业当前最受欢迎的细分领域,训练营可以帮助你提到这个细分领域的技能。
👉 现在报名
注册Chainlink 开发者 newsletter,以获取我们未来技术研讨会和训练营的最新信息:https://pages.chain.link/subscribe。
Chainlink Dev Hub 中有更多的案例,代码和文档: https://dev.chain.link/
如果你想要在你的项目中使用 Chainlink CCIP,可以与Chainlink的专业联系以获取帮助: https://chain.link/contact
CCIP 文档地址: https://docs.chain.link/ccip
在 Chainlink 平台中了解更多Chainlink的动态,服务和产品:https://chain.link/
Chainlink 是一个区块链中立的去中心化计算平台,它提供了安全访问外部数据、链下计算和跨链互操作性。
Chainlink跨链互操作协议(CCIP)作为一种互操作性标准,用于在任意支持的公有或私有区块链网络之间传输代币和/或数据
第五级跨链安全性通过利用多个去中心化网络来保护单个跨链交易,实现了前所未有的去中心化水平。此外,还结合了额外的风险管理系统来识别风险并采取相应的预防措施,例如实施紧急停机或设置速率限制。这些措施共同确保了跨链操作的高度安全性和可靠性。
第五级跨链安全不仅为您的跨链数据或消息提供一个独立的网络,还提供了多个由独立节点组成的网络,这些节点共同协作以确保每个桥接的安全性。
许多桥接解决方案使用单个节点或由一个密钥持有者控制的多个节点(例如Multichain),而第五级安全性采用多个独立节点,这些节点具有各自独立的密钥持有者,甚至将其分成两个独立的节点组:事务性DON节点和风险管理网络节点。CCIP的独立网络的一个关键特征是创建了两个完全独立的实现,具有两个独立的代码库,从而使CCIP在跨链互操作性方面实现了前所未有的客户端多样性和去中心化。
以安全性和可靠性为主要关注点开发的CCIP在跨链安全性方面达到了最高水平。CCIP的深度防御安全性和适用性可以分为以下四个类别:
CCIP依托于Chainlink经过验证的去中心化预言机基础设施。CCIP并不是作为一个单一的整体网络运行,而是由每条链路上的多个去中心化预言机网络(DONs)组成,每个网络包含一个独特的源链和目标链。这种方法使得CCIP能够横向扩展,因为每增加一个支持的区块链网络,CCIP就会添加额外的DONs,而不是将所有的跨链流量集中通过一个网络。
提交DON是一个去中心化的预言机节点网络,它监控给定源链上的事件,等待源链的最终确定,捆绑交易以创建Merkle根,并就该Merkle根达成共识,最终将该Merkle根提交到目标链。执行DON是一个去中心化的预言机节点网络,它在目标链上提交Merkle证明,然后通过确保这些交易包含在之前已由风险管理网络验证并提交的Merkle根中,在链上进行验证。
风险管理网络是一个独立的网络,它持续监控和验证CCIP的行为,通过独立验证跨链操作的异常活动,提供额外的安全层。风险管理网络使用了一个独立的、最小化实现的Chainlink节点软件,形成了一种客户端多样性,以提高系统的稳健性,同时也将外部依赖降至最低,以防止供应链攻击。
更具体地说,风险管理网络使用与主要CCIP系统(Golang)不同的编程语言(Rust)编写,由不同的内部团队开发,并且与CCIP DONs相比,使用了一组不同且不重叠的节点运营商。风险管理网络是跨链互操作性中一个完全独特的概念,它建立在已建立的工程原则(N版本编程)之上,这些原则在航空、核能和机器自动化等关键任务系统中广泛应用。
为了提高CCIP的安全性和稳健性,风险管理网络进行两种类型的活动:
次级批准: 风险管理网络基于源链上的交易独立地重新创建Merkle根,然后将其发布到目标链,并与提交DON发布的Merkle根进行比较。只有当两个网络的Merkle根匹配时,跨链交易才能执行。
异常检测:风险管理网络监控CCIP网络的异常行为(例如,提交的交易在源链上没有对应的交易)以及链的行为(例如,深度区块重组)。如果检测到可疑活动,风险管理网络可以触发紧急停止,暂停所有CCIP链路,以限制任何损失。
Chainlink DONs由一组地理分布广泛的抗Sybil攻击并经过安全审查的节点运营商运行,这些运营商在Web2和Web3领域运行关键任务基础设施方面拥有丰富经验。Chainlink生态系统中的节点运营商包括全球企业(例如,德国电信MMS、瑞士电信、沃达丰)、领先的Web3 DevOps团队(例如Infura、Coinbase Cloud)以及经验丰富的Chainlink生态系统项目。
在CCIP中,提交DONs和执行DONs由16个高质量的独立节点运营商组成,而风险管理网络由7个不同的节点运营商组成(总计23个节点运营商)。重要的是,风险管理网络由与主要CCIP网络完全独立且不重叠的一组节点组成,确保独立的次级验证。随着CCIP保护的价值随着时间的推移而增加,每个网络中的节点运营商数量可以扩展,以满足更高安全性的需求。
作为跨链代币转移的额外安全层,CCIP实施了可配置的速率限制,这些限制基于每个代币和每条链路设定,并与代币合约所有者(如Lido)协调设置。此外,CCIP代币转移还受益于每条链路上的总体速率限制(跨代币池),即使在最坏的情况下,在达到链路上的总体速率限制之前,每个代币的限制也不可能被全部用尽。
使用CCIP,您将获得:
由独立密钥持有者运行的多个独立节点。
三个去中心化网络共同执行和验证每笔桥接交易。
职责分离,拥有不同的节点运营商集合,并且事务性DONs和风险管理网络之间没有共享节点。
增强去中心化,两个不同实现的代码库,分别用两种不同的编程语言编写,创造了跨链桥接领域前所未见的软件客户端多样性。
前所未有的风险管理水平,能够快速适应跨链桥接中出现的任何新风险或攻击。
参数 | 值和描述 |
---|---|
参数 | 描述 |
---|---|
Chain Selector
Copy
Copy
CCIP 路由合约地址
0x2a9c5afb0d0e4bab2bcdae109ec4b0c4be15a165
0x0bf3de8c5d3e8a2b34d2beeb17abfcebaf363a59
LINK Token i之
0xb1D4538B4571d411F07960EF2838Ce337FE1E80E
0x779877A7B0D9E8603169DdbD7836e478b4624789
messageId
失败消息的唯一标识符。
tokenReceiver
代币将被发送到的地址。
_destinationChainSelector
16015286601757825753
这是目标区块链的CCIP链标识符(在这个例子中是 以太坊 Sepolia)。您可以在 支持的网络页面 上找到每个链选择器。
_receiver
您在 以太坊 Sepolia 上的接收者合约地址。 目标合约地址。
_text
_token
0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4
这是源链(本例中为 Avalanche Fuji)上的 CCIP-BnM 合约地址。您可以在 支持的网络页面 上找到每个支持的区块链的所有地址。
_amount
Hello World!
任何 string
1000000000000000
代币数量(对应0.001 CCIP-BnM)。