Hardhat – Property ‘PROPOSER_ROLE’ Does Not Exist on Type ‘BaseContract’: How to Solve

hardhatopenzeppelin-contracts

TimeLock contract extends from TimelockController from OpenZeppelin Contracts.

contract TimeLock is TimelockController {
 
  constructor(
    uint256 minDelay,
    address[] memory proposers,
    address[] memory executors,
    address admin
  ) TimelockController(minDelay, proposers, executors, admin) {}
}

TimelockControllers Openzeppelin has Roles

bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE");
bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");

I am using hardhat to deploy contracts which deploys TimeLock contract successfully before it runs setupGovernance contract to assign roles but it gives error at timeLock.PROPOSER_ROLE();

Property 'PROPOSER_ROLE' does not exist on type 'BaseContract'

Below is the setupGovernance hardhat script

const setupGovernance: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
    
    const { getNamedAccounts, deployments } = hre;
    const { log } = deployments;
    const { deployer } = await getNamedAccounts();
    
    const timeLock = await ethers.getContract("TimeLock", deployer);
    const governance = await ethers.getContract("GovernorContract", deployer);

    log("----------------------------------------------------")
    log("Setting up roles...");
    const proposerRole = await timeLock.PROPOSER_ROLE();
    const executorRole = await timeLock.EXECUTOR_ROLE();
    const adminRole = await timeLock.TIMELOCK_ADMIN_ROLE();

    const proposerTx = await timeLock.grantRole(proposerRole, governance.address);
    await proposerTx.wait(1); // wait for 1 block confirmation
    const executorTx = await timeLock.grantRole(executorRole, ADDRESS_ZERO);
    await executorTx.wait(1); // wait for 1 block confirmation
    const revokeTx = await timeLock.revokeRole(adminRole, deployer);
    await revokeTx.wait(1); 
}

Best Answer

Code migration from ethersv5 to ethersv6 made the following changes to the code:

const proposerRole = ethers.id("PROPOSER_ROLE");
const executorRole = ethers.id("EXECUTOR_ROLE");
const adminRole = ethers.id("TIMELOCK_ADMIN_ROLE")

to get contract method calls

const proposerTx = await timeLock.getFunction("grantRole")(proposerRole, await governance.getAddress());
await proposerTx.wait(1); // wait for 1 block confirmation
const executorTx = await timeLock.getFunction("grantRole")(executorRole, ethers.ZeroAddress);
await executorTx.wait(1); // wait for 1 block confirmation */
const revokeTx = await timeLock.getFunction("revokeRole")(adminRole, deployer);
await revokeTx.wait(1); // wait for 1 block confirmation */