【NFT黑魔法】第二期合约分析学习PXN

本篇是一个学习记录,有任何错误和建议欢迎twitter私聊,我们共同进步

0x3c 推特:https://twitter.com/pangmadee

NFT黑魔法推特 :https://twitter.com/MrsZaaa

NFT黑魔法Youtube:https://www.youtube.com/c/NFT%E9%BB%91%E9%AD%94%E6%B3%95

合约案例PXN:

https://etherscan.io/address/0x160c404b2b49cbc3240055ceaee026df1e8497a0#code

合约类型

721A合约

合约结构(分配方式、mint手段)

总量10000个,分别分配给荷兰拍、白名单、team、dev

荷兰拍:在区块时间1651719600开始,初始价格2ether,结束价格0.1ether,每隔900秒下降0.05ether,每个地址最多两个,总共4000个。

白名单:在荷兰拍开始一天后开始,时间限制1天,总共6000个,每个地址一个。

team:和白名单开始的时候,mint的数量加上目前已经被mint的数量不能超过10000个。

dev:白名单开始一天后开始,10000个减去已经mint的剩余的部分。

部署合约

采用 hardhat部署

审计分析

  1. 每一种mint都采用的_safeMint来防止Reentrancy

  2. 继承openzeppelin的Ownable合约,在需要设置权限的函数都做了限制

  3. 合约开启用的固定时间,不会被矿工操纵

  4. callerIsUser禁止了荷兰拍、白名单用合约调用

  5. recover 禁止了机器人调用

业务逻辑分析

1、mintDutchAuction() 荷兰拍

require判断的条件:

  • DA_ACTIVE 是否为true,由owner触发开始,public类型可以查看

  • directMintAllowed 是否为true,private类型外部无法查看状态,主网合约才添加的,测试网没有。如果为true,recover(signature)验证

ECDSA验证
ECDSA验证

对地址进行编码和keccak256哈希得到散列地址,并由signerAddress钱包签名,除非拥有signerAddress的私钥,否则这个操作需要在前端完成,这是为了防止机器人mint。

  • block.timestamp >= DA_STARTING_TIMESTAMP,区块时间超过设定的时间

  • block.timestamp <= WL_STARTING_TIMESTAMP 荷兰拍时间是否结束

  • quantity <= 2 和balanceOf(msg.sender) + quantity < 3

    _numberMinted(msg.sender) + quantity <= 2都是判断最多2个

  • msg.value >= quantity * _currentPrice 钱包金额足够支付

  • totalSupply() + quantity <= DA_QUANTITY 不超过最大的荷兰拍总数

当达到最大的荷兰拍总数时,计算此时的的价格,如果此时的价格小于白名单固定价格,此时价格+0.5是白名单的价格。

post image

2、mintWL() 白名单

require判断的条件:

  • DA_FINAL_PRICE>0 荷兰拍结束

  • !userToHasMintedPublicWL[msg.sender] 是否已经mint

  • recover(signature) 防机器人mint

  • block.timestamp <= WL_STARTING_TIMESTAMP + 86400 时间判断

  • PUBLIC_WL_MINTED + 1 <= WL_QUANTITY 总数最多6000个

  • .recover(signature) 验证消息是否由给定地址的私钥持有者签名

  • msg.value >= WLprice 钱包足够支付

_safeMint(msg.sender, 1); 只能mint一个

3、teamMint()

require判断的条件:

  • block.timestamp >= WL_STARTING_TIMESTAMP 和白名单开始的时间相同

  • _teamList[msg.sender] >= quantity mint的数量不能超过限定

  • msg.value >= quantity * WLprice 钱包足够支付

  • totalSupply() + quantity <= 10000 mint后的数量不超过总数

_teamList[msg.sender] = _teamList[msg.sender] - quantity; team的地址减去已经mint的数量

这里是有owner设置团队的地址,每个地址可以mint同意的数量amount

post image

4、devMint()

require判断的条件:

  • block.timestamp >= WL_STARTING_TIMESTAMP + 86400 白名单开始一天后

10000个减去已经mint的剩余的部分,这里有点特别是分了两次转

post image

5、withdrawFunds()

合约里面的钱给了dev和founder分别转了一半

post image