前置参考文档
- 《NFT区块链进阶指南一》Remix部署Solidity ERC721合约(NFT合约)到Etherscan - 掘金 (juejin.cn)
- 《NFT区块链进阶指南二》Etherscan验证Solidity智能合约(Remix插件验证) - 掘金 (juejin.cn)
前置了解知识:已经属性参考文档中的普通合约部署和验证以及remix的操作、Openzeppelin可升级代理合约
一、代理合约说明
- 普通合约(逻辑合约)指一些完整的业务逻辑,通过执行智能合约的代码实现。普通合约是以太坊智能合约的核心,可用于实现各种应用,如数字投票、保险、银行、能源等
- 代理合约是一种特殊的合约,它不包含完整的业务逻辑,而是用于代理传递数据和功能。代理合约的作用是将一组合约封装在一起,以提供更简洁和高级的功能,例如访问其他合约的方法、存储其他合约的状态、执行预处理操作等
- 普通合约一旦部署之后便不能再更改代理,如果合约发生重大BUG时将会导致很严重的资产损失,所以有了代理合约,代理合约此处采用Openzeppelin可升级代理合约实现,具体可以参考官方文档
特别说明:为了减少代理合约部署的复杂性操作失误,建议先熟悉参考文档里面普通合约部署。可升级代理合约因为操作复杂,将会按照 部署、验证、升级 分三篇文章进行说明,本篇为部署篇
代理合约的部署,需要按照下面的步骤进行部署(合约验证顺序可以无关,但是合约部署必须要按照下面的顺序)
- 部署逻辑合约
- 验证逻辑合约
- 部署代理合约
- 验证代理合约
二、部署逻辑合约
逻辑合约即真实完成合约逻辑的合约,也是主合约(可以理解为之前部署的普通合约)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
contract ERC721UpgradeableMint is ERC721Upgradeable, OwnableUpgradeable {
using StringsUpgradeable for uint256;
string public email;
function initialize(string memory _tokenName, string memory _tokenSymbol, string memory _email) initializer public {
__ERC721_init(_tokenName, _tokenSymbol);
__Ownable_init();
setEmail(_email);
}
function setEmail(string memory _email) public onlyOwner {
email = _email;
}
}
- 代码复制到remix中进行编译
- 切换到部署环境,选择部署环境为内置环境然后部署(此处为了获取initialize方法的入参信息,在部署代理合约的时候需要使用,相当于在部署代理合约的时候需要对逻辑合约调用此方法完成初始化)
- 展开部署成功的合约,然后找到initialize,按照项目的需要指定对应的参数信息
- 在控制台中找到交易记录,单击 Debug 按钮右边的箭头展开,然后找到里面的 input 参数就是此方法的入参,然后记录下来
- 此处复制出来的参数如下
0xa6487c53000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000045469616e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000358696e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000147469616e78696e636f6f7264403136332e636f6d000000000000000000000000
- 编译完成之后切换到部署选项,然后部署环境选择钱包环境进行部署
- 代理合约没有构造方法,所以直接部署即可
- 部署完成后复制合约地址
- 后续代理合约验证会单独写一篇文章,相比普通合约,代理合约的验证存在不一样的地方
三、部署代理合约
- 代理合约为固定写法
- 代理合约在部署的时候需要指定逻辑合约地址,在第一次部署时,代理合约将会保存所有逻辑合约的变量布局,所有的数据均存储在代理合约中
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
// Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
}
此部分特别重要,必须要了解清楚了再开始操作
- 部署代理合约需要准备四个东西
- 部署钱包:用来部署合约,为owner拥有者,代表合约里面 onlyOwner 权限
- 如部署钱包:0xA67bB11CD60106b175F1dC09BE38Af8eAEdf825A
- 管理员钱包:用来升级代理合约,为admin,只具备操作代理合约权限
- 如管理员钱包:0x145dEb82bfe84491209fBC14295ccd17986d659C
- 管理钱包只能用来操作代理合约,无法操作任何有关逻辑合约的东西
- 通常选择一个不常用的钱包,千万不要和部署钱包一样,否则会导致逻辑所有所有owner相关操作全部无法处理(如合约取款、转账等等)
- 所有管理员钱包发起的调用都将由代理合约本身处理,调用逻辑合约会报错
- 逻辑合约地址
- **上面部署的逻辑合约地址:**0xcf261854385730a3b837b021c6b3f70649106b4c
- 此逻辑合约的所有变量布局会在部署时全部映射到代理合约中
- 部署的逻辑合约一定要仔细检查,否则出现问题后无法修改内存布局,只能新增变量
- 部署代理合约时,逻辑合约地址中的initialize方法将自动被调用
- initialize参数,上面部署逻辑合约时保存的 initialize 方法的input数据
- 部署钱包:用来部署合约,为owner拥有者,代表合约里面 onlyOwner 权限
0xa6487c53000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000045469616e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000358696e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000147469616e78696e636f6f7264403136332e636f6d000000000000000000000000
- 将代理合约代理复制到remix中,然后进行编译
- 编译完成后进行部署,在构造方法中填写逻辑合约地址、管理员钱包地址、DATA(initialize参数),然后使用部署钱包部署
- 部署成功后获取对应的代理合约地址:0xf3cc0a7174d9525d49306b6b8aef2ef1d706ef14
- 后续代理合约验证会单独写一篇文章,相比普通合约,代理合约的验证存在不一样的地方
- 以上操作一定要特别特别小心,一旦一个逻辑合约被使用(初始化方法只能调用一次)发现出错了,那么严重情况下将会导致代理合约和普通合约都报废(大型项目一次部署所花费都是按照上千¥起,土豪就请忽略)
- 综上所述,在部署代理合约到生产环境之前,一定一定要在测试环境部署好(操作步骤完全一致)没有问题再上生产