Solidity – How to Bubble Up a Custom Error When Using DELEGATECALL in Solidity

contract-invocationcustom-errorsrevert-opcodesoliditysolidity-0.8.x

Solidity v0.8.4 introduced custom errors:

error Unauthorized();

Say I have contract A which delegate calls to contract B. The latter reverts with the custom error defined above. How can I bubble that error up in contract A?

Best Answer

Check out this function I modified from the openzepplin's Address.sol.

function _delegatecall(address target, bytes memory data) internal returns (bytes memory) {
    (bool success, bytes memory returndata) = target.delegatecall(data);
    if (!success) {
        if (returndata.length == 0) revert();
        assembly {
            revert(add(32, returndata), mload(returndata))
        }
    }
    return returndata;
}

returndata is a dynamic-sized byte array. If the delegate call fails, returndata would be the error object raised by the failed call. Say I have contract A which delegate calls to contract B. The latter reverts with the custom error GonnaMakeIt(uint256 data). Then, the returndata would be:

0xbf42f2660000000000000000000000000000000000000000000000000000000000000539
  +------+---------------------------------------------------------------+
  selector                       uint256 data

Then, we make use of Yul's revert(p, s) to revert with the custom error in contract A. p refers to the pointer of where the error byte array starts, s refers to the how long the byte array is.

Since returndata is a dynamic-sized byte array, the first 32 bit of the pointer stores the size of it, which we mload(returndata) to retrieve it. And similarly, add(32, returndata) would be the pointer to where the byte array starts. Altogther, it forms the line revert(add(32, returndata), mload(returndata)), which reverts the error you get from contract B.