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

By [0x3c](https://paragraph.com/@gamfi) · 2022-05-06

---

> 本篇是一个学习记录，有任何错误和建议欢迎twitter私聊，我们共同进步
> 
> 0x3c 推特：[https://twitter.com/pangmadee](https://twitter.com/pangmadee)
> 
> NFT黑魔法推特 ：[https://twitter.com/MrsZaaa](https://twitter.com/MrsZaaa)

> NFT黑魔法Youtube：[https://www.youtube.com/c/NFT%E9%BB%91%E9%AD%94%E6%B3%95](https://www.youtube.com/c/NFT%E9%BB%91%E9%AD%94%E6%B3%95)

合约案例PXN：

[https://etherscan.io/address/0x160c404b2b49cbc3240055ceaee026df1e8497a0#code](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验证](https://storage.googleapis.com/papyrus_images/d609cd67c38e5ecd8ef749af73e2c917f08a33ef07a927ea438af46bf597f126.png)

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是白名单的价格。

![](https://storage.googleapis.com/papyrus_images/37703f2488a2d242a94791b0b42bace3e1fef07fcd49c4fbea160c8de5824bdc.png)

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

![](https://storage.googleapis.com/papyrus_images/8b4202e5a7ed288c0110de0cf062fe161a01ef44eca10a7e62dcf07d13b23c73.png)

4、devMint()

require判断的条件：

*   block.timestamp >= WL\_STARTING\_TIMESTAMP + 86400 白名单开始一天后
    

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

![](https://storage.googleapis.com/papyrus_images/68cc28a5550ae579fc20279a2b959c9df89612a7508516992c9e61571d883d58.png)

5、withdrawFunds()

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

![](https://storage.googleapis.com/papyrus_images/ec7c4210e27a11cc5a6c0cc885c5fa54f0ce2dc7aea954a932c1d1892d327624.png)

---

*Originally published on [0x3c](https://paragraph.com/@gamfi/nft-pxn)*
