EDIT June 28 2023: When I removed import '@openzeppelin/hardhat-upgrades';
from hardhat.config.ts, then whether I use ethers.getContractFactory
or ethers.deployContract
there is always runner.provider
exists .
// 1. deployContract use case
const myContract = await ethers.deployContract("MyContract", []); await MyContract.waitForDeployment();
console.log(`deployed MyContract has runner :${myContract.runner}`); // there is an object. Good!
// 2. getContractFactory use case
const MyContractFactory = await ethers.getContractFactory("MyContract");
const myContractDeployed = await MyContractFactory.deploy();
console.log(`deployed myContractDeployed has runner :${myContractDeployed.runner}`); // there is an object. Good!
So, now I am doubting @openzeppelin/hardhat-upgrades
. This might override something unexpected and remove runner
object. (But I need for upgradeable contract)
I am a bit confused where the issue comes from during my unit testing an event emit of a contract and it has been working until I change my code.
Working code:
// myContract.test.ts
...
const myContract: MyContract;
beforeEach(async() => {
myContract = await ethers.deployContract("MyContract", []);
await MyContract.waitForDeployment();
console.log(`deployed MyContract has runner :${myContract.runner}`); // there is an object. Good!
});
it('should emit an event', async() => {
await expect(MyContract.someFn())
.to.emit(MyContract, "MyEvent") // test passes
.withArgs(42);
});
...
Then, I updated my code in order to implement upgradeable contract by using @openzeppelin/contracts-upgradeable
. Then I can't test an event anymore.
Not working code:
const myContract: MyContract;
beforeEach(async() => {
const MyContractFactory = await ethers.getContractFactory("MyContract"); // changed a deployment method from 'deployContract()'
myContract = await upgrades.deployProxy(
MyContractFactory,
[],
{
initializer: "initialize",
kind: "transparent",
}
)
await myContract.deployed();
console.log(`deployed MyContract has runner :${myContract.runner}`); // undefined. Not good :/
});
it('should emit an event', async() => {
await expect(myContract.someFn())
.to.emit(myContract, "MyEvent") // test fails
.withArgs(42);
});
...
I see that deployContract()
returns an object that contains runner
while getContractFactory()
is not. But that's the only way I know deploying a upgradeable contract.
Because there is no runner
object, then when I run emit()
method during the testing, I end up here emit.ts:
if (contract.runner === null || contract.runner.provider === null) {
throw new errors_1.HardhatChaiMatchersAssertionError("contract.runner.provider shouldn't be null");
}
The fundamental question would be what's the difference between deployContract()
and getContractFactory()
? And how can I test an event while I use getContractFactory()
. 🤔
FYI, my package.json:
"devDependencies": {
"@nomicfoundation/hardhat-toolbox": "^3.0.0",
"@typechain/ethers-v6": "^0.4.0",
"@typechain/hardhat": "^8.0.0",
"hardhat": "^2.15.0",
"typechain": "^8.2.0"
},
"dependencies": {
"@openzeppelin/contracts": "^4.9.2",
"@openzeppelin/contracts-upgradeable": "^4.9.2"
}
hardhat.config.ts:
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import '@openzeppelin/hardhat-upgrades';
const config: HardhatUserConfig = {
solidity: "0.8.19",
};
export default config;
Thanks in advance.
.to.emit(myContract, "MyEvent")
should be.to.emit(myContract.connect(await ethers.provider.getSigner()), "MyEvent")
. I don't think that's the issue you are having, but just in case someone stumble upon the same error, that was my case.