在本教程中,我们将使用LayerZero 建立一个简单的跨链消息转账合约,并使用默认的UA配置,发送一个跨链消息。
我们选择 Create an advanced sample project
为教程创建一个hardhat项目。
要发送跨链消息,合约在源链调用端点(endpoint)的send()方法,然后在目标链调用lzReceive()方法接收消息。为了使用端点合约,我们需要从 LayerZero 库 中导入接口。
备注:端点(endpoint)是在各链部署的合约,参考文档
2. 创建Solidity合约
创建合约文件LayerZeroDemo1.sol
:
//SPDX-License-Identifier: MIT pragma solidity ^0.8.9; pragma abicoder v2; import "../interfaces/ILayerZeroEndpoint.sol"; import "../interfaces/ILayerZeroReceiver.sol"; import "hardhat/console.sol"; contract LayerZeroDemo1 is ILayerZeroReceiver { event ReceiveMsg( uint16 _srcChainId, address _from, uint16 _count, bytes _payload ); ILayerZeroEndpoint public endpoint; uint16 public messageCount; bytes public message; constructor(address _endpoint) { endpoint = ILayerZeroEndpoint(_endpoint); } function sendMsg( uint16 _dstChainId, bytes calldata _destination, bytes calldata payload ) public payable { endpoint.send{value: msg.value}( _dstChainId, _destination, payload, payable(msg.sender), address(this), bytes("") ); } function lzReceive( uint16 _srcChainId, bytes memory _from, uint64, bytes memory _payload ) external override { require(msg.sender == address(endpoint)); address from; assembly { from := mload(add(_from, 20)) } if ( keccak256(abi.encodePacked((_payload))) == keccak256(abi.encodePacked((bytes10("ff")))) ) { endpoint.receivePayload( 1, bytes(""), address(0x0), 1, 1, bytes("") ); } message = _payload; messageCount += 1; emit ReceiveMsg(_srcChainId, from, messageCount, message); } // Endpoint.sol estimateFees() returns the fees for the message function estimateFees( uint16 _dstChainId, address _userApplication, bytes calldata _payload, bool _payInZRO, bytes calldata _adapterParams ) external view returns (uint256 nativeFee, uint256 zroFee) { return endpoint.estimateFees( _dstChainId, _userApplication, _payload, _payInZRO, _adapterParams ); } }
LayerZeroDemo1合约从源链向目标链发送一条消息,在合约构造时使用了端点地址,并且使用了两个接口:ILayerZeroEndpoint
和ILayerZeroReceiver
。
自定义函数sendMsg()
封装了endpoint.send(…)
,其将在目标链上触发对lzReceive()
的调用。
在源链调用endpoint.send(…)
后,接收链上会自动调用重载的lzReceive
函数。
自定义函数estimateFees()
封装了endpoint.estimateFees(…)
,该函数将返回跨链消息的所需的费用。
3. 在不同的链上部署合约
Fantom 测试网络创建部署脚本:
const hre = require("hardhat"); async function main() { const LayerZeroDemo1 = await hre.ethers.getContractFactory("LayerZeroDemo1"); const layerZeroDemo1 = await LayerZeroDemo1.deploy( "0x7dcAD72640F835B0FA36EFD3D6d3ec902C7E5acf" ); await layerZeroDemo1.deployed(); console.log("layerZeroDemo1 deployed to:", layerZeroDemo1.address); } main().catch((error) => { console.error(error); process.exitCode = 1; });
在 Fantom 测试网上部署合约:
npx hardhat run scripts/deploy_testnet.js --network testnet
Mumbai(Polygon测试网络)创建部署脚本:
const hre = require("hardhat"); async function main() { const LayerZeroDemo1 = await hre.ethers.getContractFactory("LayerZeroDemo1"); const layerZeroDemo1 = await LayerZeroDemo1.deploy( "0xf69186dfBa60DdB133E91E9A4B5673624293d8F8" ); await layerZeroDemo1.deployed(); console.log("layerZeroDemo1 deployed to:", layerZeroDemo1.address); } main().catch((error) => { console.error(error); process.exitCode = 1; });
在Mumbai测试网络部署合约:
npx hardhat run scripts/deploy_mumbai.js --network mumbai
成功部署两个合约后,我们得到了合约地址,例如:
Mubai测试网络: 0x37587469690CC37EE19Ff6163ce7275BB1b17d3b
Fantom 测试网络: 0xD67D01D6893cC4a2E17557765987d41E778fadca
4. 测试跨链消息传递
为 Mumbai 测试网络创建一个 javascript 测试脚本:
const hre = require("hardhat"); const { ethers } = require("ethers"); async function main() { const LayerZeroDemo1 = await hre.ethers.getContractFactory("LayerZeroDemo1"); const layerZeroDemo1 = await LayerZeroDemo1.attach( "0x37587469690CC37EE19Ff6163ce7275BB1b17d3b" ); const count = await layerZeroDemo1.messageCount(); const msg = await layerZeroDemo1.message(); console.log(count); console.log(ethers.utils.toUtf8String(msg)); } main().catch((error) => { console.error(error); process.exitCode = 1; });
该脚本将合约实例关联到前面部署的合约地址:0x37587469690CC37EE19Ff6163ce7275BB1b17d3b
。脚本将读取合约中的消息计数器和最后一条消息,现在返回的是0和空字符串。
使用hardhat运行脚本:
npx hardhat run scripts/demo1_mumbai.js --network mumbai
接着在 Fantom 测试网创建一个 javascript 测试脚本:
const { formatBytes32String } = require("ethers/lib/utils"); const { ethers } = require("ethers"); const hre = require("hardhat"); async function main() { const LayerZeroDemo1 = await hre.ethers.getContractFactory("LayerZeroDemo1"); const layerZeroDemo1 = await LayerZeroDemo1.attach( "0xD67D01D6893cC4a2E17557765987d41E778fadca" ); const fees = await layerZeroDemo1.estimateFees( 10009, "0x37587469690CC37EE19Ff6163ce7275BB1b17d3b", formatBytes32String("Hello LayerZero"), false, [] ); console.log(ethers.utils.formatEther(fees[0].toString())); await layerZeroDemo1.sendMsg( 10009, "0x37587469690CC37EE19Ff6163ce7275BB1b17d3b", formatBytes32String("Hello LayerZero"), { value: ethers.utils.parseEther("1") } ); } main().catch((error) => { console.error(error); process.exitCode = 1; });
Fantom 测试网测试脚本将合约实例关联上地址 0xD67D01D6893cC4a2E17557765987d41E778fadca
。该脚本会从 Fantom 测试网向Mumbai测试网上的合约(地址:0x37587469690CC37EE19Ff6163ce7275BB1b17d3b
) 发送一条消息“Hello LayerZero” ,并估算了消息费用(演示目的)。最后发送带有费用的消息, 为简单起见,这里为 1FTM。如果源交易比提供的金额少,它将把额外的金额退还到我们传递的地址 _refundAddress
。
使用 Hardhat 运行脚本:
npx hardhat run scripts/demo1_testnet.js --network testnet
脚本完成后,我们可以在 FTMScan 测试网中查找合约0xd67d01d6893cc4a2e17557765987d41e778fadca
上的交易:
再次运行 Mumbai 测试脚本,控制台将打印:
小结
教程完成了,Mumbai测试网络的合约收到Fantom测试链发来的消息,增加计数器。LayerZero 使整个过程变得非常简单。
教程源码:https://github.com/The-dLab/LayerZero-Demo
LayerZero 测试网地址: https://layerzero.gitbook.io/docs/technical-reference/testnet/testnet-addresseshttps://medium.com/@Tim4l1f3/layerzero-tutorial-for-beginners-d3fe9326e8b7
本翻译由 Duet Protocol 赞助支持。