大家好,我是 Jacob Wei,17 年入圈起一直从事区块链相关工作,专注智能合约与服务端的开发。最近 NFT 领域的兴起让我兴奋不已,因此我想在这里和大家分享一些 NFT 教程中缺失的部分即:NFT 项目的开发、部署、上线的完整流程。
本文需对对智能合约及 ERC721 有简单的了解,同时不会包含具体的代码实现,相关的开发资源都附上了对应的教程,本文更多的是分享一些开发思路。
关于 NFT 的章节,计划分为 3 个部分:
本文将从合约、后端、前端三个方面讲述如何在 NFT 发售阶段做到尽量公平,尽可能让真实用户 Mint 成功。文章结构如下:
NFT 的发售中大家讨论最多的问题归纳一下分为以下几种:
如果合约允许其他合约调用 Mint ,那大概率就会变成科学家秀操作的舞台。即使每个地址限量也可通过工程合约创建出期望数量的地址进行 Mint。
防范手段: 通过判断 TX 的发出方和发售合约收到请求的来源是否一致来判断,是否来自于其他合约的调用。如 Azuki 中的验证如下:
modifier callerIsUser() {
require(tx.origin == msg.sender, "The caller is another contract");
_;
}
注:关于 msg.sender
and tx.origin
的区别示例可以查看 Phishing with tx.origin
常见的白名单验证方式分为两种:
对于白名单数量较少或对 Merkle tree 不太熟悉的项目方可能会采用这种方式,优点就是逻辑简单,可以随时添加或删除。缺点也很明显,就是项目方需要支付修改白名单数据的费用。如 Azuki 中可以批量设置白名单地址及对应的数量,gas 消耗数量可以参考 seedAllowlist Tx 。
function seedAllowlist(address[] memory addresses, uint256[] memory numSlots)
external
onlyOwner
{
require(
addresses.length == numSlots.length,
"addresses does not match numSlots length"
);
for (uint256 i = 0; i < addresses.length; i++) {
allowlist[addresses[i]] = numSlots[i];
}
}
比较常见同时推荐使用的的方式是合约验证 Merkle Proof,通过提交链下生成的 Proof 在合约中进行验证。这个方案也是经过了很多次验证,能够充分满足实际需求,除了验证白名单地址外还可以添加 Mint 数量、空投数量等信息。
Mint 过程中用户获取 Proof 的方法可以使用 API 或将 Merkle Tree 公开让用户自行获取,下面的资源中提供了 API 的示例。
这里有很多现成的方案可以参考,资源如下:
注意事项:
对于公开发售阶段(非白名单),一般会采用参数验证的方式来检验用户数据是否合法,从过往的案例中经常出现的问题有以下几个:
参数验证环节,推荐的开发模式:在参数验证环节推荐的开发模式为每个地址的生成不同的调用参数,以防止重放攻击。
参考资源:
ECDSA
library: Checking Signatures On-Chain针对后续两个问题我们放在后端章节进行讨论
项目方为了降低合约被攻击或滥用的可能选择不公开合约源代码,不单单是在 NFT 领域在 GameFi 项目中也是如此。如果将此作为防御手段并不能够很好的防范机器人操作,举例说明:
SuperGucci 采用合约闭源的方式进行发售,其参数较为简单所以被聪明的科学家朋友攻破是必然的事情,只是时间的早晚而已。最终由于被科学家拿走了太多筹码,而不得不将最后一轮调整为了抽签发售。从链上调用数据可以看出参数和地址无关,没有其他特别的参数,因此对于此类合约存在使用相同参数重放调用的可能。
个人并不推荐这种方式进行发售,对于没有知名 IP 的项目来说未开源的项目并不能够给用户足够的信心,同样这样一点也不 Crypto 。
在参数验证环节提到了参数提前暴露及私钥暴露两个问题,相应的解决方案为:
为防止参数提前暴露,API 端对参数返回条件进行控制:
私钥防护可以从两个方面入手:
如果完成了以上两个步骤是否就万无一失了呢?对于大部分热门项目来说是的,因为对于热门项目来说公开发售基本上几分钟就销售一空,夸张的可能在几个区块里就卖空,所以留给科学家进行破解的时间就不是特别充足。那么对于采用荷兰拍机制存在长时间的降价等待时,此方法也就失效了,因为科学家们有足够的时间对接 API 获取验证参数来调用合约,最终在期望的价位把库存一扫而空。
另外未了防止被同一用户获取过多可以针对同一 IP 限制签名数量,防止同一用户使用不同地址参与发售。
前端方面将从源码防护、模拟器及群控检测、Cloudflare 设置三个方面聊起。以下方案都是一些思路,具体执行上最终都是项目方和科学家们斗智斗勇,就看谁更技高一筹了,看各位大神表演。
完成了合约和后端的防护,那么对于前端来说想要提高业务的安全性大致可以通过加密、混淆、编译、打包等方式,这方面前端的小伙伴可能就比较熟悉了。有个思路如下 JavaScript obfuscator + bytenode + node-packer 。
同时逻辑部分的代码在发售前几分钟再执行部署,这样让科学家来不及从前端里面扒出更多信息。别忘记部署后刷新下CDN的缓存,防止用户受到旧的缓存影响。下图为 Cloudflare 中清楚缓存的页面:
另外需要注意的是针对合约地址和合约 ABI 可以使用一些加密手段进行隐藏或混淆,如拆分多端然后 base64,避免科学家从混淆后的 Js 中通过正则提取出来。
除了源码部分前端还应该对于群控或模拟器做些判断,如检查浏览器屏幕大小,判断 Selenium 这类自动化插件,一般来说这类插件都会在 js 全局变量里面插入一些函数和变量,有很多检测方案可以尝试,网上资源也比较多这里就不再赘述。
Cloudflare 中一些配置可以帮助我们提高对 Bot 的防护,当然破解方式还是有的,不存在什么万全之策,能做的只能是尽自己最大的努力进行防护。
需购买 Cloudflare Pro 版本,价格为 $20 / month 。
发售阶段开启 Under Attack Mode 模式,也就是常见的五秒盾用来验证请求合法性。
如需要提高防护等级,可以开启 legacy captcha,但是这样会影响一些用户体验,要和用户提前说明可能会遇到验证码而且有些验证码比较难识别。
开启路径为:Firewall Rules → Managed Challenge → Legacy CAPTCHA
想要实现真正公平的发售是个挺复杂的问题,项目方可能也没办法把大部分精力放在技术层面。在可以遇见的将来,会出现比较完善的发售平台使得艺术家或项目方只需关注作品层面,而不需要直面技术中的种种困难。
NFT 的发售其实只是 NFT 项目其中一部分,除了上述的注意事项外还有很多细节值得重视,比如版税的设置、合约及前后端的测试、NFT Metadata 的部署、属性的随机性和稀有性、开图的公平性、还有 NFT 质押机制的设计、NFT 衍生品的设计等,如果大家感兴趣可以在后续的文章中继续和大家深入讨论。
祝愿大家都能够 Mint 到自己喜欢的 NFT。