练习 3: 使用Chainlink Local做跨链合约的测试
您可能已经注意到,由于存在一些额外的摩擦点——例如设置钱包、获取测试网代币、等待跨链交易完成等,直接在测试网络上使用 CCIP 构建并不理想。这是因为测试网络用于测试,而本地环境(如 Foundry、Hardhat 或 Remix IDE)用于构建和单元测试。
介绍 Chainlink Local
为了解决这个问题,我们创建了 Chainlink Local - the Chainlink CCIP Local Simulator。在本章中,您将学习如何在本地模拟跨链交易,并以比在测试网络上工作快 2000 倍的速度使用 Chainlink CCIP 构建项目!
Chainlink Local 是一个可安装的依赖项,例如 OpenZeppelin。它提供了一个工具(Chainlink Local 模拟器),开发人员可以将其导入到他们的 Foundry、Hardhat 或 Remix IDE 项目中。这个工具在本地运行 Chainlink CCIP,这意味着开发人员可以在本地环境中快速探索、设计原型和迭代 CCIP dApps,只有在准备好在实际环境中测试时再将项目移动至测试网。
最重要的是,用 Chainlink Local 测试的智能合约可以在不做任何修改的情况下部署到测试网络(假设通过构造函数传入了网络特定的合约地址,如 Router 合约和 LINK 代币地址)。
要查看更详细的文档和更多示例,请访问 Chainlink Local Documentation 和 Chainlink Local YouTube Playlist。
本地模式 vs 分叉模式
模拟器支持两种模式:
本地模式:使用在本地运行的开发区块链节点上的模拟合约,运行在
localhost上。分叉模式:使用已部署的 Chainlink CCIP 合约,通过多个分叉网络进行工作。
在这个示例中,我们将使用分叉模式。
在作业中,您必须使用本地模式。
本地模式
在本地模拟模式下工作时,模拟器会将一组智能合约预部署到一个空白的 Hardhat/Anvil 网络的EVM 状态,并通过调用 configuration() 函数公开它们的详细信息。尽管应该存在两个 Router 合约(sourceRouter 和 destinationRouter),开发人员通过这两个不同的 Router 路由跨链消息,但在本地模式中它们都是在本地区块链节点上运行的同一个合约。
分叉模式
在分叉模式下,您需要创建多个本地运行的区块链网络(您需要一个归档节点,该节点具有固定块中的历史网络状态且您已经从中进行本地分叉 - 请参见此处)并与官方 Chainlink 文档中提供的合约地址进行交互。
Chainlink Local 速度非常快,因为它不需要启动任何用于跨链转账的链下组件。这就是为什么 CCIP Local 模拟器分叉(Foundry 的智能合约、 Hardhat 的 TypeScript 脚本)暴露了在分叉网络之间切换的功能并需要开发人员直接将消息路由到目标区块链上 - 所以不要忘记这一步😉。
代码时间: 为我们的XNFT.sol合约编写单元测试
完整的例子参见:https://github.com/smartcontractkit/ccip-cross-chain-nft
创建一个新的Foundry工程
运行以下命令创建一个新的Foundry工程:
如果命令执行失败,请确保您已安装了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 来编译该合约。如果您正确安装了依赖项会编译成功。
配置Ethereum Sepolia和Arbitrum Sepolia的RPC URLs
通过复制 .env.example 文件创建一个新文件并将其命名为 .env。在其中加入 Ethereum Sepolia 和 Arbitrum Sepolia 的 RPC URL。这里可以使用本地归档节点或提供归档数据的服务,例如 Infura 或 Alchemy。
然后在您的 foundry.toml 文件中添加 rpc_endpoints 部分。其最终版如下所示:
使用Chainlink Local来编写您第一个测试文件
在上一个练习中,我们在两个不同的测试网络上执行了 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 结构,如下所示:
在该结构体中已预先设置了一些测试网络的详细信息,并且其中特意不包含主网络。因此最好的做法是始终验证这些信息——否则模拟将无法工作:
如果没有预置的网络详细信息(例如主网络),您必须使用 setNetworkDetails 函数手动添加这些信息。
我们将在 setup() 函数中直接执行前一个练习的步骤 1)和 2),如下所示:
在一个新的test函数中直接执行前一个练习的其余步骤,如下所示:
最终的完整代码:
Last updated