空投领取指南
在本文中,我们将研究假想的领取解决方案,尝试找出其性能问题并加以解决。 本文的重点是合约交互及其对整体性能的影响。 代码、安全方面和其他细微差别暂且不提。
领取机
几乎所有的领取解决方案都是如何运作的? 让我们想一想。
用户发送某种证明,证明他有资格领取 解决方案对其进行检查,并发送回 jetton 。 在当前情况下,"证明" 指的是merkle 证明,但也可以是签名数据或其他任何授权方法。 发送 jetton,因此会有一个 jetton 钱包和矿工。 我们还得确保这些鬼鬼祟祟的用户不能申请两次--双花消费保护合约。 哦,我们可能还想赚点钱,对吧? 所以至少要有一个申领钱包。 总结一下
Distributor
从用户处获取证明,对其进行检查,并释放 jetton。
状态初始化:(merkle_root、admin、fee_wallet_address)
。
双花
接收信息,如果已使用则跳转,否则继续传递信息
Jetton
Distributor 发送代币的 Jetton 钱包。 Jetton 矿工不在本文讨论范围之内。
收费钱包
任何类型的钱包合约
架构
V1
我首先想到的设计是这样的:
- 用户向 distributor 发送证明
- distributor 检查证明并部署
双花
合约 - distributor 将信息传递给双花。
- 如果之前没有部署,双倍花费会向分发者发送
claim_ok
文件 - distributor 将领取费用发送到费用钱包。
- distributor 向用户发放 jetton。
NAIVE ART AHEAD!
这有什么问题吗? 看来循环在这里是多余的。
V2
线性设计要好得多:
- 用户部署 "双重花费",并将证明代理给 distributor
- distributor 通过状态 init
(distributor_address, user_address?)
检查发送 "双倍花费 "的地址 - distributor 检查校样,在这种情况下,用户索引应是校样的一部分,然后放行。
- distributor 将费用发送至费用钱包 MORE NAIVE ART
分片优化
好了,我们有了一些进展,但分片优化呢?
这些是什么?
为了获得一些非常基本的理解,请参阅 为不同分片创建钱包 长话短说--分片是一个 4 位地址前缀。有点像网络。 当合约处于同一网段时,信息处理无需路由,因此速度更快。
确定我们可以控制的地址
distributor 地址
我们完全控制着分发者的数据,因此我们必须能够将其放到任何分片中。 怎么放? 请记住,合约地址是 由其状态定义 。 我们应该使用合约的某些数据字段作为 nonce,并不断尝试,直到得到想要的结果。 在真实合约中,一个好的 nonce 例子可以是钱包合约的(subwalletId/publicKey)。 任何可以在部署后修改或不影响合约逻辑(如 subwalletId)的字段都能满足要求。 我们甚至可以为此明确创建未使用的字段,就像 vanity-contract 所做的那样
distributor jetton 钱包
我们无法直接控制生成的 jetton 钱包地址。 但是,如果我们控制了 distributor 地址,我们就可以选择它,这样它所产生的 jetton 钱包就会在同一个分片中。 但如何做到这一点呢?有一个 lib可以解决这个问题! 它目前只支持钱包,但添加任意合约支持相对容易。 看看 HighloadV3 是怎么做的吧。
双花合约
双花合约应该是每个证明都独一无二的,所以我们很难对它进行分片调整? 让我们好好想想。 仔细想想,这取决于证明的结构。 我首先想到的是与 免铸造 jetton 相同的结构。
_ amount:Coins start_from:uint48 expired_at:uint48 = AirdropItem;
_ _(HashMap 267 AirdropItem) = Airdrop;
在这种情况下,当然是无法调整的,因为地址分布是随机的,而且所有数据字段都是有意义的。 但没有什么能阻止我们这样做:
_ amount:Coins start_from:uint48 expired_at:uint48 nonce:uint64 = AirdropItem;
_ _(HashMap 267 AirdropItem) = Airdrop;
甚至
_ amount:Coins start_from:uint48 expired_at:uint48 addr_hash: uint256 = AirdropItem;
_ _(HashMap 64 AirdropItem) = Airdrop;
其中,64 位索引可用作 nonce,而地址则成为数据有效载荷的一部分,用于验证。
因此,如果由 (distributor_address, index)
构建双花数据,其中索引是数据的一部分,我们仍然可以获得初始可靠性,但现在地址分片可以通过索引参数进行调整。
用户地址
显然,我们无法控制用户地址,不是吗? 是的,但我们可以将它们分组,使用户地址分片与分发者分片相匹配。 在这种情况下 ,每个分发者都将处理 merkle root ,它完全由来自其分片的用户组成。
总结
我们可以把链中的 double_spend->dist->dist_jetton
部分放在同一个分片中。
留给其他分片的就是dist_jetton->user_jetton->user_wallet
。
我们如何实际部署这种设置
让我们一步步来看看。 其中一个要求是 distributor 合约必须有可更新的 merkle root 文件。
- 使用初始
merkle_root
作为 nonce,在每个分片(0-15)内部署分发器,与其 jetton 钱包位于同一分片内 - 按地区分片对用户进行分组
- 为每个用户找到这样的索引,这样就可以 双花 合约
(distributor, index)
最终与用户地址在同一分片中。 - 用上面步骤中的索引生成 默克尔根
- 根据 默克尔根 更新_ distributor _
现在应该没问题了!
V3
- 用户使用索引调整功能在同一分片部署 双花 合约
- 用户分片中的分发器通过状态 init
(distributor_address, index)
检查发送 "双花 "的地址 - distributor 将费用发送至费用钱包
- Distributor 检查证明,在这种情况下,用户索引应是证明的一部分,并通过同一分片中的 jetton 钱包释放 jetton 。
MORE NAIVE ART 这有什么不对吗?让我们好好看看。 .... 太对了!只有一个收费钱包,而费用都排到了一个分片上。这可能是一场灾难!(想知道这是否真的发生过吗?)
V4
- 与 V3 相同,但现在有 16 个钱包,每个钱包与其 distributor 在同一个分片。
- 必须使 收费钱包 地址可更新
Bit more art
现在怎么样?LGTM。
下一步是什么?
我们总能更进一步。 来看看内置分片优化功能的自定义 jetton 钱包。 因此,用户的 jetton 钱包最终会以 87% 的概率与用户在同一个分片中。 但这还只是一个未知领域,所以你只能靠自己了。 祝你在 TGE 中好运!