本文记录一下UUPS代理合约无法在XLayer链上开源验证的问题。
昨天遇到了一个UUPS 代理合约无法在 X Layer 链上开源验证的问题,搜索了大量文章,看了油管视频,还是没有解决,最后我直接扒开@openzeppelin
源码进行实验才解决了问题。本文记录一下这个问题的解决。
关于 UUPS 代理的原理和详细内容 本文不讲,但一些文章对我的帮助很大,在此列出来:
全面理解智能合约升级 Solidity 可升级代理模式: 透明代理与 UUPS 代理 智能合约设计模式:代理
使用@openzeppelin/contracts-upgradeable
编写一个实现合约(注意,不需要自己实现代理合约,openzeppelin 已经实现好了)
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract Counter is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 public count;
// initialize: required
function initialize() public initializer {
__Ownable_init();
__UUPSUpgradeable_init();
}
// _authorizeUpgrade: required
function _authorizeUpgrade(address) internal override onlyOwner {}
function store(uint256 _count) external {
count = _count;
}
}
只是为了测试 uups 代理的编写、部署和验证,因此,这个合约特别简单,一个公共状态变量count
,和一个store
方法。
如下图所示,我们选择 X Layer 网络(ChainId: 195),并在Deploy按钮
下方勾选Deploy with Proxy
。
点击Deploy按钮
,会弹出一个对话框,提示你将要发送两笔交易,一笔是我们自己写的 Counter 实现合约,另一笔是代理合约(PS: 困扰我的问题就是这个代理合约在开源验证的时候,由于代理合约是Remix自动帮我们部署的,没有源码,始终无法验证,这个问题下文我会给出解决方法
)
点击Proceed
开始调出钱包签名发送交易,不出意外的话,当第一笔交易成功后,会再弹出一个对话框(如下图),提示我们确认部署一个ERC1967Proxy
合约,这个合约就是 Remix 自动帮我们部署的代理合约,并且和上一笔交易部署的Counter
实现合约进行关联。
点击OK
继续调出钱包签名发送交易,成功后,我们在 Remix 里就会看见这两个合约。
需要提醒的是,我们与合约的所有交互都应该调用代理合约,即ERC1967Proxy合约
,所有的状态信息都存储在这个合约里,可以查看这个合约的 Owner 已经初始化好了。当你去查看实现合约(即 Counter 合约)的 Owner 时会发现是零地址,这是对的,因为实现合约只负责业务逻辑,不做存储。
在进行合约验证时,应先验证实现合约,再验证代理合约。
打开 X Layer 测试网的区块浏览器,找到我们刚才部署的合约,其中有个合约
选项框,打开之后能看到有跳转验证页面的按钮,点击[去验证合约->
]就可以进入验证页面了。
<br />
这里的 编译器类型选择Solidity(SingleFile)
,编译器版本选择你编译合约的版本。之后点击下一步
进入下一个页面。
<br />
这个页面有很多选项,大部分不用管,把Counter合约的源码粘贴进来,其他的如 优化选项、开源许可类型等 根据自己的实际情况写就行了。
注意,这里的Counter合约源码需要先进行展开,以hardhat项目为例,执行npx hardhat flatten contracts/Counter.sol > contracts/Counter_flat.sol
,执行后,这个Counter_flat.sol
就是展开的代码。
当源码和所有选项都写好,就可以点击提交按钮了。如果你的所有步骤都操作正确,这时候合约就验证成功了。
上文说了,代理合约是Remix帮我们自动部署的,不是我们自己写的代码。所以问题是源码在哪里?
实际上,这个代理合约的源码就是 @openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol
。根据前面的验证流程,我们需要把这个合约的源码展开,因此,我们将这个合约复制到我们自己的hardhat项目中,把ERC1967Proxy.sol
中引用的相对路径(”./“, "../"
)都改成绝对路径(@openzeppelin/contracts......
), 之后,和上面实现合约验证一样,我们执行 npx hardhat flatten ......
就可以展开代码了。
还需要注意:验证代理合约时的编译器版本选择v0.8.7+commit.e28d00a7
,虽然我们自己部署合约用的是v0.8.18
,但是代理合约是Remix自动编译部署的,可能Remix编译代理合约的时候用的是v0.8.7
。
其他的验证流程和上面的实现合约验证流程一样,这里就不再赘述了。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!