βœ…05 - RevertWithSelectorPlusArgs

Goal: Revert with a custom error that includes argument data.

// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13;

import {Test, console} from "forge-std/Test.sol";
import {RevertWithSelectorPlusArgs} from "../src/RevertWithSelectorPlusArgs.sol";

contract RevertWithSelectorPlusArgsTest is Test {
    RevertWithSelectorPlusArgs public c;

    function setUp() public {
        c = new RevertWithSelectorPlusArgs();
    }

    function test_RevertWithSelectorPlusArgs(uint256 x) public {
        vm.expectRevert(abi.encodeWithSelector(RevertWithSelectorPlusArgs.RevertData.selector, x));
        c.main(x);
    }
}

The idea is the same as previous level and this one is easier: the argument x is a static type instead of dynamic type, so we don’t worry about ABI encoding (offset + length + data format).

// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13;

contract RevertWithSelectorPlusArgs {
    error RevertData(uint256); // selector: 0xae412287

    function main(uint256 x) external pure {
        assembly {
            // Allocate a free memory pointer
            let ptr := mload(0x40)
            // Write the error selector in the first 4 bytes.
            // Use shl(224, 0xae412287) to shift the 4-byte selector into the high order position.
            mstore(ptr, shl(224, 0xae412287))
            // Write the argument "x" immediately after the selector.
            mstore(add(ptr, 4), x)
            // Revert with the custom error message consisting of 36 bytes: 4 for the selector and 32 for "x".
            revert(ptr, 36)
        }
    }
}

Explanation:

  • We obtain a pointer to free memory via mload(0x40).

  • The error selector for RevertData(uint256) is 0xae412287. We shift it left by 224 bits (32 bytes - 4 bytes = 28 bytes; 28 * 8 = 224 bits) so that it occupies the high-order 4 bytes of a 32-byte word.

  • The parameter x is stored immediately after the selector, at memory location ptr + 4.

  • Finally, we call revert(ptr, 36) to trigger a revert with 36 bytes of error data (4 bytes of selector + 32 bytes for the encoded uint256).

Run test:

forge test --mp test/RevertWithSelectorPlusArgs.t.sol -vvvv

Last updated