GasToken:我为何不再担心 gas 价格飙升(下)

(续前)GST2
free*函数调用下列destroyChildren实现:
function destroyChildren(uint256 value) internal { uint256 tail = s_tail; // tail points to slot behind the last contract in the queue for (uint256 i = tail + 1; i <= tail + value; i++) { mk_contract_address(this, i).call; } s_tail = tail + value;}此处 , 我们遍历 child 合约 , 并调用这些合约中的回调函数 。 正如 GST2 文档所指出的那样 , 发行代币时 , 合约必须找到 child 合约的创建地址(存储这些地址的成本会很高 , 因此我们会即时计算这些地址) 。 幸运的是 , 这是有可能做到的 , 因为使用 CREATE生成的合约地址是根据地址/账户已创建的合约数量(nonce)计算得出的 , 具有确定性 。 这些合约地址都是在mk_contract_address函数中计算得出的 , 在调用时无需任何参数或值 , 调用回调函数 , 然后就像在对应mint函数中硬编码的那样 , gas 退款会发送至 parent 合约 。
CHI GasToken历时 3 年 , CHI GasToken 终于上线 。 CHI 由去中心化交易所聚合器 1inch.exchange 开发 , 与传统的 GasToken 类似 , 但是铸币效率比后者高出 1% , 释放代币的效率比后者高出 10% , 而且采用新的 CREATE2操作码 。 该操作码可以提前通过确定性方式来创建链上合约地址 , 主要用于反事实的 Layer-2 解决方案 。
CREATE2操作码采用 4 个堆栈参数:endowment、memory_start、memory_length 和盐值 。 生成地址等于keccak256( 0xff ++ address ++ salt ++ keccak256(init_code))[12:] , 而非常见的将发送方地址和 nonce 进行哈希计算 。 由于盐值控制在用户手中 , 用户可以提前知道地址 。
function mint(uint256 value) public { uint256 offset = totalMinted; assembly { mstore(0, 0x746d4946c0e9F43F4Dee607b0eF1fA1c3318585733ff6000526015600bf30000) for {let i := div(value, )} i {i := 32 sub((i, 1)} { pop(create2(0, 0, 30, add(offset, 0))) pop(create2(0, 0, 30, add(offset, 1))) pop(create2(0, 0, 30, add(offset, 2))) pop(create2(0, 0, 30, add(offset, 3))) pop(create2(0, 0, 30, add(offset, 4))) pop(create2(0, 0, 30, add(offset, 5))) pop(create2(0, 0, 30, add(offset, 6))) pop(create2(0, 0, 30, add(offset, 7))) pop(create2(0, 0, 30, add(offset, 8))) pop(create2(0, 0, 30, add(offset, 9))) pop(create2(0, 0, 30, add(offset, 10))) pop(create2(0, 0, 30, add(offset, 11))) pop(create2(0, 0, 30, add(offset, 12))) pop(create2(0, 0, 30, add(offset, 13))) pop(create2(0, 0, 30, add(offset, 14))) pop(create2(0, 0, 30, add(offset, 15))) pop(create2(0, 0, 30, add(offset, 16))) pop(create2(0, 0, 30, add(offset, 17))) pop(create2(0, 0, 30, add(offset, 18))) pop(create2(0, 0, 30, add(offset, 19))) pop(create2(0, 0, 30, add(offset, 20))) pop(create2(0, 0, 30, add(offset, 21))) pop(create2(0, 0, 30, add(offset, 22))) pop(create2(0, 0, 30, add(offset, 23))) pop(create2(0, 0, 30, add(offset, 24))) pop(create2(0, 0, 30, add(offset, 25))) pop(create2(0, 0, 30, add(offset, 26))) pop(create2(0, 0, 30, add(offset, 27))) pop(create2(0, 0, 30, add(offset, 28))) pop(create2(0, 0, 30, add(offset, 29))) pop(create2(0, 0, 30, add(offset, 30))) pop(create2(0, 0, 30, add(offset, 31))) offset := add(offset, 32) } for {let i := and(value, 0x1F)} i {i := sub(i, 1)} { pop(create2(0, 0, 30, offset)) offset := add(offset, 1) } } _mint(msg.sender, value); totalMinted = offset;}这里的一般流程是 , 将固定的 child 合约字节码存储到 memory 中(第 4 行代码) , 然后使用 for 循环反复调用 CREATE2 , 直到计算出对应的值为止 。 CREATE2返回已部署 child 合约的地址 , 我们不关心这个地址 , 因此我们只是将这个地址从堆栈中弹出 。 偏移量计数器被用来计算 child 合约的数量 , 并将其永久存储在第 33 行代码中 。
对应的 free*函数调用_destoryChildren
function _destroyChildren(uint256 value) internal { assembly { let i := sload(totalBurned_slot) let end := add(i, value) sstore(totalBurned_slot, end) let data := mload(0x40) mstore(data, 0xff0000000000004946c0e9F43F4Dee607b0eF1fA1c0000000000000000000000) mstore(add(data, 53), 0x3c1644c68e5d6cb380c36d1bf847fdbc0c7ac28030025a2fc5e63cce23c16348) let ptr := add(data, 21)