1

I am trying to test TimelockController contract from Openzeppelin using their test.

The timelockController imports Address.sol, which has a custom Error called FailedCall. Now for my case, I have created my own TimelockController just by inheriting this OZ TimelockController.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts-upgradeable/governance/TimelockControllerUpgradeable.sol";

contract PushTimelockController is TimelockControllerUpgradeable {

    function initialize(
        uint256 minDelay,
        address[] memory proposers,
        address[] memory executors,
        address admin
    ) public initializer {
        __TimelockController_init(minDelay, proposers, executors, admin);
    }
}

The problem now is while expecting some functions to revert with custom error FailedCall using this syntax:

await expect(
            this.mock
              .connect(this.executor)
              .executeBatch(
                operation.targets,
                operation.values,
                operation.payloads,
                operation.predecessor,
                operation.salt,
              ),
          ).to.be.revertedWithCustomError(this.mock, 'FailedCall');
        })

where this.mock is the PushTimelockController, hardhat is unable to locate the custom error, throwing this error:

Error: The given contract doesn't have a custom error named 'FailedCall'

This might be because my PushTimelockController is inheriting TimelockController which is using Address.sol, and hardhat is unable to read the custom errors defined in Address.sol.

What can be a workaround? Do I need to get a contract instance of Address.sol just for the errors or is there any other way?

1 Answer 1

1
+50

The message is thrown at chai-matchers validation and it means, that the expected error is not found in the contracts interface:

https://github.com/NomicFoundation/hardhat/blob/b267db8d1f2acb6f5611e82b74b9d9e555024c84/packages/hardhat-chai-matchers/src/internal/reverted/revertedWithCustomError.ts#L178

You could try to extend the contracts interface manually:

contract.interface.errors.push(ethers.utils.ErrorFragment.from('FailedCall()');

Additionally, the TimelockControllerUpgradeable.sol doesn't inherit the Address contract, the Address is the library.

1
  • Thanks mate, I tried to manually push the error but the interface array is non-extensible by default hence getting this error : TypeError: Cannot add property 49, object is not extensible . Let me know if you are aware of any workaround. Additionally, this is the working code for me (for ethers V6): contract.interface.fragments.push(ethers.ErrorFragment.from('FailedCall()')); Apr 18 at 17:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.