当前位置:   article > 正文

conflux开发NFT智能合约(ERC721 & 工厂合约 & 可升级合约)_erc721升级

erc721升级

以下场景可借鉴本文内容

  • 需要创建很多合约
  • 需要使用conflux代付机制(只需将工厂合约设置为代付,即可无限创建新合约)
  • 合约想要有可升级的能力(如:特殊玩法 or 代码有bug)
  • ERC-721 NFT

基于以上场景,需要三个主要合约实现

  • 工厂合约
  • 代理合约
  • 逻辑合约

想要完全掌握本文内容,你需要提前了解

  • Conflux交易详解:https://juejin.cn/post/6971741780429668365
  • conflux-rpc文档:OPEN-RPC Playground
  • NFT开发示例:https://forum.conflux.fun/t/conflux-2022-5-18-721-20-721-1155-nft/8781
  • conflux metadata: https://forum.conflux.fun/t/conflux-metadata/16083
  • "数字藏品"开发规范:https://forum.conflux.fun/t/conflux/15538
  • 合约之间互相调用:https://zhuanlan.zhihu.com/p/503497056
  • 可升级合约:https://learnblockchain.cn/article/4257
  • 工厂 + 代理:http://t.csdn.cn/aym0I
  • eip1967: https://zhuanlan.zhihu.com/p/480217161

代码实现

1、工厂合约(创建代理合约、创建逻辑合约、代付)

  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. import "@confluxfans/contracts/InternalContracts/InternalContractsHandler.sol";
  4. import "./Monkey.sol";
  5. import "./MonkeyProxy.sol";
  6. import "./CloneFactory.sol";
  7. interface LogicInterface {
  8. function initialize(string memory name, string memory symbol, string memory uri) external;
  9. }
  10. interface ProxyInterface {
  11. function mint(address to, uint256 tokenId) external;
  12. function transfer(address from, address to, uint256 tokenId) external;
  13. function burn(uint256 tokenId) external;
  14. }
  15. contract MonkeyFactory is CloneFactory {
  16. address private _admin;
  17. address private _logicTemplate;
  18. SponsorWhitelistControl constant private SPONSOR = SponsorWhitelistControl(0x0888000000000000000000000000000000000001);
  19. constructor(address admin, address logicTemplate) public{
  20. _admin = admin;
  21. _logicTemplate = logicTemplate;
  22. _addPrivilege(admin);
  23. }
  24. function _addPrivilege(address admin) private {
  25. address[] memory addressList = new address[](1);
  26. addressList[0] = admin;
  27. SPONSOR.addPrivilege(addressList);
  28. }
  29. function updateLogicTemplate(address logicTemplate) public {
  30. require(_admin == msg.sender, "MonkeyFactory: must have admin role");
  31. _logicTemplate = logicTemplate;
  32. }
  33. function createLogic() external returns(address){
  34. require(_admin == msg.sender, "MonkeyFactory: must have admin role");
  35. LogicInterface logic = LogicInterface(createClone(_logicTemplate));
  36. return address(logic); // 这里可以考虑使用event
  37. }
  38. function createProxy(address logicAddr, string memory name, string memory symbol, string memory uri) external returns(address){
  39. require(_admin == msg.sender, "MonkeyFactory: must have admin role");
  40. bytes memory initData = abi.encodeWithSignature("initialize(string,string,string)", name, symbol, uri);
  41. MonkeyProxy proxy = new MonkeyProxy(logicAddr, initData);
  42. return address(proxy); // 这里可以考虑使用event
  43. }
  44. function upgradeLogic(address proxyAddr, address newAddress) public{
  45. require(_admin == msg.sender, "MonkeyFactory: must have admin role");
  46. (bool _ok, bytes memory ret) = proxyAddr.call(abi.encodeWithSignature(
  47. "upgradeVersion(address,address)", newAddress
  48. ));
  49. require(_ok, string(ret));
  50. }
  51. function mint(address proxyAddr, address to, uint256 tokenId) public{
  52. require(_admin == msg.sender, "MonkeyFactory: must have admin role");
  53. ProxyInterface proxy = ProxyInterface(proxyAddr);
  54. proxy.mint(to, tokenId);
  55. // 如果使用featureCode,可以在这里继续操作,其他代码则按需实现
  56. }
  57. function transfer(address proxyAddr, address from, address to, uint256 tokenId) external {
  58. require(_admin == msg.sender, "MonkeyFactory: must have admin role");
  59. ProxyInterface proxy = ProxyInterface(proxyAddr);
  60. proxy.transfer(from, to, tokenId);
  61. }
  62. function burn(address proxyAddr, uint256 tokenId) external {
  63. require(_admin == msg.sender, "MonkeyFactory: must have admin role");
  64. ProxyInterface proxy = ProxyInterface(proxyAddr);
  65. proxy.burn(tokenId);
  66. }
  67. // 考虑使用fallback调用代理合约,即使方法变更,也可以正常发起调用
  68. }

2、代理合约

  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. // 看明白这个合约,先了解下eip-1967,很多代码是固定写法
  4. contract MonkeyProxy {
  5. // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1967.md
  6. bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
  7. bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
  8. constructor(address logic, bytes memory initData) {
  9. require(logic != address(0),"MonkeyProxy: wrong proxy contract address");
  10. StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = msg.sender;
  11. StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = logic;
  12. (bool _ok, bytes memory returnData) = logic.delegatecall(initData);
  13. require(_ok, string(returnData));
  14. }
  15. // 基本是固定写法
  16. fallback() external payable {
  17. address _impl = StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
  18. assembly {
  19. let ptr := mload(0x40)
  20. calldatacopy(ptr, 0, calldatasize())
  21. let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0)
  22. let size := returndatasize()
  23. returndatacopy(ptr, 0, size)
  24. switch result
  25. case 0 {
  26. revert(ptr, size)
  27. }
  28. default {
  29. return(ptr, size)
  30. }
  31. }
  32. }
  33. function upgradeVersion(address newAddress) public {
  34. require(StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value == msg.sender, "MonkeyProxy: only admin can be modified");
  35. StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newAddress;
  36. }
  37. }

3、逻辑合约

  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. import "@confluxfans/contracts/token/CRC721/extensions/CRC721Enumerable.sol";
  4. import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
  5. import "./Initializable.sol";
  6. contract Monkey is AccessControlEnumerable, CRC721Enumerable, Initializable {
  7. using Strings for uint256;
  8. string private _name;
  9. string private _symbol;
  10. string private _uri;
  11. mapping(uint256 => uint256) public tokenFeatureCode;
  12. bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
  13. constructor() public ERC721("", "") {}
  14. function initialize(string memory name, string memory symbol, string memory uri) public initializer {
  15. _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
  16. _setupRole(MINTER_ROLE, msg.sender);
  17. _name = name;
  18. _symbol = symbol;
  19. setURI(uri);
  20. }
  21. function name() public view virtual override returns (string memory) {
  22. return _name;
  23. }
  24. function symbol() public view virtual override returns (string memory) {
  25. return _symbol;
  26. }
  27. function setURI(string memory newuri) public virtual {
  28. require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Monkey: must have admin role to set URI");
  29. _uri = newuri;
  30. }
  31. function _baseURI() internal view virtual override returns (string memory) {
  32. return _uri;
  33. }
  34. function tokenURI(uint256 tokenId) public view virtual override(ERC721) returns (string memory) {
  35. require(_exists(tokenId), "Monkey: nonexistent token");
  36. string memory baseURI = _baseURI();
  37. return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString(), ".json")) : "";
  38. }
  39. function mint(address to, uint256 tokenId) public virtual {
  40. require(hasRole(MINTER_ROLE, _msgSender()), "Monkey: must have minter role to mint");
  41. _mint(to, tokenId);
  42. }
  43. function burn(uint256 tokenId) public virtual {
  44. require(hasRole(MINTER_ROLE, _msgSender()), "Monkey: must have admin role to burn");
  45. _burn(tokenId);
  46. }
  47. function transfer(address from, address to, uint256 tokenId) public virtual {
  48. require(hasRole(MINTER_ROLE, _msgSender()), "Monkey: must have admin role to transfer");
  49. _transfer(from, to, tokenId);
  50. }
  51. function setTokenFeatureCode(uint256 tokenId, uint256 featureCode) public virtual {
  52. require(hasRole(MINTER_ROLE, _msgSender()), "Monkey: must have minter role to mint");
  53. require(tokenFeatureCode[tokenId] == 0, "Monkey: token feature code is already set up");
  54. tokenFeatureCode[tokenId] = featureCode;
  55. }
  56. function addMinter(address minter) external {
  57. require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Monkey: must have admin role to add minter");
  58. grantRole(MINTER_ROLE, minter);
  59. }
  60. function removeMinter(address minter) external {
  61. require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Monkey: must have admin role to remove minter");
  62. revokeRole(MINTER_ROLE, minter);
  63. }
  64. /**
  65. * See {IERC165-supportsInterface}
  66. */
  67. function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, ERC721Enumerable) returns (bool) {
  68. return AccessControlEnumerable.supportsInterface(interfaceId) || ERC721Enumerable.supportsInterface(interfaceId);
  69. }
  70. }

代码中用到的工具类、库都可以找到开源代码,若有需要可点赞、留言,或私聊,小的会补充

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/566338
推荐阅读
相关标签
  

闽ICP备14008679号