Openzeppelin 新增了 CrossChain (跨链)功能,看看如何使用它。
- 原文:https://soliditydeveloper.com/openzeppelin-crosschain
- 译文出自:登链翻译计划
- 译者:翻译小组
- 校对:Tiny 熊
- 本文永久链接:learnblockchain.cn/article…
Openzeppelin合约首次增加了跨链支持,特别是目前支持以下链:
在跨链通信中到底有哪些需要考虑的事情呢?
msg.sender
实际上是一个子链上的地址。当然 msg.sender
到底是什么取决于子链,但它不会是根链的实际交易者地址。签名的重复使用:如果你在合约中允许任何签名,它们可能会被重复使用。这就是为什么你应该经常仔细检查chainID,或者最好使用EIP-712,它可以处理所有复杂的安全签名。
Openzeppelin已经增加了合约,以支持在Polygon、Optimism、Arbitrum和AMB中的使用。主要接口是CrossChainEnabled.sol,所有支持的四个实现都基于它。另外还有一个AccessControlCrossChain.sol,它支持跨链下使用角色进行安全访问控制。相关的概述可以参见这里。
每个链的实现都包含一个不同的机制来检索原始的跨链交易发起者,大致上看起来像这样:
function processMessageFromRoot(
uint256, /* stateId */
address rootMessageSender,
bytes calldata data
)
AMB_Bridge(bridge).messageSender()
LibArbitrumL2.crossChainSender(LibArbitrumL2.ARBSYS)
Optimism_Bridge(messenger).xDomainMessageSender()
让我们以Polygon为例,深入了解一下如何实际使用Openzeppelin 跨链功能。
我们将在根链(Root chain)和子链(child chain)上创建具有安全访问控制的合约。
首先让我们创建一个合约,并在根链上部署。在正常情况下,通常是以太坊主网。我们可以继承FxBaseRootTunnel.sol合约,并根据网络的不同传递检查点和根地址。
import {FxBaseRootTunnel} from
"fx-portal/contracts/tunnel/FxBaseRootTunnel.sol";
// see left for full addresses
address constant GOERLI_CP_MANAGER = 0x2890bA17EfE978480615e...;
address constant GOERLI_FX_ROOT = 0x3d1d3E34f7fB6D26245E6640...;
contract PolygonRoot is FxBaseRootTunnel {
bytes public latestData;
constructor()
FxBaseRootTunnel(GOERLI_CP_MANAGER, GOERLI_FX_ROOT) {
}
function _processMessageFromChild(
bytes memory data
) internal override {
latestData = data;
}
function sendMessageToChild(bytes memory message) public {
_sendMessageToChild(message);
}
}
它有两个内部函数:
_processMessageFromChild
: 重写(Override)函数,以便处理从子合约发来的消息。_sendMessageToChild
:调用函数来向子合约发送消息。然后我们在子链上创建部署子合约,在我们的案例中,子链是Polygon网络。我们可以继承Openzeppelin CrossChainEnabledPolygonChild.sol合约,并根据网络的情况传递FX Portal子合约。
也可以使用Openzeppelin的AccessControlCrossChain.sol。只要在合约中继承它,我们就会得到通常的访问控制功能以及一个新的_crossChainRoleAlias
函数。
import {CrossChainEnabledPolygonChild} from
"oz/contracts/crosschain/polygon/CrossChainEnabledPolygonChild.sol";
import {AccessControlCrossChain} from
"oz/contracts/access/AccessControlCrossChain.sol";
address constant MUMBAI_FX_CHILD = 0xCf73231F...; // see right
contract PolygonChild is
CrossChainEnabledPolygonChild,
AccessControlCrossChain
{
event MessageSent(bytes message);
uint256 public myNumber = 12;
constructor(
address rootParent
) CrossChainEnabledPolygonChild(MUMBAI_FX_CHILD) {
_grantRole(
_crossChainRoleAlias(DEFAULT_ADMIN_ROLE),
rootParent
);
}
function setNumberForParentChain(
uint256 newNumber
) external onlyRole(DEFAULT_ADMIN_ROLE) {
myNumber = newNumber;
}
function _sendMessageToRoot(
bytes memory message
) internal {
emit MessageSent(message);
}
}
在示例中,在部署时,我们将把先前部署的根合约地址传到这里,并立即授予它管理角色,但由于这实际上是一个跨链通信,它的工作方式有点不同:
_crossChainRoleAlias
来授予角色权限。然后我们添加一个测试函数setNumberForParentChain
,只允许CrossChain的根合约调用。
为了完整起见,如果你想把信息发回给根,可以在Polygon中通过发出MessageSent事件来实现。
这个部署是可选的,但是处于我们的测试需要,你可以添加一个额外的函数,就像这样:
function getEncodedSetNumberData(uint256 newNumber) external pure returns (bytes memory) {
return abi.encodeWithSelector(PolygonChild.setNumberForParentChain.selector, newNumber);
}
基本上这个函数就是返回要调用函数的编码数据,如果你想调用setNumberForParentChain函数,你需要获得它的编码数据,你需要通过sendMessageToChild
在根合约中发送这个数据。当然,在大多数环境中,你只是在使用Web3.js或任何你所使用的前端框架来获得编码数据。
好了,现在让我们把它部署到测试网,我们这里使用:
对比部署到正式主网,流程是相同的,只是使用的地址不同。
所以我们可以直接把这里的全部代码复制到Remix中:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {CrossChainEnabledPolygonChild} from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/crosschain/polygon/CrossChainEnabledPolygonChild.sol";
import {AccessControlCrossChain} from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControlCrossChain.sol";
import {FxBaseRootTunnel} from "https://github.com/fx-portal/contracts/blob/main/contracts/tunnel/FxBaseRootTunnel.sol";
address constant MUMBAI_FX_CHILD = 0xCf73231F28B7331BBe3124B907840A94851f9f11;
address constant GOERLI_CHECKPOINT_MANAGER = 0x2890bA17EfE978480615e330ecB65333b880928e;
address constant GOERLI_FX_ROOT = 0x3d1d3E34f7fB6D26245E6640E1c50710eFFf15bA;
address constant MAINNET_FX_CHILD = 0x8397259c983751DAf40400790063935a11afa28a;
address constant MAINNET_CHECKPOINT_MANAGER = 0x86E4Dc95c7FBdBf52e33D563BbDB00823894C287;
address constant MAINNET_FX_ROOT = 0xfe5e5D361b2ad62c541bAb87C45a0B9B018389a2;
contract PolygonChild is CrossChainEnabledPolygonChild, AccessControlCrossChain {
uint256 public myNumber;
constructor(address rootParent) CrossChainEnabledPolygonChild(MUMBAI_FX_CHILD) {
_grantRole(_crossChainRoleAlias(DEFAULT_ADMIN_ROLE), rootParent);
myNumber = 12;
}
function setNumberForParentChain(uint256 newNumber) external onlyRole(DEFAULT_ADMIN_ROLE) {
myNumber = newNumber;
}
function getEncodedSetNumberData(uint256 newNumber) external pure returns (bytes memory) {
return abi.encodeWithSelector(PolygonChild.setNumberForParentChain.selector, newNumber);
}
}
contract PolygonRoot is FxBaseRootTunnel {
bytes public latestData;
constructor() FxBaseRootTunnel(GOERLI_CHECKPOINT_MANAGER, GOERLI_FX_ROOT) {}
function _processMessageFromChild(bytes memory data) internal override {
latestData = data;
}
function sendMessageToChild(bytes memory message) public {
_sendMessageToChild(message);
}
}
现在你可以:
setFxChildTunnel
,传递子合约地址。setNumberForParentChain
的数字设置为42,编码后的数据是:0x21148d91000000000000000000000000000000000000000000000000000000000000002a. 最简单的方法是通过getEncodedSetNumberData
帮方法来获得。这些交易看起来可能让你感到困惑,但它并不奇怪。零地址是Polygon的一个特殊系统地址,用于提交CrossChain调用。
在我的测试中,直到Polygon上的CrossChain转账完成,需要2到25分钟。
跨链调用到这里就完成了,如果一切都正确,你可以切换回Mumbai测试网,并读取新设置的数字,它应该已经变成了42。
本翻译由 Duet Protocol 赞助支持。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!