Upgrades + hardhat-truffle5

Hi everyone :wave: Hope you're having a great day.

TL;DR: is it possible to use OpenZeppelin Upgrades plugin with @nomiclabs/hardhat-truffle5 and not make changes in tests?

I'm working with a fork of some protocol. Protocol team used Truffle in the beginning, wrote a lot of tests and then decided to move to Hardhat. To not rewrite bunch of tests, they used @nomiclabs/hardhat-truffle5 library.

But now I want to make their smart contracts UUPS upgradeable. And for this I need to use OpenZeppelin Upgrades library.

First, I tried to use @openzeppelin/truffle-upgrades because all contract deployings were written with Truffle. But I encountered an error:

TypeError: Cannot use 'in' operator to search for 'sendAsync' in undefined
      at wrapProvider (.../node_modules/@openzeppelin/truffle-upgrades/src/utils/wrap-provider.ts:11:33)
      at deployProxy (.../node_modules/@openzeppelin/truffle-upgrades/src/deploy-proxy.ts:39:32)

Code looks like this

const Contract1 = artifacts.require("./Contract1.sol")
const Contract2 = artifacts.require("./Contract2.sol")
const { deployProxy } = require('@openzeppelin/truffle-upgrades');
...
const contract1 = await Contract1.new()
Contract1.setAsDeployed(contract1)
// lines above were in the code so I tried to replicate it for my test subject contract
const contract2 = await deployProxy(Contract2, {kind: "uups"} ) // fails here
Contract2.setAsDeployed(contract2)

After some research, I saw someone with an advice to launch those tests not from hardhat env but from truffle - npx truffle test ...
I couldn't make this work, and it would be not very smart to return back to Truffle.

Then I tried @openzeppelin/hardhat-upgrades.

const Contract1 = artifacts.require("./Contract1.sol")
require('@openzeppelin/hardhat-upgrades');
...
const contract1 = await Contract1.new()
Contract1.setAsDeployed(contract1)
// lines above were in the code so I tried to replicate it for my test subject contract
const Contract2 = await ethers.getContractFactory("Contract2")
const contract2 = await upgrades.deployProxy(Contract2, [], { kind: "uups" })

And it would work just fine. But then when it comes to the tests, we have this for example:

// bob is just a string with some public address
...
describe('a', async accounts => { 
  it("b", async () => {
    try {
      const tx1 = await contract2.someFunction(bob, bob, bob, { from: bob })
    } catch (err) {
       assert.include(err.message, "revert")
    }
  })
})

This wouldn't work because hardhat's contract instances don't understand { from: bob }. We need to make a signer from this and connect to a contract instance.

const aliceSigner = await hre.ethers.getImpersonatedSigner(alice)
...
try {
  const txAlice = await contract2.connect(aliceSigner).someFunction(bob, '30000000000000000000', bob, alice)
} catch (err) {
  assert.include(err.message, "revert")
}

So my question is, is it possible to use OpenZeppelin Upgrades plugin with @nomiclabs/hardhat-truffle5 and not make changes in tests?
Maybe someone already encountered this and found a solution? Thanks!

The Truffle upgrades plugin does not support @nomiclabs/hardhat-truffle5.
For Hardhat / ethers.js, the contract instances are different than Truffle as you mentioned, so it looks like connect is needed in this case.

Thank you so much for your reply!

That's what I've come up with too.

Perhaps another approach that you can try is to get the address from the ethers.js contract instance, then create a Truffle contract instance out of that.

For example, maybe something like this:

const Contract2Ethers = await ethers.getContractFactory("Contract2");
const contract2ethers = await upgrades.deployProxy(Contract2Ethers, [], { kind: "uups" });

const Contract2Truffle = artifacts.require("./Contract2.sol");
const contract2truffle = await Contract2Truffle.at(contract2ethers.address);
...
const tx1 = await contract2truffle.someFunction(bob, bob, bob, { from: bob });

You're saviour, thanks :pray:

1 Like