When I deploy an Ownable contract via hardhat (hardhat-deploy in particular) to Optimistic Kovan, it doesn't seem to set the owner correctly, whereas when I deploy the same set of contracts via Remix, it does set the owner correctly. I have a github link to the repro here: https://github.com/zawazawa01/ownable-repro.

What's the correct way to deploy these contracts?

My script is the following:

module.exports = async ({getNamedAccounts, getUnnamedAccounts, deployments}) => {
    const {deploy} = deployments;
    const {deployer} = await getNamedAccounts();
    const accounts = await getUnnamedAccounts();
    await deploy('TestOwnable', {
      from: accounts[0],
      args: [],
      log: true,
  module.exports.tags = ['TestOwnable'];

and the relevant network config is

    'kovan-ovm': {
      url: 'https://kovan.optimism.io',
      ovm: true,
      accounts: [process.env.PRIVATE_KEY],

The contract is barebones:

//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

// Inherited
import "@openzeppelin/contracts/access/Ownable.sol";

contract TestOwnable is Ownable {
    constructor() Ownable() {}

I deploy this with command yarn deploy:ovm, which runs hardhat deploy --network kovan-ovm, and the transaction succeeds, but according to the logs we can see that ownership is not transferred to the deploying account: https://kovan-optimistic.etherscan.io/tx/0x9740d01219838ea7a7c8155dc30df9fb12a69105014386a14df4aedd9e401aa9#eventlog.

On the other hand, deploying with remix, the transfer of ownership does succeed: https://kovan-optimistic.etherscan.io/tx/0x2b92f8ae26994055d19b5f90fe975dc605177da38865e247cd24f980659315d0#eventlog.

What am I missing here?


2 Answers 2


Try to change your contract as follows

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

import "@openzeppelin/contracts/access/Ownable.sol";

contract TestOwnable is Ownable {
    constructor() Ownable(msg.sender) {}

The alternative is to add the owner as an argument in the constructor.

contract TestOwnable is Ownable {
    constructor(address initialOwner) Ownable(initialOwner) {}

Deployment should look something like this:

await deploy('TestOwnable', {
  from: accounts[0],
  args: [owner.address],
  log: true,

Look at Hardhat's Deploying your contracts, they have an example.

It should be something along these lines -

  const TestOwnable = await hre.ethers.getContractFactory("TestOwnable");
  const testOwnable = await TestOwnable.deploy();
  await testOwnable.deployed();

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.