求助:在Solidity YUL内联汇编中实现自定义错误回退的问题
Solidity YUL内联汇编自定义错误回退问题
问题代码
function mint(uint256 amountToMint) external onlyOwner() { assembly { // check if exceed maxSupply let maxSupplySlot := sload(maxSupply.slot) let res := lt(add(amountToMint, mload(counter.slot)), add(maxSupplySlot, 1)) let ptrError := mload(0x40) mstore(ptrError, 14) mstore(add(ptrError, 0x20), 0x4d6178537570706c795265616368) // => "MaxSupplyReach" if iszero(res) { revert(ptrError, 0x40) } // populate mapping balanceOf let ptr := mload(0x40) mstore(ptr, caller()) mstore(add(ptr, 0x20), balanceOf.slot) let slot := keccak256(ptr, 0x40) sstore(slot, amountToMint) // increment counter let counterSlot := sload(counter.slot) let resCounter := add(counterSlot, amountToMint) sstore(counter.slot, resCounter) } }
当前问题
- 触发自定义错误时返回数据为:
0x000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000004d6178537570706c795265616368 - 使用
revert(0,0)测试正常,但自定义错误无法被Foundry识别,报错显示自定义错误不匹配。
错误分析
- 存储变量读取错误:读取
counter存储变量时用了mload(counter.slot),mload是内存读取操作,存储变量必须用sload读取,这会导致计数器值读取错误,逻辑判断失效。 - 自定义错误格式错误:Solidity自定义错误的回退数据格式是4字节错误选择器开头,而非你使用的「字符串长度+字符串」格式。当前构造方式是普通字符串回退的格式,无法被识别为自定义错误。
- revert长度参数错误:你传递的
0x40(64字节)远大于自定义错误所需的4字节长度。
修正后的代码
// 先在合约层面定义自定义错误 error MaxSupplyReach(); function mint(uint256 amountToMint) external onlyOwner() { assembly { // 正确读取存储变量counter的值 let currentCounter := sload(counter.slot) let maxSupplySlot := sload(maxSupply.slot) let res := lt(add(amountToMint, currentCounter), add(maxSupplySlot, 1)) if iszero(res) { // 获取空闲内存指针 let ptrError := mload(0x40) // 写入自定义错误选择器:bytes4(keccak256("MaxSupplyReach()")) mstore(ptrError, keccak256("MaxSupplyReach()")) // 回退:传递4字节的错误选择器 revert(ptrError, 0x04) } // 写入balanceOf映射 let ptr := mload(0x40) mstore(ptr, caller()) mstore(add(ptr, 0x20), balanceOf.slot) let slot := keccak256(ptr, 0x40) sstore(slot, amountToMint) // 更新计数器 let resCounter := add(currentCounter, amountToMint) sstore(counter.slot, resCounter) } }
补充说明
- 错误选择器可以提前计算好硬编码,比如
bytes4(keccak256("MaxSupplyReach()"))的结果可通过Solidity编译得到,直接替换keccak256("MaxSupplyReach()")能节省gas。 - Foundry测试时,用
expectRevert(MaxSupplyReach.selector)即可捕获该自定义错误。
内容的提问来源于stack exchange,提问作者chixx.eth
相关产品推荐
相关产品推荐

