以下場(chǎng)景可借鑒本文內(nèi)容
- 需要?jiǎng)?chuàng)建很多合約
- 需要使用conflux代付機(jī)制(只需將工廠合約設(shè)置為代付,即可無限創(chuàng)建新合約)
- 合約想要有可升級(jí)的能力(如:特殊玩法 or 代碼有bug)
- ERC-721 NFT
基于以上場(chǎng)景,需要三個(gè)主要合約實(shí)現(xiàn)
- 工廠合約
- 代理合約
- 邏輯合約
想要完全掌握本文內(nèi)容,你需要提前了解
- Conflux交易詳解:https://juejin.cn/post/6971741780429668365
- conflux-rpc文檔:OPEN-RPC Playground
- NFT開發(fā)示例: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
- "數(shù)字藏品"開發(fā)規(guī)范:https://forum.conflux.fun/t/conflux/15538
- 合約之間互相調(diào)用:https://zhuanlan.zhihu.com/p/503497056
- 可升級(jí)合約:https://learnblockchain.cn/article/4257
- 工廠 + 代理:http://t.csdn.cn/aym0I
- eip1967: https://zhuanlan.zhihu.com/p/480217161
代碼實(shí)現(xiàn)
1、工廠合約(創(chuàng)建代理合約、創(chuàng)建邏輯合約、代付)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@confluxfans/contracts/InternalContracts/InternalContractsHandler.sol";
import "./Monkey.sol";
import "./MonkeyProxy.sol";
import "./CloneFactory.sol";
interface LogicInterface {
function initialize(string memory name, string memory symbol, string memory uri) external;
}
interface ProxyInterface {
function mint(address to, uint256 tokenId) external;
function transfer(address from, address to, uint256 tokenId) external;
function burn(uint256 tokenId) external;
}
contract MonkeyFactory is CloneFactory {
address private _admin;
address private _logicTemplate;
SponsorWhitelistControl constant private SPONSOR = SponsorWhitelistControl(0x0888000000000000000000000000000000000001);
constructor(address admin, address logicTemplate) public{
_admin = admin;
_logicTemplate = logicTemplate;
_addPrivilege(admin);
}
function _addPrivilege(address admin) private {
address[] memory addressList = new address[](1);
addressList[0] = admin;
SPONSOR.addPrivilege(addressList);
}
function updateLogicTemplate(address logicTemplate) public {
require(_admin == msg.sender, "MonkeyFactory: must have admin role");
_logicTemplate = logicTemplate;
}
function createLogic() external returns(address){
require(_admin == msg.sender, "MonkeyFactory: must have admin role");
LogicInterface logic = LogicInterface(createClone(_logicTemplate));
return address(logic); // 這里可以考慮使用event
}
function createProxy(address logicAddr, string memory name, string memory symbol, string memory uri) external returns(address){
require(_admin == msg.sender, "MonkeyFactory: must have admin role");
bytes memory initData = abi.encodeWithSignature("initialize(string,string,string)", name, symbol, uri);
MonkeyProxy proxy = new MonkeyProxy(logicAddr, initData);
return address(proxy); // 這里可以考慮使用event
}
function upgradeLogic(address proxyAddr, address newAddress) public{
require(_admin == msg.sender, "MonkeyFactory: must have admin role");
(bool _ok, bytes memory ret) = proxyAddr.call(abi.encodeWithSignature(
"upgradeVersion(address,address)", newAddress
));
require(_ok, string(ret));
}
function mint(address proxyAddr, address to, uint256 tokenId) public{
require(_admin == msg.sender, "MonkeyFactory: must have admin role");
ProxyInterface proxy = ProxyInterface(proxyAddr);
proxy.mint(to, tokenId);
// 如果使用featureCode,可以在這里繼續(xù)操作,其他代碼則按需實(shí)現(xiàn)
}
function transfer(address proxyAddr, address from, address to, uint256 tokenId) external {
require(_admin == msg.sender, "MonkeyFactory: must have admin role");
ProxyInterface proxy = ProxyInterface(proxyAddr);
proxy.transfer(from, to, tokenId);
}
function burn(address proxyAddr, uint256 tokenId) external {
require(_admin == msg.sender, "MonkeyFactory: must have admin role");
ProxyInterface proxy = ProxyInterface(proxyAddr);
proxy.burn(tokenId);
}
// 考慮使用fallback調(diào)用代理合約,即使方法變更,也可以正常發(fā)起調(diào)用
}
2、代理合約
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 看明白這個(gè)合約,先了解下eip-1967,很多代碼是固定寫法
contract MonkeyProxy {
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1967.md
bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
constructor(address logic, bytes memory initData) {
require(logic != address(0),"MonkeyProxy: wrong proxy contract address");
StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = msg.sender;
StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = logic;
(bool _ok, bytes memory returnData) = logic.delegatecall(initData);
require(_ok, string(returnData));
}
// 基本是固定寫法
fallback() external payable {
address _impl = StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
assembly {
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize())
let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0)
let size := returndatasize()
returndatacopy(ptr, 0, size)
switch result
case 0 {
revert(ptr, size)
}
default {
return(ptr, size)
}
}
}
function upgradeVersion(address newAddress) public {
require(StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value == msg.sender, "MonkeyProxy: only admin can be modified");
StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newAddress;
}
}
3、邏輯合約文章來源:http://www.zghlxwxcb.cn/news/detail-796995.html
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@confluxfans/contracts/token/CRC721/extensions/CRC721Enumerable.sol";
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "./Initializable.sol";
contract Monkey is AccessControlEnumerable, CRC721Enumerable, Initializable {
using Strings for uint256;
string private _name;
string private _symbol;
string private _uri;
mapping(uint256 => uint256) public tokenFeatureCode;
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
constructor() public ERC721("", "") {}
function initialize(string memory name, string memory symbol, string memory uri) public initializer {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(MINTER_ROLE, msg.sender);
_name = name;
_symbol = symbol;
setURI(uri);
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function setURI(string memory newuri) public virtual {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Monkey: must have admin role to set URI");
_uri = newuri;
}
function _baseURI() internal view virtual override returns (string memory) {
return _uri;
}
function tokenURI(uint256 tokenId) public view virtual override(ERC721) returns (string memory) {
require(_exists(tokenId), "Monkey: nonexistent token");
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString(), ".json")) : "";
}
function mint(address to, uint256 tokenId) public virtual {
require(hasRole(MINTER_ROLE, _msgSender()), "Monkey: must have minter role to mint");
_mint(to, tokenId);
}
function burn(uint256 tokenId) public virtual {
require(hasRole(MINTER_ROLE, _msgSender()), "Monkey: must have admin role to burn");
_burn(tokenId);
}
function transfer(address from, address to, uint256 tokenId) public virtual {
require(hasRole(MINTER_ROLE, _msgSender()), "Monkey: must have admin role to transfer");
_transfer(from, to, tokenId);
}
function setTokenFeatureCode(uint256 tokenId, uint256 featureCode) public virtual {
require(hasRole(MINTER_ROLE, _msgSender()), "Monkey: must have minter role to mint");
require(tokenFeatureCode[tokenId] == 0, "Monkey: token feature code is already set up");
tokenFeatureCode[tokenId] = featureCode;
}
function addMinter(address minter) external {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Monkey: must have admin role to add minter");
grantRole(MINTER_ROLE, minter);
}
function removeMinter(address minter) external {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Monkey: must have admin role to remove minter");
revokeRole(MINTER_ROLE, minter);
}
/**
* See {IERC165-supportsInterface}
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, ERC721Enumerable) returns (bool) {
return AccessControlEnumerable.supportsInterface(interfaceId) || ERC721Enumerable.supportsInterface(interfaceId);
}
}
代碼中用到的工具類、庫都可以找到開源代碼,若有需要可點(diǎn)贊、留言,或私聊,小的會(huì)補(bǔ)充文章來源地址http://www.zghlxwxcb.cn/news/detail-796995.html
到了這里,關(guān)于conflux開發(fā)NFT智能合約(ERC721 & 工廠合約 & 可升級(jí)合約)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!