Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Ownable tests #4657

Merged
merged 31 commits into from
Oct 17, 2023
Merged

Migrate Ownable tests #4657

merged 31 commits into from
Oct 17, 2023

Conversation

Amxx
Copy link
Collaborator

@Amxx Amxx commented Oct 5, 2023

This is the first PR of a long series that will migrate from truffle + web3js to hardhat-toolbox + ethers v6.

The goal of this PR is to

  • setup the necessary packages
  • migrate a simple test file
    • decide what format/syntax the new tests should use (how to deal with signers? use of fixture?)
  • identify the helpers needed
  • list the deprecated packages that should be removed once the transition is over

Obviously, this being a small test file, not all issues will appear from the start. This is intended. Here we resolve the most obvious things. As we progress in the migration, we will identify and resolve more subtle challenges. Homefully, we don't have to reconsider on past decision when we get there.

Guidelines

  • replace all numbers and BigNumber with bigint.

Deprecations:

  • @nomiclabs/hardhat-truffle5
  • @nomiclabs/hardhat-web3
  • @openzeppelin/test-helpers
  • web3
  • ./helpers/customError

Possible deprecations (to evaluate in upcomming PR:

  • merkletreejs@openzeppelin/merkle-tree
  • keccak256ethers?
  • eth-sig-util → ethers?
  • ethereumjs-utilethers?
  • ethereumjs-walletethers?

Question

  • what happens to ./hardhat/env-contracts.js
    • body(accounts.slice(1)) has no effect over on ethers.getSigners()
    • do we need snapshots if we use fixtures ?

PR Checklist

  • Tests
  • Changeset entry (run npx changeset add)

@changeset-bot
Copy link

changeset-bot bot commented Oct 5, 2023

⚠️ No Changeset found

Latest commit: 69b91a0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

hardhat.config.js Outdated Show resolved Hide resolved
@Amxx Amxx added the tests Test suite and helpers. label Oct 5, 2023
@Amxx Amxx modified the milestone: 5.1 Oct 5, 2023
@socket-security
Copy link

socket-security bot commented Oct 5, 2023

New dependencies detected. Learn more about Socket for GitHub ↗︎

Packages Version New capabilities Transitives Size Publisher
@nomicfoundation/hardhat-toolbox 3.0.0 environment +58 62 MB fvictorio
ethers 6.7.1 network, filesystem +6 17.1 MB ricmoo
@nomicfoundation/hardhat-chai-matchers 2.0.2 None +11 17.7 MB fvictorio
@nomicfoundation/hardhat-ethers 3.0.4 None +8 17.4 MB fvictorio

@Amxx Amxx added this to the tests migration milestone Oct 5, 2023
@Amxx Amxx mentioned this pull request Oct 5, 2023
const { deploy, getFactory } = require('../helpers/deploy');

async function fixture() {
const accounts = await ethers.getSigners(); // this is slow :/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could cache it in a global helper. But it should run only once per contract test suite anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is easily cacheable in env.contract.js

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know how this will behave with ethers?
Would it work if in the tests we do this.accounts.shift() ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking in something like:

extendEnvironment(env => {
  const { contract } = env;

  env.contract = function (name, body) {
    const { takeSnapshot } = require('@nomicfoundation/hardhat-network-helpers');

    contract(name, accounts => {
      // reset the state of the chain in between contract test suites
      let snapshot;
      let signers;

      before(async function () {
        signers = signers ?? await ethers.getSigners();
        snapshot = await takeSnapshot();
      });

      after(async function () {
        await snapshot.restore();
      });

      // remove the default account from the accounts list used in tests, in order
      // to protect tests against accidentally passing due to the contract
      // deployer being used subsequently as function caller
      // The signers array is also cloned using the spread operator to avoid changes
      // to the original array.
      body([...signers].slice(1));
    });
  };
});

Copy link
Collaborator Author

@Amxx Amxx Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And we would get the signers as an argument of contract("...", accounts => { ... });
?

That makes sens, but it will also break the existing truffle tests, which we don't want.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think of keeping a registry of already migrated contract suites?

Too maintenance heavy (and error prone) IMO.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We couldn't confirm such theory, but if that's the case, we should keep loadFixtures per contract suite and also this snapshot.

If loadFixture does a "per contract suite" snapshot, I don't see why we would have to also do a "cross contract suite" snapshot. That is making everything slower, and I really don't see what it brings in exchange. The only usecase I see for that, is if we wanted our test to all start with blockNumber = 0 ... and I don't think that is an assumption we should make. IMO our tests (each test.js file) should work regardless of the initial state of the chain.

Copy link
Collaborator Author

@Amxx Amxx Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could do

body(accounts.slice(1), signers.slice(1));

And then do

// truffle, no change needed
contract("...", accounts => { ... });

and

// ethers
contract("...", (_, signers) => { ... });

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping the high-level snapshot makes sense if not all the contract suites implement a fixture. Let's try removing the high-level snapshot once we finish with the migration as well.

we could do

body(accounts.slice(1), signers.slice(1));

I like this, let's implement it that way 😄

Copy link
Member

@ernestognw ernestognw Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note, I also added a comment in #4658 to keep track of things we should do to consolidate the migration once done


expect(await this.ownable.owner()).to.equal(other);
expect(await this.ownable.owner()).to.equal(this.other.address);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using .address everywhere will be a pain. There is an issue about it:

Are there any workarounds? Is it worth it to contribute to Hardhat to improve this?

Copy link
Collaborator Author

@Amxx Amxx Oct 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I once wrote an helper that is

const getAddress = (account : string | { address: string }) : string => account.address ?? account;

but calling it everywhere is even worst.

Maybe it could be integrated by default ...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ethers.resolveAddress already does it even better ...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I'd love to simplify the code by removing .address when its better supported, but I don't think we should wait for that. We should migrate with .address and then remove them when possible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with migrating using .address without waiting for Hardhat support.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For anyone curious, this PR NomicFoundation/hardhat#4449 resolves the issue in hardhat-chai-matchers

ernestognw
ernestognw previously approved these changes Oct 10, 2023
Copy link
Member

@ernestognw ernestognw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good. It's very weird that the CI is failing for an ERC721 test. Any idea?

test/access/Ownable.test.js Outdated Show resolved Hide resolved
test/access/Ownable.test.js Outdated Show resolved Hide resolved

expect(await this.ownable.owner()).to.equal(other);
expect(await this.ownable.owner()).to.equal(this.other.address);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with migrating using .address without waiting for Hardhat support.

@ernestognw ernestognw dismissed their stale review October 10, 2023 04:07

Accidental approval

test/access/Ownable.test.js Outdated Show resolved Hide resolved
@ernestognw
Copy link
Member

Upgradeable tests are failing because we need to override env-artifacts to also override ethers' equivalent.

Taking a look

@Amxx
Copy link
Collaborator Author

Amxx commented Oct 10, 2023

In env-contract.js:

  • In before, we could do signer = await Promise.all(accounts.map(ethers.getSigner));
    • If we do that, signer is not when doing body ... so its not possible to pass it ...
  • I don't like it, but we could technically do this.signer = await Promise.all(accounts.map(ethers.getSigner)); and that would be passed to the test's this
    • but this this would not be available in loadFixture, were its super usefull.

I'm starting to think that env-contract.js should probably go

@ernestognw
Copy link
Member

Tests are failing because custom errors can't be decoded. Not sure what may be causing it but perhaps it's related to #4349

@Amxx Amxx requested a review from ernestognw October 11, 2023 16:04
@ernestognw
Copy link
Member

After reviewing, I have the suspicion the decoding errors were caused by overriding the require with .call(this. This seemed to be interfering with Hardhat heuristics for decoding errors. We no longer need to bind this for the case of legacy tests so I reverted and tests seem to be passing.

LGTM if they pass.

ernestognw
ernestognw previously approved these changes Oct 12, 2023
@ernestognw
Copy link
Member

Ok it didn't work. An ugly workaround for now would be to run npm run compile in the CI first. For some reason, it solves the issue locally after npm run clean. We should trying removing it after the migration

@ernestognw ernestognw mentioned this pull request Oct 12, 2023
3 tasks
@Amxx
Copy link
Collaborator Author

Amxx commented Oct 12, 2023

Checklist:

  • tests
  • upgradeable tests
  • coverage

Note: yarn coverage --testfiles test/access/Ownable.test.js locally doesnt give any error :/

Edit: this might ba the npm run compile that was added to the test workflow that is missing.

@Amxx
Copy link
Collaborator Author

Amxx commented Oct 12, 2023

Adding npm run compile to the test-upgradeable workflow fixed it.
Coverage still not working :/

Comment on lines +97 to +98
- name: Compile contracts # TODO: Remove after migrating tests to ethers
run: npm run compile
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is needed because the solcover.js config specifies that compileCommand: 'npm run compile'. Are you sure this actually did something?

Just curious, we can leave if it'll be removed at the end of the migration

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it actually does somthing. I just added that to all workflows

Lets see if we can remove them after the migration.

// - the return of hre.ethers.getSigners()
extendEnvironment(hre => {
// TODO: replace with a mocha root hook.
// (see https://github.com/sc-forks/solidity-coverage/issues/819#issuecomment-1762963679)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly I don't see how to use these hooks because they require using a --require flag that I don't see exposed through Hardhat's test CLI (based on mocha).

@Amxx Amxx merged commit 149e1b7 into OpenZeppelin:master Oct 17, 2023
12 checks passed
@Amxx Amxx deleted the test/migration/ownable branch October 17, 2023 08:06
@pcaversaccio
Copy link
Contributor

I hope it's ok to ask two questions here:

  • Have you considered internally moving most tests (private functions and variables as exceptions) to Foundry? Also, IMO, the OZ test suite is very exhaustive, but many unit tests could be replaced with fuzz tests, e.g. you don't test for a specific value but fuzz msg.value directly, or the token minting amount etc. Nowadays, I only write unit tests where it's really required (e.g. you want to test something with address(0)) and for the rest I simply let the params be fuzzed.
  • Is there any specific reason why you still stick to JS after the migration instead of using TS?

@ernestognw ernestognw mentioned this pull request Oct 23, 2023
3 tasks
ernestognw added a commit to ernestognw/openzeppelin-contracts that referenced this pull request Oct 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ignore-changeset tests Test suite and helpers.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants