# PXN 合约分析 **Published by:** [Jack_Zhang](https://paragraph.com/@jack-zhang-2/) **Published on:** 2022-05-07 **URL:** https://paragraph.com/@jack-zhang-2/pxn ## Content 初学者对 PXN 项目的合约分析 OpenSea : https://opensea.io/collection/pxnghostdivision 合约地址:https://etherscan.io/address/0x160c404b2b49cbc3240055ceaee026df1e8497a0#code Tips: 自学有一段时间了,尝试看了一些项目的合约,这是第一次写总结,有不足或者有问题的地方欢迎在推特上私信或留言,一起讨论学习成长。 https://twitter.com/Bowei52748580 合约类型 继承了 ERC721A;Ownable 关键变量 //荷兰拍的数量 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 合约概览分析 使用 Ownable 对函数调用的权限做了限制; mint函数中,均采用了_safemint; 对关键性信息做了 require校验:每个钱包mint数量,项目总mint数量,钱包余额,mint地址,荷兰拍和不同种类 mint 时间的起始判断; 以上判断可防止「超发」「科学家非法手段mint」「Reentrancy 重入攻击」「一个钱包多次mint」 使用 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 价格 条件校验: 判断荷兰拍开始。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。 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 msg.sender: 指直接调用智能合约函数的账户或智能合约地址; **tx.origin:**指调用智能合约函数的账户地址,只有账户地址可以是tx.origin. 因此可以通过 callerIsUser() 这个修改器判断当前合约调用者是不是合约地址。 知识点2 如何具体查看Transaction失败的原因? 首先到 etherscan 上我们可以看到某些 transcation 是失败的,此时我们可以将这个失败的 Transaction Hash 粘贴到 tenderly 上去查看具体原因。如图所示,还可以看到那些改变链上状态的函数消耗了多少 gas fee,用来调试优化合约时候可以用到。毕竟每次修改链上状态都是要花真金白银的。 区块链的特点是匿名、公开、透明、不可更改,每一笔交易所有的细节都能在链上看到,从最开始尝试看懂链上数据,到后来决定走科学家这条路从中收获很多,虽才刚刚开始,但持之以恒总会有满意的收获。 以后遇到火爆的项目会持续解读欢迎关注我的推特 在成为科学家的道路上一起成长,在赚钱的道路上并肩前行,你好我是Jack_Zhang. https://twitter.com/Bowei52748580 ## Publication Information - [Jack_Zhang](https://paragraph.com/@jack-zhang-2/): Publication homepage - [All Posts](https://paragraph.com/@jack-zhang-2/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@jack-zhang-2): Subscribe to updates