
Uniswap V3交易预计算技巧
Uniswap上做swap交易时,比如用usdt购买btc,会根据界面上输入的usdt数量,实时计算出可以swap到多少个btc,v2版本因为是应用了xy=k的公式,可以方便的计算出来。代码里通过getAmountOut和getAmountIn得到,这两个都是view函数,不需要消耗gas。而到了...
Meebits mint随机算法
先上代码function randomIndex() internal returns (uint) { uint totalSize = TOKEN_LIMIT - numTokens; uint index = uint(keccak256(abi.encodePacked(nonce, ms...

nftx闪电贷领取apecoin空投
已经是两个多月前的事件了,这几天才对这个tx进行了分析和fork重现,记录下来加深一下理解。apecoin的空投并没有限制调用方不能是合约地址,也不是用链下签名再到合约里验签的方式来领取空投,而是直接在合约里校验调用方的地址里有没有bayc/mayc,再加上apecoin当时的价格在8u左右,总体...
<100 subscribers

Uniswap V3交易预计算技巧
Uniswap上做swap交易时,比如用usdt购买btc,会根据界面上输入的usdt数量,实时计算出可以swap到多少个btc,v2版本因为是应用了xy=k的公式,可以方便的计算出来。代码里通过getAmountOut和getAmountIn得到,这两个都是view函数,不需要消耗gas。而到了...
Meebits mint随机算法
先上代码function randomIndex() internal returns (uint) { uint totalSize = TOKEN_LIMIT - numTokens; uint index = uint(keccak256(abi.encodePacked(nonce, ms...

nftx闪电贷领取apecoin空投
已经是两个多月前的事件了,这几天才对这个tx进行了分析和fork重现,记录下来加深一下理解。apecoin的空投并没有限制调用方不能是合约地址,也不是用链下签名再到合约里验签的方式来领取空投,而是直接在合约里校验调用方的地址里有没有bayc/mayc,再加上apecoin当时的价格在8u左右,总体...
Share Dialog
Share Dialog
在ENSToken的合约里看到了Bitmaps的应用,在地址认领空投时用了Merkle树证明来check用户地址和认领数量,进而会对应一个Merkle的index,为了防止重复认领空投,合约里用了OpenZeppelin的Bitmaps库来做位图存储,地址认领成功后,就将对应的index在位图里存true,下次如果再来认领就会判断这个位图,如果为true时就返回错误,以此来防止重复认领空投。
BitMaps.BitMap private claimed; /** * @dev Claims airdropped tokens. * @param amount The amount of the claim being made. * @param delegate The address the tokenholder wants to delegate their votes to. * @param merkleProof A merkle proof proving the claim is valid. */ function claimTokens(uint256 amount, address delegate, bytes32[] calldata merkleProof) external { bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount)); (bool valid, uint256 index) = MerkleProof.verify(merkleProof, merkleRoot, leaf); require(valid, "ENS: Valid proof required."); require(!isClaimed(index), "ENS: Tokens already claimed.");//认领过就revert claimed.set(index);//设置位图,表示当前index认领过空投 emit Claim(msg.sender, amount); _delegate(msg.sender, delegate); _transfer(address(this), msg.sender, amount); }
普通的做法可以用mapping(uint256=>bool) hasClaimed来实现此功能,认领过就hasClaimed[index]=true,这样做每个index都要进行一下写storage操作,消耗较多的gas。可以采用位图来优化存储,因为uint256有256位,每位对应一个bool值,最多可以存储256个index,显然不够,因此可以将index拆分成n个256,存储到多个槽位,就变成mapping(uint256=>uint256) _data结构来存储位图。
struct BitMap { mapping(uint256 => uint256) _data; } function set(BitMap storage bitmap, uint256 index) internal { uint256 bucket = index >> 8;//右移8位,相当于除以256取整 uint256 mask = 1 << (index & 0xff); bitmap._data[bucket] |= mask; }
上面代码的index&0xff相当于index对256取模,计算出余数,然后用1左移对应的余数位,表示要在256位中的哪一位写上1,就表示true,然后对_data里对应槽位的数据做或运算,表示更新存储。除了第一次的bucket存储是写操作要消耗20000gas外,后面相同的bucket存储就是修改操作,消耗5000gas,显然比上面的那种方法节省gas。
例如:index=1000
index>>8=3,bucket=3
index&&0xff=232
1<<232,表示如下二进制整数

再对bitmap._data[bucket]进行或运算,相当于将bitmap._data[bucket]值的第232位设置为1。
对应的get则相反,将bitmap._data[bucket]与mask做与运算,如果不为0,就表示这个位是1。
function get(BitMap storage bitmap, uint256 index) internal view returns (bool) { uint256 bucket = index >> 8; uint256 mask = 1 << (index & 0xff); return bitmap._data[bucket] & mask != 0; }
位图结构对存储uint256=>bool的映射时可以节省gas cost,如果是address,可以uint256(uint160(addr))转为uint256后使用此结构。例如,记录某地址是否做过某事时就可以用Bitmaps。
在ENSToken的合约里看到了Bitmaps的应用,在地址认领空投时用了Merkle树证明来check用户地址和认领数量,进而会对应一个Merkle的index,为了防止重复认领空投,合约里用了OpenZeppelin的Bitmaps库来做位图存储,地址认领成功后,就将对应的index在位图里存true,下次如果再来认领就会判断这个位图,如果为true时就返回错误,以此来防止重复认领空投。
BitMaps.BitMap private claimed; /** * @dev Claims airdropped tokens. * @param amount The amount of the claim being made. * @param delegate The address the tokenholder wants to delegate their votes to. * @param merkleProof A merkle proof proving the claim is valid. */ function claimTokens(uint256 amount, address delegate, bytes32[] calldata merkleProof) external { bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount)); (bool valid, uint256 index) = MerkleProof.verify(merkleProof, merkleRoot, leaf); require(valid, "ENS: Valid proof required."); require(!isClaimed(index), "ENS: Tokens already claimed.");//认领过就revert claimed.set(index);//设置位图,表示当前index认领过空投 emit Claim(msg.sender, amount); _delegate(msg.sender, delegate); _transfer(address(this), msg.sender, amount); }
普通的做法可以用mapping(uint256=>bool) hasClaimed来实现此功能,认领过就hasClaimed[index]=true,这样做每个index都要进行一下写storage操作,消耗较多的gas。可以采用位图来优化存储,因为uint256有256位,每位对应一个bool值,最多可以存储256个index,显然不够,因此可以将index拆分成n个256,存储到多个槽位,就变成mapping(uint256=>uint256) _data结构来存储位图。
struct BitMap { mapping(uint256 => uint256) _data; } function set(BitMap storage bitmap, uint256 index) internal { uint256 bucket = index >> 8;//右移8位,相当于除以256取整 uint256 mask = 1 << (index & 0xff); bitmap._data[bucket] |= mask; }
上面代码的index&0xff相当于index对256取模,计算出余数,然后用1左移对应的余数位,表示要在256位中的哪一位写上1,就表示true,然后对_data里对应槽位的数据做或运算,表示更新存储。除了第一次的bucket存储是写操作要消耗20000gas外,后面相同的bucket存储就是修改操作,消耗5000gas,显然比上面的那种方法节省gas。
例如:index=1000
index>>8=3,bucket=3
index&&0xff=232
1<<232,表示如下二进制整数

再对bitmap._data[bucket]进行或运算,相当于将bitmap._data[bucket]值的第232位设置为1。
对应的get则相反,将bitmap._data[bucket]与mask做与运算,如果不为0,就表示这个位是1。
function get(BitMap storage bitmap, uint256 index) internal view returns (bool) { uint256 bucket = index >> 8; uint256 mask = 1 << (index & 0xff); return bitmap._data[bucket] & mask != 0; }
位图结构对存储uint256=>bool的映射时可以节省gas cost,如果是address,可以uint256(uint160(addr))转为uint256后使用此结构。例如,记录某地址是否做过某事时就可以用Bitmaps。
No comments yet