You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:在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识别,报错显示自定义错误不匹配。

错误分析

  1. 存储变量读取错误:读取counter存储变量时用了mload(counter.slot),mload是内存读取操作,存储变量必须用sload读取,这会导致计数器值读取错误,逻辑判断失效。
  2. 自定义错误格式错误:Solidity自定义错误的回退数据格式是4字节错误选择器开头,而非你使用的「字符串长度+字符串」格式。当前构造方式是普通字符串回退的格式,无法被识别为自定义错误。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 17:18:11