我有一个合约使用 try-catch 调用另一个合约,该合约会返回自定义错误。我正在使用 abi.decode 解码错误原因字节,但当我尝试解码时它正在恢复。
这是在 remix 中使用 Solidity 0.8.4。
代码是:
pragma solidity = 0.8.4;
error MyCustomError(uint256 value1, uint256 value2);
contract Reverter {
function revertMe(uint256 valueA, uint256 valueB) public {
revert MyCustomError(valueA, valueB);
}
}
contract RevertCaller {
Reverter reverter;
event Revert(uint256 indexed value1, uint256 indexed value2);
event LogBytes(bytes);
constructor() {
reverter = new Reverter();
}
function callRevert(uint256 valueA, uint256 valueB) public {
try reverter.revertMe(valueA, valueB) {
} catch (bytes memory reason) {
emit Revert(valueA, valueB);
emit LogBytes(reason);
(bytes4 errorSelector, uint256 value1, uint256 value2) = abi.decode(reason, (bytes4, uint256, uint256));
if (errorSelector == MyCustomError.selector) {
// Handle invalid amount error
emit Revert(value1, value2);
} else {
emit Revert(0, 0);
}
}
}
}
如果我注释掉 abi.decode 行并将一些其他值传递到事件中,它不会恢复。它也只能像这样解码选择器:
(bytes4 errorSelector) = abi.decode(reason, (bytes4));
但是我没有得到我想要使用的错误参数。
记录的原因(使用 LogBytes)是
0x64f2b82800000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005
也许我错误地使用了abi.decode?
您确实错误地使用了
abi.decode
。
错误的前 4 个字节的处理方式与函数选择器相同,并且它们的编码不遵循 ABI 规范。根据 ABI 规范,
bytes4
值应该用零填充,最多可达 32 个字节。这就是 abi.decode(reason, (bytes4))
起作用的原因,因为后面有零。然而,当您尝试解码完整字节时,解码器的数据中没有足够的填充,它只会恢复事务。
不幸的是,仍然没有好的方法可以在 Solidity 中进行自定义错误捕获。
以下汇编代码将实现所需的错误匹配行为:
callRevert(uint256 valueA, uint256 valueB) public {
try reverter.revertMe(valueA, valueB) {
} catch (bytes memory reason) {
emit Revert(valueA, valueB);
emit LogBytes(reason);
bytes4 errorSelector = bytes4(reason);
uint256 value1;
uint256 value2;
assembly ("memory-safe") {
value1 := mload(add(reason, 0x24))
value2 := mload(add(reason, 0x44))
}
if (errorSelector == MyCustomError.selector) {
// Handle invalid amount error
emit Revert(value1, value2);
} else {
emit Revert(0, 0);
}
}
}
要使
bytes4 errorSelector = bytes4(reason)
投射正常工作,您需要更新到 Solidity 0.8.5。