宇宙就是一座黑暗森林,每个文明都是带枪的猎人,像幽灵般潜行于林间,轻轻拨开挡路的树枝,竭力不让脚步发出一点儿声音,连呼吸都小心翼翼他必须小心,因为林中到处都有与他一样潜行的猎人。——《黑暗森林》
Dark Forest(黑暗森林)这个概念来源于《三体》,Dan和 Georgios 从范式中推广的加密术语,其中用来描述以太坊的环境。以太坊是一个高度对抗的环境,链本身就是一个战场,同样包括内存池。在我看来加密货币交易亦是如此,充斥着混沌、无秩序、野蛮、掠夺。这就是区块链的世界,是每个web3.0(区块链)探索者必经之路。欢迎来到黑暗森林!
ERC721R:为NFT创造者带来更多的责任感
ERC721R主要作用区别于ERC721、ERC1155以及ERC875的作用主要为 NFT 智能合约添加了无需信任的退款,允许铸币者在给定的退款期内返还以成本铸造的 NFT。
优势
买家30天内可以退款,只损失手续费
退回的代表金额不会低于mint时的出售价格
增加创作者的责任感
减少买家购买NFT的风险
劣势
权限过大,refund函数中管理员权限过大,当用户进行退款时,管理员将得到退款的NFT,refund中并没有对管理员做些权限限制,管理员也可以操作退款NFT,耗尽合约的资金
合约漏洞,买家没有归还NFT的时间段内,管理员可以铸造NFT,然后像上一步的描述中那样,耗尽合约资金
https://github.com/exo-digital-labs/ERC721R/blob/main/contracts/ERC721RExample.sol
//ERC721Reciver
contract ERC721RExample is ERC721A, Ownable {
// 最大铸造数量
uint256 public constant maxMintSupply = 8000;
// 保护的最低价格
uint256 public constant mintPrice = 0.1 ether;
// NFT退回时间
uint256 public constant refundPeriod = 45 days;
// 公开销售活跃状态
bool public publicSaleActive;
// 预售活跃状态
bool public presaleActive;
// 退回结束时间
uint256 public refundEndTime;
// 退回的管理者地址
address public refundAddress;
// 用户最大铸造的数量
uint256 public constant maxUserMintAmount = 5;
// 默克尔树根
bytes32 public merkleRoot;
// 已经退回的NFT,通过NFT的id查询
mapping(uint256 => bool) public hasRefunded; // users can search if the NFT has been refunded
// 是否是所有者铸造的NFT
mapping(uint256 => bool) public isOwnerMint; // if the NFT was freely minted by owner
// NFT的基本链接
string private baseURI;
// 初始化NFT名称和标识
constructor() ERC721A("ERC721RExample", "ERC721R") {
// 退回地址为部署者,也就是管理员
refundAddress = msg.sender;
toggleRefundCountdown();
}
预售NFT的函数是preSafeMint,可以理解为早期的空投、私募和一级市场的买家获得的资格
// 铸造NFT
// @param `quantity` 铸造数量
// @param `proof` 叶子到树根的分支上的兄弟姐妹哈希值
function preSaleMint(uint256 quantity, bytes32[] calldata proof)
确保预售已经开始
require(presaleActive, "Presale is not active");确保当前买家的余额 == NFT数量 * 铸造价格
require(msg.value == quantity * mintPrice, "Value");确定买家在预售白名单中,通过MerkleProof进行判断
require( _isAllowlisted(msg.sender, proof, merkleRoot), "Not on allow list" );买家可铸造NFT的数量 + NFT数量 <= 用户最大的铸造数量
require( _numberMinted(msg.sender) + quantity <= maxUserMintAmount, "Max amount" );供应的铸造数量 + 铸造的数量 <= 最大铸造数量
require(_totalMinted() + quantity <= maxMintSupply, "Max mint supply");安全铸造
_safeMint(msg.sender, quantity);
公开销售NFT的函数是publicSaleMint,和preSaleMint逻辑一样
// 公开销售NFT
// @param `quantity` 铸造数量
function publicSaleMint(uint256 quantity) external payable
确保公开销售已经开始
require(publicSaleActive, "Public sale is not active");确保当前买家的余额 == NFT数量 * 铸造价格
require(msg.value >= quantity * mintPrice, "Not enough eth sent");买家可铸造NFT的数量 + NFT数量 <= 用户最大的铸造数量
require( _numberMinted(msg.sender) + quantity <= maxUserMintAmount, "Over mint limit" );供应的铸造数量 + 铸造的数量 <= 最大铸造数量
require( _totalMinted() + quantity <= maxMintSupply, "Max mint supply reached" );安全铸造
_safeMint(msg.sender, quantity);
所有者铸造NFT的函数是ownerMint,只允许所有者调用
function ownerMint(uint256 quantity) external onlyOwner
供应的铸造数量 + 铸造的数量 <= 最大铸造数量
require( _totalMinted() + quantity <= maxMintSupply, "Max mint supply reached" );安全铸造
_safeMint(msg.sender, quantity);将所有者铸造的所有NFT编号映射在isOwnerMint
for (uint256 i = _currentIndex - quantity; i < _currentIndex; i++) { isOwnerMint[i] = true; }
退回NFT的函数是refund,由买家外部调用
// @param `tokenIds` 退回的NFT编号数组
function refund(uint256[] calldata tokenIds) external
退回NFT的时间是否过期
require(isRefundGuaranteeActive(), "Refund expired");确保NFT已经售出
require(msg.sender == ownerOf(tokenId), "Not token owner");确保NFT没有重复退回
require(!hasRefunded[tokenId], "Already refunded");确保NFT不属于所有者铸造
require(!isOwnerMint[tokenId], "Freely minted NFTs cannot be refunded");记录退回NFT的状态
hasRefunded[tokenId] = true;退回金额 = NFT数量 * 铸造时的价格
uint256 refundAmount = tokenIds.length * mintPrice;调用transferFrom进行NFT转让
transferFrom(msg.sender, refundAddress, tokenId);退回NFT的资金给买家
Address.sendValue(payable(msg.sender), refundAmount);
提前资金的函数是withdraw,仅限管理员操作
function withdraw() external onlyOwner
确保提取资金时间在退款结束后
require(block.timestamp > refundEndTime, "Refund period not over");获取合约余额
uint256 balance = address(this).balance;提现合约资金
Address.sendValue(payable(owner()), balance);
// 判断是否退回时间内
function isRefundGuaranteeActive() public view returns (bool) {
return (block.timestamp <= refundEndTime);
}
// 返回NFT的基本链接
function _baseURI() internal view override returns (string memory) {
return baseURI;
}
// 设置NFT退回的地址,只允许管理员调用
function setRefundAddress(address _refundAddress) external onlyOwner {
refundAddress = _refundAddress;
}
// 设置Merkle根,只允许管理员调用
function setMerkleRoot(bytes32 _root) external onlyOwner {
merkleRoot = _root;
}
// 设置基本链接,只允许管理员调用
function setBaseURI(string memory uri) external onlyOwner {
baseURI = uri;
}
// 设置退回时间,只允许管理员调用
function toggleRefundCountdown() public onlyOwner {
refundEndTime = block.timestamp + refundPeriod;
}
// 切换预售状态,只允许管理员调用
function togglePresaleStatus() external onlyOwner {
presaleActive = !presaleActive;
}
// 切换公开销售状态,只允许管理员调用
function togglePublicSaleStatus() external onlyOwner {
publicSaleActive = !publicSaleActive;
}
// 叶子节点
function _leaf(address _account) internal pure returns (bytes32){
return keccak256(abi.encodePacked(_account));
}
// 调用MerkleProof, 判断是否满足MerkleProot
function _isAllowlisted(
address _account,
bytes32[] calldata _proof,
bytes32 _root
) internal pure returns (bool) {
return MerkleProof.verify(_proof, _root, _leaf(_account));
}

