PXN 合约分析

初学者对 PXN 项目的合约分析

OpenSea : https://opensea.io/collection/pxnghostdivision

合约地址:https://etherscan.io/address/0x160c404b2b49cbc3240055ceaee026df1e8497a0#code

Tips:

自学有一段时间了,尝试看了一些项目的合约,这是第一次写总结,有不足或者有问题的地方欢迎在推特上私信或留言,一起讨论学习成长。

https://twitter.com/Bowei52748580

合约类型

继承了 ERC721A;Ownable

post image

关键变量

//荷兰拍的数量 4k
DA_QUANTITY = 4000; 

//白单可以mint 6k.
WL_QUANTITY = 6000;

//WL Price 白单地板价
WLprice = 0.35 ether; 

//合拍开始价格 2e
DA_STARTING_PRICE = 2 ether;

//合拍结束价格 0.1
DA_ENDING_PRICE = 0.1 ether;

//合拍递减的价格 0.05
DA_DECREMENT = 0.05 ether;

//每15分钟降价一次
DA_DECREMENT_FREQUENCY = 900;

//荷兰拍开始的时间
DA_STARTING_TIMESTAMP = 1651719600

总结:

该项目共计 10k 个nft,荷兰拍起拍价 2e,之后每隔15分钟降价 0.05e,白单mint价格 0.35e

合约概览分析

  1. 使用 Ownable 对函数调用的权限做了限制;

  2. mint函数中,均采用了_safemint;

  3. 对关键性信息做了 require校验:每个钱包mint数量,项目总mint数量,钱包余额,mint地址,荷兰拍和不同种类 mint 时间的起始判断;

  4. 以上判断可防止「超发」「科学家非法手段mint」「Reentrancy 重入攻击」「一个钱包多次mint」

  5. 使用 callerIsUser() 防止合约调用;

    modifier callerIsUser() {
      require(tx.origin == msg.sender, "The caller is another contract");
      _;
    }
    

业务逻辑分析

荷兰拍 -→ mintDutchAuction

基础逻辑:

  • 荷兰拍共提供 4000 个,起拍价 2e,然后每隔15分钟降价 0.05e。

  • 荷兰拍开始时间是:

起拍时间的时间戳
起拍时间的时间戳
  • 时间戳转换之后如下:

时间戳转换之后的时间
时间戳转换之后的时间
  • 随着时间的推移,荷兰拍的价格会逐渐下降,当荷兰拍价格小于 0.7 时那么此时 WL mint的价格就是:「当前荷兰拍价格/ 2」。若当前荷兰拍结束后其价格是0.6 那么白单的mint价格从初始的 0.35e 变为 0.3e。所以荷兰拍的最终价格可能会影响到 WL 的mint 价格。

荷兰拍最终价格影响 WL 价格
荷兰拍最终价格影响 WL 价格

条件校验:

  • 判断荷兰拍开始。DA_ACTIVE == TRUE 【合约状态校验】

  • 判断前端页面传过来的的加密签名是不是和后端加密之后的一样,这样可以防止合约机器人调用合约。【校验合约调用者】

  • 判断荷兰拍是否到4000个【防止超发】

  • 通过获取当前区块时间来判断是否到荷兰拍的开始时间【荷兰拍时间校验】

  • 若当前区块时间在白单开始时间之前那么此时荷兰拍尚未结束【荷兰拍结束时间校验】

    • 白单mint 开始时间是荷兰拍进行24小时之后,换句话说,荷兰拍持续时间是24小时。

  • 校验当前钱包mint的数量,要求数量最多 2 个【钱包 mint 数量校验】

  • 判断当前钱包余额是否够支付【金额校验】

    当上边的校验都通过之后开始执行 _safeMint()

  • 第一个成功通过荷兰拍 mint 的人共花了 4k多刀的 gas fee。所以下次如果参加 荷兰拍想要提前确认交易,就尽可能提高你的 gas 设置吧。至于为什么这和 POW 共识机制有关,请自行Google。

  • 整个荷兰拍在10分钟左右就结束了。所以说这个项目非常火。最后一个人mint 的gas fee 只有55刀,可见时机的把握可以节省不少 gas fee。

荷兰拍第一个成功确认交易的人
荷兰拍第一个成功确认交易的人

白单→mintWL

条件校验:

  • 签名校验与荷兰拍的签名校验方法一样【校验合约调用者】

  • 判断mint 数量不超过 6000 个【防止超发】

  • 校验当前 mint 地址是否已经 mint 过,一个地址只能 mint 一次,mint 数量也是 1.【防止单个地址多次 mint 】

  • 获取当前区块时间应大于白单开始 mint 的时间,否则白单 mint 尚未开始【校验mint 开始时间】

  • 校验当前 msg.value 应该足够支付【看是否有足够的金额支付交易】

  • 执行 _safeMint() 完成交易

开发团队→ devMint

  • 白单mint 一天「24h」之后,dev 团队开始mint,dev团队mint 的数量就是项目总提供量 10k - 已经被mint的。

  • dev mint 的方法可以理解为将尚未mint 的全部打到dve 的地址中。他们mint的方式是10个10个的mint,这样应该是为了节省 gas fee。

post image

team → teamMint

条件校验:

  • team mint 时间和白单 mint 开始时间相同【mint 时间校验】

  • team 的mint 地址不能超过限定的数量,这个根据贡献度不同 mint 的数量不同【规则校验】

  • 校验钱包的余额是否够支付此次交易【余额校验】

  • 然后执行 _safeMint()

以上就是 PXN 项目的合约分析,如果有不懂的或者有问题的欢迎讨论在我的推特下留言。

补充知识

知识点1:

在修改器 modifier 中我们看到 tx.origin == msg.sender,这段代码是什么意思呢?请看图

modifier callerIsUser() {
  require(tx.origin == msg.sender, "The caller is another contract");
  _;
}
原文地址:https://davidkathoh.medium.com/tx-origin-vs-msg-sender-93db7f234cb9
原文地址:https://davidkathoh.medium.com/tx-origin-vs-msg-sender-93db7f234cb9

msg.sender: 指直接调用智能合约函数的账户或智能合约地址;

**tx.origin:**指调用智能合约函数的账户地址,只有账户地址可以是tx.origin.

因此可以通过 callerIsUser() 这个修改器判断当前合约调用者是不是合约地址。

知识点2

如何具体查看Transaction失败的原因?

首先到 etherscan 上我们可以看到某些 transcation 是失败的,此时我们可以将这个失败的 Transaction Hash 粘贴到 tenderly 上去查看具体原因。如图所示,还可以看到那些改变链上状态的函数消耗了多少 gas fee,用来调试优化合约时候可以用到。毕竟每次修改链上状态都是要花真金白银的。

post image

区块链的特点是匿名、公开、透明、不可更改,每一笔交易所有的细节都能在链上看到,从最开始尝试看懂链上数据,到后来决定走科学家这条路从中收获很多,虽才刚刚开始,但持之以恒总会有满意的收获。

以后遇到火爆的项目会持续解读欢迎关注我的推特

在成为科学家的道路上一起成长,在赚钱的道路上并肩前行,你好我是Jack_Zhang.

https://twitter.com/Bowei52748580