Finalize test migration: remove legacy dependencies and test helpers (#4797)

pull/4716/head
Hadrien Croubois 1 year ago committed by GitHub
parent abcf9dd8b7
commit a72c9561b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 6
      .github/workflows/checks.yml
  3. 6
      .gitignore
  4. 2
      README.md
  5. 2
      contracts/proxy/README.adoc
  6. 2
      docs/modules/ROOT/pages/index.adoc
  7. 9
      docs/modules/ROOT/pages/utilities.adoc
  8. 2
      docs/modules/ROOT/pages/wizard.adoc
  9. 1
      hardhat.config.js
  10. 23
      hardhat/env-contract.js
  11. 17166
      package-lock.json
  12. 9
      package.json
  13. 2
      scripts/generate/templates/Checkpoints.js
  14. 20
      scripts/upgradeable/upgradeable.patch
  15. 62
      test/access/AccessControl.behavior.js
  16. 2
      test/access/AccessControl.test.js
  17. 18
      test/access/Ownable.test.js
  18. 22
      test/access/Ownable2Step.test.js
  19. 6
      test/access/extensions/AccessControlDefaultAdminRules.test.js
  20. 2
      test/access/extensions/AccessControlEnumerable.test.js
  21. 19
      test/access/manager/AccessManaged.test.js
  22. 10
      test/access/manager/AccessManager.behavior.js
  23. 11
      test/access/manager/AccessManager.predicate.js
  24. 191
      test/access/manager/AccessManager.test.js
  25. 1
      test/access/manager/AuthorityUtils.test.js
  26. 2
      test/finance/VestingWallet.behavior.js
  27. 10
      test/finance/VestingWallet.test.js
  28. 200
      test/governance/Governor.test.js
  29. 20
      test/governance/TimelockController.test.js
  30. 20
      test/governance/extensions/GovernorERC721.test.js
  31. 30
      test/governance/extensions/GovernorPreventLateQuorum.test.js
  32. 10
      test/governance/extensions/GovernorStorage.test.js
  33. 88
      test/governance/extensions/GovernorTimelockAccess.test.js
  34. 84
      test/governance/extensions/GovernorTimelockCompound.test.js
  35. 86
      test/governance/extensions/GovernorTimelockControl.test.js
  36. 20
      test/governance/extensions/GovernorVotesQuorumFraction.test.js
  37. 24
      test/governance/extensions/GovernorWithParams.test.js
  38. 4
      test/governance/utils/ERC6372.behavior.js
  39. 40
      test/governance/utils/Votes.behavior.js
  40. 12
      test/governance/utils/Votes.test.js
  41. 9
      test/helpers/access-manager.js
  42. 6
      test/helpers/chainid.js
  43. 5
      test/helpers/eip712.js
  44. 14
      test/helpers/enums.js
  45. 2
      test/helpers/governance.js
  46. 3
      test/helpers/math.js
  47. 22
      test/helpers/namespaced-storage.js
  48. 19
      test/helpers/storage.js
  49. 19
      test/helpers/time.js
  50. 2
      test/helpers/txpool.js
  51. 8
      test/metatx/ERC2771Context.test.js
  52. 6
      test/metatx/ERC2771Forwarder.test.js
  53. 34
      test/proxy/ERC1967/ERC1967Utils.test.js
  54. 6
      test/proxy/Proxy.behaviour.js
  55. 7
      test/proxy/beacon/BeaconProxy.test.js
  56. 14
      test/proxy/beacon/UpgradeableBeacon.test.js
  57. 13
      test/proxy/transparent/ProxyAdmin.test.js
  58. 34
      test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js
  59. 18
      test/proxy/utils/UUPSUpgradeable.test.js
  60. 6
      test/sanity.test.js
  61. 80
      test/token/ERC1155/ERC1155.behavior.js
  62. 14
      test/token/ERC1155/ERC1155.test.js
  63. 4
      test/token/ERC1155/extensions/ERC1155Burnable.test.js
  64. 2
      test/token/ERC1155/extensions/ERC1155Pausable.test.js
  65. 22
      test/token/ERC20/ERC20.behavior.js
  66. 22
      test/token/ERC20/ERC20.test.js
  67. 14
      test/token/ERC20/extensions/ERC20Burnable.test.js
  68. 36
      test/token/ERC20/extensions/ERC20FlashMint.test.js
  69. 6
      test/token/ERC20/extensions/ERC20Permit.test.js
  70. 50
      test/token/ERC20/extensions/ERC20Votes.test.js
  71. 38
      test/token/ERC20/extensions/ERC20Wrapper.test.js
  72. 150
      test/token/ERC20/extensions/ERC4626.test.js
  73. 24
      test/token/ERC20/utils/SafeERC20.test.js
  74. 70
      test/token/ERC721/ERC721.behavior.js
  75. 4
      test/token/ERC721/extensions/ERC721Burnable.test.js
  76. 22
      test/token/ERC721/extensions/ERC721Consecutive.test.js
  77. 2
      test/token/ERC721/extensions/ERC721Pausable.test.js
  78. 2
      test/token/ERC721/extensions/ERC721URIStorage.test.js
  79. 18
      test/token/ERC721/extensions/ERC721Votes.test.js
  80. 58
      test/token/ERC721/extensions/ERC721Wrapper.test.js
  81. 2
      test/token/ERC721/utils/ERC721Holder.test.js
  82. 14
      test/utils/Address.test.js
  83. 6
      test/utils/Context.behavior.js
  84. 4
      test/utils/Create2.test.js
  85. 8
      test/utils/Multicall.test.js
  86. 2
      test/utils/Nonces.test.js
  87. 4
      test/utils/Pausable.test.js
  88. 35
      test/utils/cryptography/ECDSA.test.js
  89. 3
      test/utils/cryptography/EIP712.test.js
  90. 2
      test/utils/introspection/ERC165.test.js
  91. 11
      test/utils/introspection/SupportsInterface.behavior.js
  92. 4
      test/utils/math/Math.test.js
  93. 2
      test/utils/math/SafeCast.test.js
  94. 2
      test/utils/math/SignedMath.test.js
  95. 2
      test/utils/structs/Checkpoints.test.js
  96. 14
      test/utils/types/Time.test.js

@ -10,7 +10,7 @@ about: Report a bug in OpenZeppelin Contracts
**💻 Environment** **💻 Environment**
<!-- Tell us what version of OpenZeppelin Contracts you're using, and how you're using it: Truffle, Remix, etc. --> <!-- Tell us what version of OpenZeppelin Contracts you're using, and how you're using it: Hardhat, Remix, etc. -->
**📝 Details** **📝 Details**

@ -34,8 +34,6 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Set up environment - name: Set up environment
uses: ./.github/actions/setup uses: ./.github/actions/setup
- name: Compile contracts # TODO: Remove after migrating tests to ethers
run: npm run compile
- name: Run tests and generate gas report - name: Run tests and generate gas report
run: npm run test run: npm run test
- name: Check linearisation of the inheritance graph - name: Check linearisation of the inheritance graph
@ -64,8 +62,6 @@ jobs:
cp -rnT contracts lib/openzeppelin-contracts/contracts cp -rnT contracts lib/openzeppelin-contracts/contracts
- name: Transpile to upgradeable - name: Transpile to upgradeable
run: bash scripts/upgradeable/transpile.sh run: bash scripts/upgradeable/transpile.sh
- name: Compile contracts # TODO: Remove after migrating tests to ethers
run: npm run compile
- name: Run tests - name: Run tests
run: npm run test run: npm run test
- name: Check linearisation of the inheritance graph - name: Check linearisation of the inheritance graph
@ -94,8 +90,6 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Set up environment - name: Set up environment
uses: ./.github/actions/setup uses: ./.github/actions/setup
- name: Compile contracts # TODO: Remove after migrating tests to ethers
run: npm run compile
- name: Run coverage - name: Run coverage
run: npm run coverage run: npm run coverage
- uses: codecov/codecov-action@v3 - uses: codecov/codecov-action@v3

6
.gitignore vendored

@ -29,15 +29,9 @@ npm-debug.log
# local env variables # local env variables
.env .env
# truffle build directory
build/
# macOS # macOS
.DS_Store .DS_Store
# truffle
.node-xmlhttprequest-*
# IntelliJ IDE # IntelliJ IDE
.idea .idea

@ -23,7 +23,7 @@
### Installation ### Installation
#### Hardhat, Truffle (npm) #### Hardhat (npm)
``` ```
$ npm install @openzeppelin/contracts $ npm install @openzeppelin/contracts

@ -19,7 +19,7 @@ There are two alternative ways to add upgradeability to an ERC-1967 proxy. Their
- {TransparentUpgradeableProxy}: A proxy with a built-in immutable admin and upgrade interface. - {TransparentUpgradeableProxy}: A proxy with a built-in immutable admin and upgrade interface.
- {UUPSUpgradeable}: An upgradeability mechanism to be included in the implementation contract. - {UUPSUpgradeable}: An upgradeability mechanism to be included in the implementation contract.
CAUTION: Using upgradeable proxies correctly and securely is a difficult task that requires deep knowledge of the proxy pattern, Solidity, and the EVM. Unless you want a lot of low level control, we recommend using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins] for Truffle and Hardhat. CAUTION: Using upgradeable proxies correctly and securely is a difficult task that requires deep knowledge of the proxy pattern, Solidity, and the EVM. Unless you want a lot of low level control, we recommend using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins] for Hardhat and Foundry.
A different family of proxies are beacon proxies. This pattern, popularized by Dharma, allows multiple proxies to be upgraded to a different implementation in a single transaction. A different family of proxies are beacon proxies. This pattern, popularized by Dharma, allows multiple proxies to be upgraded to a different implementation in a single transaction.

@ -13,7 +13,7 @@ IMPORTANT: OpenZeppelin Contracts uses semantic versioning to communicate backwa
[[install]] [[install]]
=== Installation === Installation
==== Hardhat, Truffle (npm) ==== Hardhat (npm)
```console ```console
$ npm install @openzeppelin/contracts $ npm install @openzeppelin/contracts

@ -186,16 +186,15 @@ contract Box is Multicall {
} }
---- ----
This is how to call the `multicall` function using Truffle, allowing `foo` and `bar` to be called in a single transaction: This is how to call the `multicall` function using Ethers.js, allowing `foo` and `bar` to be called in a single transaction:
[source,javascript] [source,javascript]
---- ----
// scripts/foobar.js // scripts/foobar.js
const Box = artifacts.require('Box'); const instance = await ethers.deployContract("Box");
const instance = await Box.new();
await instance.multicall([ await instance.multicall([
instance.contract.methods.foo().encodeABI(), instance.interface.encodeFunctionData("foo"),
instance.contract.methods.bar().encodeABI() instance.interface.encodeFunctionData("bar")
]); ]);
---- ----

@ -4,7 +4,7 @@
Not sure where to start? Use the interactive generator below to bootstrap your Not sure where to start? Use the interactive generator below to bootstrap your
contract and learn about the components offered in OpenZeppelin Contracts. contract and learn about the components offered in OpenZeppelin Contracts.
TIP: Place the resulting contract in your `contracts` directory in order to compile it with a tool like Hardhat or Truffle. Consider reading our guide on xref:learn::developing-smart-contracts.adoc[Developing Smart Contracts] for more guidance! TIP: Place the resulting contract in your `contracts` or `src` directory in order to compile it with a tool like Hardhat or Foundry. Consider reading our guide on xref:learn::developing-smart-contracts.adoc[Developing Smart Contracts] for more guidance!
++++ ++++
<script async src="https://wizard.openzeppelin.com/build/embed.js"></script> <script async src="https://wizard.openzeppelin.com/build/embed.js"></script>

@ -55,7 +55,6 @@ const argv = require('yargs/yargs')()
}, },
}).argv; }).argv;
require('@nomiclabs/hardhat-truffle5'); // deprecated
require('@nomicfoundation/hardhat-toolbox'); require('@nomicfoundation/hardhat-toolbox');
require('@nomicfoundation/hardhat-ethers'); require('@nomicfoundation/hardhat-ethers');
require('hardhat-ignore-warnings'); require('hardhat-ignore-warnings');

@ -15,27 +15,4 @@ extendEnvironment(hre => {
const filteredSignersAsPromise = originalGetSigners().then(signers => signers.slice(1)); const filteredSignersAsPromise = originalGetSigners().then(signers => signers.slice(1));
hre.ethers.getSigners = () => filteredSignersAsPromise; hre.ethers.getSigners = () => filteredSignersAsPromise;
} }
// override hre.contract
const originalContract = hre.contract;
hre.contract = function (name, body) {
originalContract.call(this, name, accounts => {
let snapshot;
before(async function () {
// reset the state of the chain in between contract test suites
// TODO: this should be removed when migration to ethers is over
const { takeSnapshot } = require('@nomicfoundation/hardhat-network-helpers');
snapshot = await takeSnapshot();
});
after(async function () {
// reset the state of the chain in between contract test suites
// TODO: this should be removed when migration to ethers is over
await snapshot.restore();
});
body(accounts.slice(1));
});
};
}); });

17166
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -53,24 +53,18 @@
"@changesets/cli": "^2.26.0", "@changesets/cli": "^2.26.0",
"@changesets/pre": "^2.0.0", "@changesets/pre": "^2.0.0",
"@changesets/read": "^0.6.0", "@changesets/read": "^0.6.0",
"@nomicfoundation/hardhat-chai-matchers": "^2.0.2", "@nomicfoundation/hardhat-chai-matchers": "^2.0.3",
"@nomicfoundation/hardhat-ethers": "^3.0.4", "@nomicfoundation/hardhat-ethers": "^3.0.4",
"@nomicfoundation/hardhat-foundry": "^1.1.1", "@nomicfoundation/hardhat-foundry": "^1.1.1",
"@nomicfoundation/hardhat-network-helpers": "^1.0.3", "@nomicfoundation/hardhat-network-helpers": "^1.0.3",
"@nomicfoundation/hardhat-toolbox": "^4.0.0", "@nomicfoundation/hardhat-toolbox": "^4.0.0",
"@nomiclabs/hardhat-truffle5": "^2.0.5",
"@nomiclabs/hardhat-web3": "^2.0.0",
"@openzeppelin/docs-utils": "^0.1.5", "@openzeppelin/docs-utils": "^0.1.5",
"@openzeppelin/merkle-tree": "^1.0.5", "@openzeppelin/merkle-tree": "^1.0.5",
"@openzeppelin/test-helpers": "^0.5.13",
"@openzeppelin/upgrade-safe-transpiler": "^0.3.32", "@openzeppelin/upgrade-safe-transpiler": "^0.3.32",
"@openzeppelin/upgrades-core": "^1.20.6", "@openzeppelin/upgrades-core": "^1.20.6",
"chai": "^4.2.0", "chai": "^4.2.0",
"eslint": "^8.30.0", "eslint": "^8.30.0",
"eslint-config-prettier": "^9.0.0", "eslint-config-prettier": "^9.0.0",
"eth-sig-util": "^3.0.0",
"ethereumjs-util": "^7.0.7",
"ethereumjs-wallet": "^1.0.1",
"ethers": "^6.7.1", "ethers": "^6.7.1",
"glob": "^10.3.5", "glob": "^10.3.5",
"graphlib": "^2.1.8", "graphlib": "^2.1.8",
@ -91,7 +85,6 @@
"solidity-coverage": "^0.8.5", "solidity-coverage": "^0.8.5",
"solidity-docgen": "^0.6.0-beta.29", "solidity-docgen": "^0.6.0-beta.29",
"undici": "^5.22.1", "undici": "^5.22.1",
"web3": "^1.3.0",
"yargs": "^17.0.0" "yargs": "^17.0.0"
} }
} }

@ -1,5 +1,5 @@
const format = require('../format-lines'); const format = require('../format-lines');
const { OPTS } = require('./Checkpoints.opts.js'); const { OPTS } = require('./Checkpoints.opts');
// TEMPLATE // TEMPLATE
const header = `\ const header = `\

@ -1,6 +1,6 @@
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644 deleted file mode 100644
index 2797a0889..000000000 index 35ad097ff..000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md --- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null +++ /dev/null
@@ -1,21 +0,0 @@ @@ -1,21 +0,0 @@
@ -16,7 +16,7 @@ index 2797a0889..000000000
- -
-**💻 Environment** -**💻 Environment**
- -
-<!-- Tell us what version of OpenZeppelin Contracts you're using, and how you're using it: Truffle, Remix, etc. --> -<!-- Tell us what version of OpenZeppelin Contracts you're using, and how you're using it: Hardhat, Remix, etc. -->
- -
-**📝 Details** -**📝 Details**
- -
@ -59,7 +59,7 @@ index ff596b0c3..000000000
-<!-- Make sure that you have reviewed the OpenZeppelin Contracts Contributor Guidelines. --> -<!-- Make sure that you have reviewed the OpenZeppelin Contracts Contributor Guidelines. -->
-<!-- https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CONTRIBUTING.md --> -<!-- https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CONTRIBUTING.md -->
diff --git a/README.md b/README.md diff --git a/README.md b/README.md
index 9ca41573f..57d6e3b5b 100644 index 35083bc6e..05cf4fc27 100644
--- a/README.md --- a/README.md
+++ b/README.md +++ b/README.md
@@ -19,6 +19,9 @@ @@ -19,6 +19,9 @@
@ -73,7 +73,7 @@ index 9ca41573f..57d6e3b5b 100644
### Installation ### Installation
@@ -26,7 +29,7 @@ @@ -26,7 +29,7 @@
#### Hardhat, Truffle (npm) #### Hardhat (npm)
``` ```
-$ npm install @openzeppelin/contracts -$ npm install @openzeppelin/contracts
@ -110,7 +110,7 @@ index 9ca41573f..57d6e3b5b 100644
} }
``` ```
diff --git a/contracts/package.json b/contracts/package.json diff --git a/contracts/package.json b/contracts/package.json
index be3e741e3..877e942c2 100644 index 6ab89138a..ece834a44 100644
--- a/contracts/package.json --- a/contracts/package.json
+++ b/contracts/package.json +++ b/contracts/package.json
@@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
@ -118,7 +118,7 @@ index be3e741e3..877e942c2 100644
- "name": "@openzeppelin/contracts", - "name": "@openzeppelin/contracts",
+ "name": "@openzeppelin/contracts-upgradeable", + "name": "@openzeppelin/contracts-upgradeable",
"description": "Secure Smart Contract library for Solidity", "description": "Secure Smart Contract library for Solidity",
"version": "5.0.0", "version": "5.0.1",
"files": [ "files": [
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
}, },
@ -140,7 +140,7 @@ index be3e741e3..877e942c2 100644
+ } + }
} }
diff --git a/contracts/utils/cryptography/EIP712.sol b/contracts/utils/cryptography/EIP712.sol diff --git a/contracts/utils/cryptography/EIP712.sol b/contracts/utils/cryptography/EIP712.sol
index 8e548cdd8..a60ee74fd 100644 index 77c4c8990..602467f40 100644
--- a/contracts/utils/cryptography/EIP712.sol --- a/contracts/utils/cryptography/EIP712.sol
+++ b/contracts/utils/cryptography/EIP712.sol +++ b/contracts/utils/cryptography/EIP712.sol
@@ -4,7 +4,6 @@ @@ -4,7 +4,6 @@
@ -307,7 +307,7 @@ index 8e548cdd8..a60ee74fd 100644
} }
} }
diff --git a/package.json b/package.json diff --git a/package.json b/package.json
index c2c3a2675..3301b213d 100644 index ec2c44ced..46eedc98f 100644
--- a/package.json --- a/package.json
+++ b/package.json +++ b/package.json
@@ -32,7 +32,7 @@ @@ -32,7 +32,7 @@
@ -328,10 +328,10 @@ index 304d1386a..a1cd63bee 100644
+@openzeppelin/contracts-upgradeable/=contracts/ +@openzeppelin/contracts-upgradeable/=contracts/
+@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ +@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
diff --git a/test/utils/cryptography/EIP712.test.js b/test/utils/cryptography/EIP712.test.js diff --git a/test/utils/cryptography/EIP712.test.js b/test/utils/cryptography/EIP712.test.js
index 75ca00b12..265e6c909 100644 index 166038b36..268e0d29d 100644
--- a/test/utils/cryptography/EIP712.test.js --- a/test/utils/cryptography/EIP712.test.js
+++ b/test/utils/cryptography/EIP712.test.js +++ b/test/utils/cryptography/EIP712.test.js
@@ -40,27 +40,6 @@ describe('EIP712', function () { @@ -47,27 +47,6 @@ describe('EIP712', function () {
const rebuildDomain = await getDomain(this.eip712); const rebuildDomain = await getDomain(this.eip712);
expect(rebuildDomain).to.be.deep.equal(this.domain); expect(rebuildDomain).to.be.deep.equal(this.domain);
}); });

@ -1,7 +1,7 @@
const { ethers } = require('hardhat'); const { ethers } = require('hardhat');
const { expect } = require('chai'); const { expect } = require('chai');
const { bigint: time } = require('../helpers/time'); const time = require('../helpers/time');
const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior'); const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior');
@ -38,7 +38,7 @@ function shouldBehaveLikeAccessControl() {
it('non-admin cannot grant role to other accounts', async function () { it('non-admin cannot grant role to other accounts', async function () {
await expect(this.mock.connect(this.other).grantRole(ROLE, this.authorized)) await expect(this.mock.connect(this.other).grantRole(ROLE, this.authorized))
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.other.address, DEFAULT_ADMIN_ROLE); .withArgs(this.other, DEFAULT_ADMIN_ROLE);
}); });
it('accounts can be granted a role multiple times', async function () { it('accounts can be granted a role multiple times', async function () {
@ -68,7 +68,7 @@ function shouldBehaveLikeAccessControl() {
it('admin can revoke role', async function () { it('admin can revoke role', async function () {
await expect(this.mock.connect(this.defaultAdmin).revokeRole(ROLE, this.authorized)) await expect(this.mock.connect(this.defaultAdmin).revokeRole(ROLE, this.authorized))
.to.emit(this.mock, 'RoleRevoked') .to.emit(this.mock, 'RoleRevoked')
.withArgs(ROLE, this.authorized.address, this.defaultAdmin.address); .withArgs(ROLE, this.authorized, this.defaultAdmin);
expect(await this.mock.hasRole(ROLE, this.authorized)).to.equal(false); expect(await this.mock.hasRole(ROLE, this.authorized)).to.equal(false);
}); });
@ -76,7 +76,7 @@ function shouldBehaveLikeAccessControl() {
it('non-admin cannot revoke role', async function () { it('non-admin cannot revoke role', async function () {
await expect(this.mock.connect(this.other).revokeRole(ROLE, this.authorized)) await expect(this.mock.connect(this.other).revokeRole(ROLE, this.authorized))
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.other.address, DEFAULT_ADMIN_ROLE); .withArgs(this.other, DEFAULT_ADMIN_ROLE);
}); });
it('a role can be revoked multiple times', async function () { it('a role can be revoked multiple times', async function () {
@ -106,7 +106,7 @@ function shouldBehaveLikeAccessControl() {
it('bearer can renounce role', async function () { it('bearer can renounce role', async function () {
await expect(this.mock.connect(this.authorized).renounceRole(ROLE, this.authorized)) await expect(this.mock.connect(this.authorized).renounceRole(ROLE, this.authorized))
.to.emit(this.mock, 'RoleRevoked') .to.emit(this.mock, 'RoleRevoked')
.withArgs(ROLE, this.authorized.address, this.authorized.address); .withArgs(ROLE, this.authorized, this.authorized);
expect(await this.mock.hasRole(ROLE, this.authorized)).to.equal(false); expect(await this.mock.hasRole(ROLE, this.authorized)).to.equal(false);
}); });
@ -145,26 +145,26 @@ function shouldBehaveLikeAccessControl() {
it('the new admin can grant roles', async function () { it('the new admin can grant roles', async function () {
await expect(this.mock.connect(this.otherAdmin).grantRole(ROLE, this.authorized)) await expect(this.mock.connect(this.otherAdmin).grantRole(ROLE, this.authorized))
.to.emit(this.mock, 'RoleGranted') .to.emit(this.mock, 'RoleGranted')
.withArgs(ROLE, this.authorized.address, this.otherAdmin.address); .withArgs(ROLE, this.authorized, this.otherAdmin);
}); });
it('the new admin can revoke roles', async function () { it('the new admin can revoke roles', async function () {
await this.mock.connect(this.otherAdmin).grantRole(ROLE, this.authorized); await this.mock.connect(this.otherAdmin).grantRole(ROLE, this.authorized);
await expect(this.mock.connect(this.otherAdmin).revokeRole(ROLE, this.authorized)) await expect(this.mock.connect(this.otherAdmin).revokeRole(ROLE, this.authorized))
.to.emit(this.mock, 'RoleRevoked') .to.emit(this.mock, 'RoleRevoked')
.withArgs(ROLE, this.authorized.address, this.otherAdmin.address); .withArgs(ROLE, this.authorized, this.otherAdmin);
}); });
it("a role's previous admins no longer grant roles", async function () { it("a role's previous admins no longer grant roles", async function () {
await expect(this.mock.connect(this.defaultAdmin).grantRole(ROLE, this.authorized)) await expect(this.mock.connect(this.defaultAdmin).grantRole(ROLE, this.authorized))
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.defaultAdmin.address, OTHER_ROLE); .withArgs(this.defaultAdmin, OTHER_ROLE);
}); });
it("a role's previous admins no longer revoke roles", async function () { it("a role's previous admins no longer revoke roles", async function () {
await expect(this.mock.connect(this.defaultAdmin).revokeRole(ROLE, this.authorized)) await expect(this.mock.connect(this.defaultAdmin).revokeRole(ROLE, this.authorized))
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.defaultAdmin.address, OTHER_ROLE); .withArgs(this.defaultAdmin, OTHER_ROLE);
}); });
}); });
@ -180,13 +180,13 @@ function shouldBehaveLikeAccessControl() {
it("revert if sender doesn't have role #1", async function () { it("revert if sender doesn't have role #1", async function () {
await expect(this.mock.connect(this.other).$_checkRole(ROLE)) await expect(this.mock.connect(this.other).$_checkRole(ROLE))
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.other.address, ROLE); .withArgs(this.other, ROLE);
}); });
it("revert if sender doesn't have role #2", async function () { it("revert if sender doesn't have role #2", async function () {
await expect(this.mock.connect(this.authorized).$_checkRole(OTHER_ROLE)) await expect(this.mock.connect(this.authorized).$_checkRole(OTHER_ROLE))
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.authorized.address, OTHER_ROLE); .withArgs(this.authorized, OTHER_ROLE);
}); });
}); });
@ -271,7 +271,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
describe(`${getter}()`, function () { describe(`${getter}()`, function () {
it('has a default set to the initial default admin', async function () { it('has a default set to the initial default admin', async function () {
const value = await this.mock[getter](); const value = await this.mock[getter]();
expect(value).to.equal(this.defaultAdmin.address); expect(value).to.equal(this.defaultAdmin);
expect(await this.mock.hasRole(DEFAULT_ADMIN_ROLE, value)).to.be.true; expect(await this.mock.hasRole(DEFAULT_ADMIN_ROLE, value)).to.be.true;
}); });
@ -284,7 +284,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
await this.mock.connect(this.newDefaultAdmin).acceptDefaultAdminTransfer(); await this.mock.connect(this.newDefaultAdmin).acceptDefaultAdminTransfer();
const value = await this.mock[getter](); const value = await this.mock[getter]();
expect(value).to.equal(this.newDefaultAdmin.address); expect(value).to.equal(this.newDefaultAdmin);
}); });
}); });
} }
@ -312,7 +312,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
await time.increaseTo.timestamp(firstSchedule + fromSchedule); await time.increaseTo.timestamp(firstSchedule + fromSchedule);
const { newAdmin, schedule } = await this.mock.pendingDefaultAdmin(); const { newAdmin, schedule } = await this.mock.pendingDefaultAdmin();
expect(newAdmin).to.equal(this.newDefaultAdmin.address); expect(newAdmin).to.equal(this.newDefaultAdmin);
expect(schedule).to.equal(firstSchedule); expect(schedule).to.equal(firstSchedule);
}); });
} }
@ -429,7 +429,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
it('reverts if called by non default admin accounts', async function () { it('reverts if called by non default admin accounts', async function () {
await expect(this.mock.connect(this.other).beginDefaultAdminTransfer(this.newDefaultAdmin)) await expect(this.mock.connect(this.other).beginDefaultAdminTransfer(this.newDefaultAdmin))
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.other.address, DEFAULT_ADMIN_ROLE); .withArgs(this.other, DEFAULT_ADMIN_ROLE);
}); });
describe('when there is no pending delay nor pending admin transfer', function () { describe('when there is no pending delay nor pending admin transfer', function () {
@ -440,10 +440,10 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
await time.increaseTo.timestamp(nextBlockTimestamp, false); // set timestamp but don't mine the block yet await time.increaseTo.timestamp(nextBlockTimestamp, false); // set timestamp but don't mine the block yet
await expect(this.mock.connect(this.defaultAdmin).beginDefaultAdminTransfer(this.newDefaultAdmin)) await expect(this.mock.connect(this.defaultAdmin).beginDefaultAdminTransfer(this.newDefaultAdmin))
.to.emit(this.mock, 'DefaultAdminTransferScheduled') .to.emit(this.mock, 'DefaultAdminTransferScheduled')
.withArgs(this.newDefaultAdmin.address, acceptSchedule); .withArgs(this.newDefaultAdmin, acceptSchedule);
const { newAdmin, schedule } = await this.mock.pendingDefaultAdmin(); const { newAdmin, schedule } = await this.mock.pendingDefaultAdmin();
expect(newAdmin).to.equal(this.newDefaultAdmin.address); expect(newAdmin).to.equal(this.newDefaultAdmin);
expect(schedule).to.equal(acceptSchedule); expect(schedule).to.equal(acceptSchedule);
}); });
}); });
@ -470,7 +470,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
); );
const newSchedule = (await time.clock.timestamp()) + this.delay; const newSchedule = (await time.clock.timestamp()) + this.delay;
const { newAdmin, schedule } = await this.mock.pendingDefaultAdmin(); const { newAdmin, schedule } = await this.mock.pendingDefaultAdmin();
expect(newAdmin).to.equal(this.other.address); expect(newAdmin).to.equal(this.other);
expect(schedule).to.equal(newSchedule); expect(schedule).to.equal(newSchedule);
}); });
} }
@ -513,11 +513,11 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
const expectedAcceptSchedule = nextBlockTimestamp + expectedDelay; const expectedAcceptSchedule = nextBlockTimestamp + expectedDelay;
await expect(this.mock.connect(this.defaultAdmin).beginDefaultAdminTransfer(this.newDefaultAdmin)) await expect(this.mock.connect(this.defaultAdmin).beginDefaultAdminTransfer(this.newDefaultAdmin))
.to.emit(this.mock, 'DefaultAdminTransferScheduled') .to.emit(this.mock, 'DefaultAdminTransferScheduled')
.withArgs(this.newDefaultAdmin.address, expectedAcceptSchedule); .withArgs(this.newDefaultAdmin, expectedAcceptSchedule);
// Check that the schedule corresponds with the new delay // Check that the schedule corresponds with the new delay
const { newAdmin, schedule: transferSchedule } = await this.mock.pendingDefaultAdmin(); const { newAdmin, schedule: transferSchedule } = await this.mock.pendingDefaultAdmin();
expect(newAdmin).to.equal(this.newDefaultAdmin.address); expect(newAdmin).to.equal(this.newDefaultAdmin);
expect(transferSchedule).to.equal(expectedAcceptSchedule); expect(transferSchedule).to.equal(expectedAcceptSchedule);
}); });
} }
@ -534,7 +534,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
await time.increaseTo.timestamp(this.acceptSchedule + 1n, false); await time.increaseTo.timestamp(this.acceptSchedule + 1n, false);
await expect(this.mock.connect(this.other).acceptDefaultAdminTransfer()) await expect(this.mock.connect(this.other).acceptDefaultAdminTransfer())
.to.be.revertedWithCustomError(this.mock, 'AccessControlInvalidDefaultAdmin') .to.be.revertedWithCustomError(this.mock, 'AccessControlInvalidDefaultAdmin')
.withArgs(this.other.address); .withArgs(this.other);
}); });
describe('when caller is pending default admin and delay has passed', function () { describe('when caller is pending default admin and delay has passed', function () {
@ -546,14 +546,14 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
// Emit events // Emit events
await expect(this.mock.connect(this.newDefaultAdmin).acceptDefaultAdminTransfer()) await expect(this.mock.connect(this.newDefaultAdmin).acceptDefaultAdminTransfer())
.to.emit(this.mock, 'RoleRevoked') .to.emit(this.mock, 'RoleRevoked')
.withArgs(DEFAULT_ADMIN_ROLE, this.defaultAdmin.address, this.newDefaultAdmin.address) .withArgs(DEFAULT_ADMIN_ROLE, this.defaultAdmin, this.newDefaultAdmin)
.to.emit(this.mock, 'RoleGranted') .to.emit(this.mock, 'RoleGranted')
.withArgs(DEFAULT_ADMIN_ROLE, this.newDefaultAdmin.address, this.newDefaultAdmin.address); .withArgs(DEFAULT_ADMIN_ROLE, this.newDefaultAdmin, this.newDefaultAdmin);
// Storage changes // Storage changes
expect(await this.mock.hasRole(DEFAULT_ADMIN_ROLE, this.defaultAdmin)).to.be.false; expect(await this.mock.hasRole(DEFAULT_ADMIN_ROLE, this.defaultAdmin)).to.be.false;
expect(await this.mock.hasRole(DEFAULT_ADMIN_ROLE, this.newDefaultAdmin)).to.be.true; expect(await this.mock.hasRole(DEFAULT_ADMIN_ROLE, this.newDefaultAdmin)).to.be.true;
expect(await this.mock.owner()).to.equal(this.newDefaultAdmin.address); expect(await this.mock.owner()).to.equal(this.newDefaultAdmin);
// Resets pending default admin and schedule // Resets pending default admin and schedule
const { newAdmin, schedule } = await this.mock.pendingDefaultAdmin(); const { newAdmin, schedule } = await this.mock.pendingDefaultAdmin();
@ -581,7 +581,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
it('reverts if called by non default admin accounts', async function () { it('reverts if called by non default admin accounts', async function () {
await expect(this.mock.connect(this.other).cancelDefaultAdminTransfer()) await expect(this.mock.connect(this.other).cancelDefaultAdminTransfer())
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.other.address, DEFAULT_ADMIN_ROLE); .withArgs(this.other, DEFAULT_ADMIN_ROLE);
}); });
describe('when there is a pending default admin transfer', function () { describe('when there is a pending default admin transfer', function () {
@ -619,7 +619,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
// Previous pending default admin should not be able to accept after cancellation. // Previous pending default admin should not be able to accept after cancellation.
await expect(this.mock.connect(this.newDefaultAdmin).acceptDefaultAdminTransfer()) await expect(this.mock.connect(this.newDefaultAdmin).acceptDefaultAdminTransfer())
.to.be.revertedWithCustomError(this.mock, 'AccessControlInvalidDefaultAdmin') .to.be.revertedWithCustomError(this.mock, 'AccessControlInvalidDefaultAdmin')
.withArgs(this.newDefaultAdmin.address); .withArgs(this.newDefaultAdmin);
}); });
}); });
@ -666,14 +666,14 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
await this.mock.connect(this.other).renounceRole(DEFAULT_ADMIN_ROLE, this.other); await this.mock.connect(this.other).renounceRole(DEFAULT_ADMIN_ROLE, this.other);
expect(await this.mock.hasRole(DEFAULT_ADMIN_ROLE, this.defaultAdmin)).to.be.true; expect(await this.mock.hasRole(DEFAULT_ADMIN_ROLE, this.defaultAdmin)).to.be.true;
expect(await this.mock.defaultAdmin()).to.be.equal(this.defaultAdmin.address); expect(await this.mock.defaultAdmin()).to.be.equal(this.defaultAdmin);
}); });
it('renounces role', async function () { it('renounces role', async function () {
await time.increaseBy.timestamp(this.delay + 1n, false); await time.increaseBy.timestamp(this.delay + 1n, false);
await expect(this.mock.connect(this.defaultAdmin).renounceRole(DEFAULT_ADMIN_ROLE, this.defaultAdmin)) await expect(this.mock.connect(this.defaultAdmin).renounceRole(DEFAULT_ADMIN_ROLE, this.defaultAdmin))
.to.emit(this.mock, 'RoleRevoked') .to.emit(this.mock, 'RoleRevoked')
.withArgs(DEFAULT_ADMIN_ROLE, this.defaultAdmin.address, this.defaultAdmin.address); .withArgs(DEFAULT_ADMIN_ROLE, this.defaultAdmin, this.defaultAdmin);
expect(await this.mock.hasRole(DEFAULT_ADMIN_ROLE, this.defaultAdmin)).to.be.false; expect(await this.mock.hasRole(DEFAULT_ADMIN_ROLE, this.defaultAdmin)).to.be.false;
expect(await this.mock.defaultAdmin()).to.be.equal(ethers.ZeroAddress); expect(await this.mock.defaultAdmin()).to.be.equal(ethers.ZeroAddress);
@ -690,7 +690,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
await expect(this.mock.connect(this.defaultAdmin).$_grantRole(DEFAULT_ADMIN_ROLE, this.other)) await expect(this.mock.connect(this.defaultAdmin).$_grantRole(DEFAULT_ADMIN_ROLE, this.other))
.to.emit(this.mock, 'RoleGranted') .to.emit(this.mock, 'RoleGranted')
.withArgs(DEFAULT_ADMIN_ROLE, this.other.address, this.defaultAdmin.address); .withArgs(DEFAULT_ADMIN_ROLE, this.other, this.defaultAdmin);
}); });
describe('schedule not passed', function () { describe('schedule not passed', function () {
@ -712,7 +712,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
it('reverts if called by non default admin accounts', async function () { it('reverts if called by non default admin accounts', async function () {
await expect(this.mock.connect(this.other).changeDefaultAdminDelay(time.duration.hours(4))) await expect(this.mock.connect(this.other).changeDefaultAdminDelay(time.duration.hours(4)))
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.other.address, DEFAULT_ADMIN_ROLE); .withArgs(this.other, DEFAULT_ADMIN_ROLE);
}); });
for (const [delayDifference, delayChangeType] of [ for (const [delayDifference, delayChangeType] of [
@ -810,7 +810,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() {
it('reverts if called by non default admin accounts', async function () { it('reverts if called by non default admin accounts', async function () {
await expect(this.mock.connect(this.other).rollbackDefaultAdminDelay()) await expect(this.mock.connect(this.other).rollbackDefaultAdminDelay())
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.other.address, DEFAULT_ADMIN_ROLE); .withArgs(this.other, DEFAULT_ADMIN_ROLE);
}); });
describe('when there is a pending delay', function () { describe('when there is a pending delay', function () {

@ -1,7 +1,7 @@
const { ethers } = require('hardhat'); const { ethers } = require('hardhat');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { DEFAULT_ADMIN_ROLE, shouldBehaveLikeAccessControl } = require('./AccessControl.behavior.js'); const { DEFAULT_ADMIN_ROLE, shouldBehaveLikeAccessControl } = require('./AccessControl.behavior');
async function fixture() { async function fixture() {
const [defaultAdmin, ...accounts] = await ethers.getSigners(); const [defaultAdmin, ...accounts] = await ethers.getSigners();

@ -16,7 +16,7 @@ describe('Ownable', function () {
it('emits ownership transfer events during construction', async function () { it('emits ownership transfer events during construction', async function () {
await expect(this.ownable.deploymentTransaction()) await expect(this.ownable.deploymentTransaction())
.to.emit(this.ownable, 'OwnershipTransferred') .to.emit(this.ownable, 'OwnershipTransferred')
.withArgs(ethers.ZeroAddress, this.owner.address); .withArgs(ethers.ZeroAddress, this.owner);
}); });
it('rejects zero address for initialOwner', async function () { it('rejects zero address for initialOwner', async function () {
@ -26,22 +26,22 @@ describe('Ownable', function () {
}); });
it('has an owner', async function () { it('has an owner', async function () {
expect(await this.ownable.owner()).to.equal(this.owner.address); expect(await this.ownable.owner()).to.equal(this.owner);
}); });
describe('transfer ownership', function () { describe('transfer ownership', function () {
it('changes owner after transfer', async function () { it('changes owner after transfer', async function () {
await expect(this.ownable.connect(this.owner).transferOwnership(this.other)) await expect(this.ownable.connect(this.owner).transferOwnership(this.other))
.to.emit(this.ownable, 'OwnershipTransferred') .to.emit(this.ownable, 'OwnershipTransferred')
.withArgs(this.owner.address, this.other.address); .withArgs(this.owner, this.other);
expect(await this.ownable.owner()).to.equal(this.other.address); expect(await this.ownable.owner()).to.equal(this.other);
}); });
it('prevents non-owners from transferring', async function () { it('prevents non-owners from transferring', async function () {
await expect(this.ownable.connect(this.other).transferOwnership(this.other)) await expect(this.ownable.connect(this.other).transferOwnership(this.other))
.to.be.revertedWithCustomError(this.ownable, 'OwnableUnauthorizedAccount') .to.be.revertedWithCustomError(this.ownable, 'OwnableUnauthorizedAccount')
.withArgs(this.other.address); .withArgs(this.other);
}); });
it('guards ownership against stuck state', async function () { it('guards ownership against stuck state', async function () {
@ -55,7 +55,7 @@ describe('Ownable', function () {
it('loses ownership after renouncement', async function () { it('loses ownership after renouncement', async function () {
await expect(this.ownable.connect(this.owner).renounceOwnership()) await expect(this.ownable.connect(this.owner).renounceOwnership())
.to.emit(this.ownable, 'OwnershipTransferred') .to.emit(this.ownable, 'OwnershipTransferred')
.withArgs(this.owner.address, ethers.ZeroAddress); .withArgs(this.owner, ethers.ZeroAddress);
expect(await this.ownable.owner()).to.equal(ethers.ZeroAddress); expect(await this.ownable.owner()).to.equal(ethers.ZeroAddress);
}); });
@ -63,7 +63,7 @@ describe('Ownable', function () {
it('prevents non-owners from renouncement', async function () { it('prevents non-owners from renouncement', async function () {
await expect(this.ownable.connect(this.other).renounceOwnership()) await expect(this.ownable.connect(this.other).renounceOwnership())
.to.be.revertedWithCustomError(this.ownable, 'OwnableUnauthorizedAccount') .to.be.revertedWithCustomError(this.ownable, 'OwnableUnauthorizedAccount')
.withArgs(this.other.address); .withArgs(this.other);
}); });
it('allows to recover access using the internal _transferOwnership', async function () { it('allows to recover access using the internal _transferOwnership', async function () {
@ -71,9 +71,9 @@ describe('Ownable', function () {
await expect(this.ownable.$_transferOwnership(this.other)) await expect(this.ownable.$_transferOwnership(this.other))
.to.emit(this.ownable, 'OwnershipTransferred') .to.emit(this.ownable, 'OwnershipTransferred')
.withArgs(ethers.ZeroAddress, this.other.address); .withArgs(ethers.ZeroAddress, this.other);
expect(await this.ownable.owner()).to.equal(this.other.address); expect(await this.ownable.owner()).to.equal(this.other);
}); });
}); });
}); });

@ -22,10 +22,10 @@ describe('Ownable2Step', function () {
it('starting a transfer does not change owner', async function () { it('starting a transfer does not change owner', async function () {
await expect(this.ownable2Step.connect(this.owner).transferOwnership(this.accountA)) await expect(this.ownable2Step.connect(this.owner).transferOwnership(this.accountA))
.to.emit(this.ownable2Step, 'OwnershipTransferStarted') .to.emit(this.ownable2Step, 'OwnershipTransferStarted')
.withArgs(this.owner.address, this.accountA.address); .withArgs(this.owner, this.accountA);
expect(await this.ownable2Step.owner()).to.equal(this.owner.address); expect(await this.ownable2Step.owner()).to.equal(this.owner);
expect(await this.ownable2Step.pendingOwner()).to.equal(this.accountA.address); expect(await this.ownable2Step.pendingOwner()).to.equal(this.accountA);
}); });
it('changes owner after transfer', async function () { it('changes owner after transfer', async function () {
@ -33,9 +33,9 @@ describe('Ownable2Step', function () {
await expect(this.ownable2Step.connect(this.accountA).acceptOwnership()) await expect(this.ownable2Step.connect(this.accountA).acceptOwnership())
.to.emit(this.ownable2Step, 'OwnershipTransferred') .to.emit(this.ownable2Step, 'OwnershipTransferred')
.withArgs(this.owner.address, this.accountA.address); .withArgs(this.owner, this.accountA);
expect(await this.ownable2Step.owner()).to.equal(this.accountA.address); expect(await this.ownable2Step.owner()).to.equal(this.accountA);
expect(await this.ownable2Step.pendingOwner()).to.equal(ethers.ZeroAddress); expect(await this.ownable2Step.pendingOwner()).to.equal(ethers.ZeroAddress);
}); });
@ -44,7 +44,7 @@ describe('Ownable2Step', function () {
await expect(this.ownable2Step.connect(this.accountB).acceptOwnership()) await expect(this.ownable2Step.connect(this.accountB).acceptOwnership())
.to.be.revertedWithCustomError(this.ownable2Step, 'OwnableUnauthorizedAccount') .to.be.revertedWithCustomError(this.ownable2Step, 'OwnableUnauthorizedAccount')
.withArgs(this.accountB.address); .withArgs(this.accountB);
}); });
}); });
@ -52,7 +52,7 @@ describe('Ownable2Step', function () {
it('changes owner after renouncing ownership', async function () { it('changes owner after renouncing ownership', async function () {
await expect(this.ownable2Step.connect(this.owner).renounceOwnership()) await expect(this.ownable2Step.connect(this.owner).renounceOwnership())
.to.emit(this.ownable2Step, 'OwnershipTransferred') .to.emit(this.ownable2Step, 'OwnershipTransferred')
.withArgs(this.owner.address, ethers.ZeroAddress); .withArgs(this.owner, ethers.ZeroAddress);
// If renounceOwnership is removed from parent an alternative is needed ... // If renounceOwnership is removed from parent an alternative is needed ...
// without it is difficult to cleanly renounce with the two step process // without it is difficult to cleanly renounce with the two step process
@ -62,14 +62,14 @@ describe('Ownable2Step', function () {
it('pending owner resets after renouncing ownership', async function () { it('pending owner resets after renouncing ownership', async function () {
await this.ownable2Step.connect(this.owner).transferOwnership(this.accountA); await this.ownable2Step.connect(this.owner).transferOwnership(this.accountA);
expect(await this.ownable2Step.pendingOwner()).to.equal(this.accountA.address); expect(await this.ownable2Step.pendingOwner()).to.equal(this.accountA);
await this.ownable2Step.connect(this.owner).renounceOwnership(); await this.ownable2Step.connect(this.owner).renounceOwnership();
expect(await this.ownable2Step.pendingOwner()).to.equal(ethers.ZeroAddress); expect(await this.ownable2Step.pendingOwner()).to.equal(ethers.ZeroAddress);
await expect(this.ownable2Step.connect(this.accountA).acceptOwnership()) await expect(this.ownable2Step.connect(this.accountA).acceptOwnership())
.to.be.revertedWithCustomError(this.ownable2Step, 'OwnableUnauthorizedAccount') .to.be.revertedWithCustomError(this.ownable2Step, 'OwnableUnauthorizedAccount')
.withArgs(this.accountA.address); .withArgs(this.accountA);
}); });
it('allows to recover access using the internal _transferOwnership', async function () { it('allows to recover access using the internal _transferOwnership', async function () {
@ -77,9 +77,9 @@ describe('Ownable2Step', function () {
await expect(this.ownable2Step.$_transferOwnership(this.accountA)) await expect(this.ownable2Step.$_transferOwnership(this.accountA))
.to.emit(this.ownable2Step, 'OwnershipTransferred') .to.emit(this.ownable2Step, 'OwnershipTransferred')
.withArgs(ethers.ZeroAddress, this.accountA.address); .withArgs(ethers.ZeroAddress, this.accountA);
expect(await this.ownable2Step.owner()).to.equal(this.accountA.address); expect(await this.ownable2Step.owner()).to.equal(this.accountA);
}); });
}); });
}); });

@ -1,11 +1,13 @@
const { ethers } = require('hardhat'); const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { bigint: time } = require('../../helpers/time');
const time = require('../../helpers/time');
const { const {
shouldBehaveLikeAccessControl, shouldBehaveLikeAccessControl,
shouldBehaveLikeAccessControlDefaultAdminRules, shouldBehaveLikeAccessControlDefaultAdminRules,
} = require('../AccessControl.behavior.js'); } = require('../AccessControl.behavior');
async function fixture() { async function fixture() {
const delay = time.duration.hours(10); const delay = time.duration.hours(10);

@ -5,7 +5,7 @@ const {
DEFAULT_ADMIN_ROLE, DEFAULT_ADMIN_ROLE,
shouldBehaveLikeAccessControl, shouldBehaveLikeAccessControl,
shouldBehaveLikeAccessControlEnumerable, shouldBehaveLikeAccessControlEnumerable,
} = require('../AccessControl.behavior.js'); } = require('../AccessControl.behavior');
async function fixture() { async function fixture() {
const [defaultAdmin, ...accounts] = await ethers.getSigners(); const [defaultAdmin, ...accounts] = await ethers.getSigners();

@ -1,8 +1,9 @@
const { ethers } = require('hardhat'); const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { impersonate } = require('../../helpers/account'); const { impersonate } = require('../../helpers/account');
const { bigint: time } = require('../../helpers/time'); const time = require('../../helpers/time');
async function fixture() { async function fixture() {
const [admin, roleMember, other] = await ethers.getSigners(); const [admin, roleMember, other] = await ethers.getSigners();
@ -35,7 +36,7 @@ describe('AccessManaged', function () {
it('sets authority and emits AuthorityUpdated event during construction', async function () { it('sets authority and emits AuthorityUpdated event during construction', async function () {
await expect(this.managed.deploymentTransaction()) await expect(this.managed.deploymentTransaction())
.to.emit(this.managed, 'AuthorityUpdated') .to.emit(this.managed, 'AuthorityUpdated')
.withArgs(this.authority.target); .withArgs(this.authority);
}); });
describe('restricted modifier', function () { describe('restricted modifier', function () {
@ -53,7 +54,7 @@ describe('AccessManaged', function () {
it('reverts when role is not granted', async function () { it('reverts when role is not granted', async function () {
await expect(this.managed.connect(this.other)[this.selector]()) await expect(this.managed.connect(this.other)[this.selector]())
.to.be.revertedWithCustomError(this.managed, 'AccessManagedUnauthorized') .to.be.revertedWithCustomError(this.managed, 'AccessManagedUnauthorized')
.withArgs(this.other.address); .withArgs(this.other);
}); });
it('panics in short calldata', async function () { it('panics in short calldata', async function () {
@ -103,21 +104,21 @@ describe('AccessManaged', function () {
it('reverts if the caller is not the authority', async function () { it('reverts if the caller is not the authority', async function () {
await expect(this.managed.connect(this.other).setAuthority(this.other)) await expect(this.managed.connect(this.other).setAuthority(this.other))
.to.be.revertedWithCustomError(this.managed, 'AccessManagedUnauthorized') .to.be.revertedWithCustomError(this.managed, 'AccessManagedUnauthorized')
.withArgs(this.other.address); .withArgs(this.other);
}); });
it('reverts if the new authority is not a valid authority', async function () { it('reverts if the new authority is not a valid authority', async function () {
await expect(this.managed.connect(this.authorityAsSigner).setAuthority(this.other)) await expect(this.managed.connect(this.authorityAsSigner).setAuthority(this.other))
.to.be.revertedWithCustomError(this.managed, 'AccessManagedInvalidAuthority') .to.be.revertedWithCustomError(this.managed, 'AccessManagedInvalidAuthority')
.withArgs(this.other.address); .withArgs(this.other);
}); });
it('sets authority and emits AuthorityUpdated event', async function () { it('sets authority and emits AuthorityUpdated event', async function () {
await expect(this.managed.connect(this.authorityAsSigner).setAuthority(this.anotherAuthority)) await expect(this.managed.connect(this.authorityAsSigner).setAuthority(this.anotherAuthority))
.to.emit(this.managed, 'AuthorityUpdated') .to.emit(this.managed, 'AuthorityUpdated')
.withArgs(this.anotherAuthority.target); .withArgs(this.anotherAuthority);
expect(await this.managed.authority()).to.equal(this.anotherAuthority.target); expect(await this.managed.authority()).to.equal(this.anotherAuthority);
}); });
}); });
@ -136,7 +137,7 @@ describe('AccessManaged', function () {
await expect(this.managed.connect(this.other).fnRestricted()) await expect(this.managed.connect(this.other).fnRestricted())
.to.emit(this.authorityObserveIsConsuming, 'ConsumeScheduledOpCalled') .to.emit(this.authorityObserveIsConsuming, 'ConsumeScheduledOpCalled')
.withArgs( .withArgs(
this.other.address, this.other,
this.managed.interface.encodeFunctionData(fnRestricted, []), this.managed.interface.encodeFunctionData(fnRestricted, []),
isConsumingScheduledOp.selector, isConsumingScheduledOp.selector,
); );

@ -1,3 +1,5 @@
const { expect } = require('chai');
const { const {
LIKE_COMMON_IS_EXECUTING, LIKE_COMMON_IS_EXECUTING,
LIKE_COMMON_GET_ACCESS, LIKE_COMMON_GET_ACCESS,
@ -39,7 +41,7 @@ function shouldBehaveLikeDelayedAdminOperation() {
await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata })) await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata }))
.to.be.revertedWithCustomError(this.target, 'AccessManagerUnauthorizedAccount') .to.be.revertedWithCustomError(this.target, 'AccessManagerUnauthorizedAccount')
.withArgs( .withArgs(
this.caller.address, this.caller,
this.roles.ADMIN.id, // Although PUBLIC is required, target function role doesn't apply to admin ops this.roles.ADMIN.id, // Although PUBLIC is required, target function role doesn't apply to admin ops
); );
}); });
@ -83,7 +85,7 @@ function shouldBehaveLikeNotDelayedAdminOperation() {
await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata })) await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata }))
.to.be.revertedWithCustomError(this.target, 'AccessManagerUnauthorizedAccount') .to.be.revertedWithCustomError(this.target, 'AccessManagerUnauthorizedAccount')
.withArgs( .withArgs(
this.caller.address, this.caller,
this.roles.ADMIN.id, // Although PUBLIC_ROLE is required, admin ops are not subject to target function roles this.roles.ADMIN.id, // Although PUBLIC_ROLE is required, admin ops are not subject to target function roles
); );
}); });
@ -123,7 +125,7 @@ function shouldBehaveLikeRoleAdminOperation(roleAdmin) {
it('reverts as AccessManagerUnauthorizedAccount', async function () { it('reverts as AccessManagerUnauthorizedAccount', async function () {
await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata })) await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata }))
.to.be.revertedWithCustomError(this.target, 'AccessManagerUnauthorizedAccount') .to.be.revertedWithCustomError(this.target, 'AccessManagerUnauthorizedAccount')
.withArgs(this.caller.address, roleAdmin); .withArgs(this.caller, roleAdmin);
}); });
}, },
specificRoleIsRequired: getAccessPath, specificRoleIsRequired: getAccessPath,
@ -142,7 +144,7 @@ function shouldBehaveLikeAManagedRestrictedOperation() {
it('reverts as AccessManagedUnauthorized', async function () { it('reverts as AccessManagedUnauthorized', async function () {
await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata })) await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata }))
.to.be.revertedWithCustomError(this.target, 'AccessManagedUnauthorized') .to.be.revertedWithCustomError(this.target, 'AccessManagedUnauthorized')
.withArgs(this.caller.address); .withArgs(this.caller);
}); });
} }

@ -1,9 +1,10 @@
const { ethers } = require('hardhat'); const { ethers } = require('hardhat');
const { expect } = require('chai');
const { setStorageAt } = require('@nomicfoundation/hardhat-network-helpers'); const { setStorageAt } = require('@nomicfoundation/hardhat-network-helpers');
const { EXECUTION_ID_STORAGE_SLOT, EXPIRATION, prepareOperation } = require('../../helpers/access-manager'); const { EXECUTION_ID_STORAGE_SLOT, EXPIRATION, prepareOperation } = require('../../helpers/access-manager');
const { impersonate } = require('../../helpers/account'); const { impersonate } = require('../../helpers/account');
const { bigint: time } = require('../../helpers/time'); const time = require('../../helpers/time');
// ============ COMMON PREDICATES ============ // ============ COMMON PREDICATES ============
@ -17,7 +18,7 @@ const LIKE_COMMON_IS_EXECUTING = {
it('reverts as AccessManagerUnauthorizedAccount', async function () { it('reverts as AccessManagerUnauthorizedAccount', async function () {
await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata })) await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata }))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedAccount') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedAccount')
.withArgs(this.caller.address, this.role.id); .withArgs(this.caller, this.role.id);
}); });
}, },
}; };
@ -30,7 +31,7 @@ const LIKE_COMMON_GET_ACCESS = {
it('reverts as AccessManagerUnauthorizedAccount', async function () { it('reverts as AccessManagerUnauthorizedAccount', async function () {
await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata })) await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata }))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedAccount') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedAccount')
.withArgs(this.caller.address, this.role.id); .withArgs(this.caller, this.role.id);
}); });
}, },
afterGrantDelay: undefined, // Diverges if there's an operation delay or not afterGrantDelay: undefined, // Diverges if there's an operation delay or not
@ -40,7 +41,7 @@ const LIKE_COMMON_GET_ACCESS = {
it('reverts as AccessManagerUnauthorizedAccount', async function () { it('reverts as AccessManagerUnauthorizedAccount', async function () {
await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata })) await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata }))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedAccount') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedAccount')
.withArgs(this.caller.address, this.role.id); .withArgs(this.caller, this.role.id);
}); });
}, },
afterGrantDelay() { afterGrantDelay() {
@ -71,7 +72,7 @@ const LIKE_COMMON_GET_ACCESS = {
it('reverts as AccessManagerUnauthorizedAccount', async function () { it('reverts as AccessManagerUnauthorizedAccount', async function () {
await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata })) await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata }))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedAccount') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedAccount')
.withArgs(this.caller.address, this.role.id); .withArgs(this.caller, this.role.id);
}); });
}, },
}; };

@ -1,11 +1,11 @@
const { ethers } = require('hardhat'); const { ethers } = require('hardhat');
const { expect } = require('chai'); const { expect } = require('chai');
const { loadFixture, getStorageAt } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { impersonate } = require('../../helpers/account'); const { impersonate } = require('../../helpers/account');
const { MAX_UINT48 } = require('../../helpers/constants'); const { MAX_UINT48 } = require('../../helpers/constants');
const { bigint: time } = require('../../helpers/time');
const { selector } = require('../../helpers/methods'); const { selector } = require('../../helpers/methods');
const time = require('../../helpers/time');
const { const {
buildBaseRoles, buildBaseRoles,
@ -17,12 +17,14 @@ const {
prepareOperation, prepareOperation,
hashOperation, hashOperation,
} = require('../../helpers/access-manager'); } = require('../../helpers/access-manager');
const { const {
shouldBehaveLikeDelayedAdminOperation, shouldBehaveLikeDelayedAdminOperation,
shouldBehaveLikeNotDelayedAdminOperation, shouldBehaveLikeNotDelayedAdminOperation,
shouldBehaveLikeRoleAdminOperation, shouldBehaveLikeRoleAdminOperation,
shouldBehaveLikeAManagedRestrictedOperation, shouldBehaveLikeAManagedRestrictedOperation,
} = require('./AccessManager.behavior'); } = require('./AccessManager.behavior');
const { const {
LIKE_COMMON_SCHEDULABLE, LIKE_COMMON_SCHEDULABLE,
testAsClosable, testAsClosable,
@ -33,8 +35,6 @@ const {
testAsGetAccess, testAsGetAccess,
} = require('./AccessManager.predicate'); } = require('./AccessManager.predicate');
const { address: someAddress } = ethers.Wallet.createRandom();
async function fixture() { async function fixture() {
const [admin, roleAdmin, roleGuardian, member, user, other] = await ethers.getSigners(); const [admin, roleAdmin, roleGuardian, member, user, other] = await ethers.getSigners();
@ -72,11 +72,8 @@ async function fixture() {
} }
return { return {
// TODO: Check if all signers are actually used
admin, admin,
roleAdmin, roleAdmin,
roleGuardian,
member,
user, user,
other, other,
roles, roles,
@ -99,9 +96,7 @@ async function fixture() {
// The predicates can be identified by the `testAs*` prefix while the behaviors // The predicates can be identified by the `testAs*` prefix while the behaviors
// are prefixed with `shouldBehave*`. The common assertions for predicates are // are prefixed with `shouldBehave*`. The common assertions for predicates are
// defined as constants. // defined as constants.
contract('AccessManager', function () { describe('AccessManager', function () {
// const [admin, manager, guardian, member, user, other] = accounts;
beforeEach(async function () { beforeEach(async function () {
Object.assign(this, await loadFixture(fixture)); Object.assign(this, await loadFixture(fixture));
}); });
@ -144,7 +139,7 @@ contract('AccessManager', function () {
closed() { closed() {
it('should return false and no delay', async function () { it('should return false and no delay', async function () {
const { immediate, delay } = await this.manager.canCall( const { immediate, delay } = await this.manager.canCall(
someAddress, this.other,
this.target, this.target,
this.calldata.substring(0, 10), this.calldata.substring(0, 10),
); );
@ -763,11 +758,7 @@ contract('AccessManager', function () {
describe('#hashOperation', function () { describe('#hashOperation', function () {
it('returns an operationId', async function () { it('returns an operationId', async function () {
const calldata = '0x123543'; const args = [this.user, this.other, '0x123543'];
const address = someAddress;
const args = [this.user.address, address, calldata];
expect(await this.manager.hashOperation(...args)).to.equal(hashOperation(...args)); expect(await this.manager.hashOperation(...args)).to.equal(hashOperation(...args));
}); });
}); });
@ -980,7 +971,7 @@ contract('AccessManager', function () {
describe('#setTargetAdminDelay', function () { describe('#setTargetAdminDelay', function () {
describe('restrictions', function () { describe('restrictions', function () {
beforeEach('set method and args', function () { beforeEach('set method and args', function () {
const args = [someAddress, time.duration.days(3)]; const args = [this.other.address, time.duration.days(3)];
const method = this.manager.interface.getFunction('setTargetAdminDelay(address,uint32)'); const method = this.manager.interface.getFunction('setTargetAdminDelay(address,uint32)');
this.calldata = this.manager.interface.encodeFunctionData(method, args); this.calldata = this.manager.interface.encodeFunctionData(method, args);
}); });
@ -991,50 +982,48 @@ contract('AccessManager', function () {
describe('when increasing the delay', function () { describe('when increasing the delay', function () {
const oldDelay = time.duration.days(10); const oldDelay = time.duration.days(10);
const newDelay = time.duration.days(11); const newDelay = time.duration.days(11);
const target = someAddress;
beforeEach('sets old delay', async function () { beforeEach('sets old delay', async function () {
await this.manager.$_setTargetAdminDelay(target, oldDelay); await this.manager.$_setTargetAdminDelay(this.other, oldDelay);
await time.increaseBy.timestamp(MINSETBACK); await time.increaseBy.timestamp(MINSETBACK);
expect(await this.manager.getTargetAdminDelay(target)).to.equal(oldDelay); expect(await this.manager.getTargetAdminDelay(this.other)).to.equal(oldDelay);
}); });
it('increases the delay after minsetback', async function () { it('increases the delay after minsetback', async function () {
const txResponse = await this.manager.connect(this.admin).setTargetAdminDelay(target, newDelay); const txResponse = await this.manager.connect(this.admin).setTargetAdminDelay(this.other, newDelay);
const setTargetAdminDelayAt = await time.clockFromReceipt.timestamp(txResponse); const setTargetAdminDelayAt = await time.clockFromReceipt.timestamp(txResponse);
expect(txResponse) expect(txResponse)
.to.emit(this.manager, 'TargetAdminDelayUpdated') .to.emit(this.manager, 'TargetAdminDelayUpdated')
.withArgs(target, newDelay, setTargetAdminDelayAt + MINSETBACK); .withArgs(this.other, newDelay, setTargetAdminDelayAt + MINSETBACK);
expect(await this.manager.getTargetAdminDelay(target)).to.equal(oldDelay); expect(await this.manager.getTargetAdminDelay(this.other)).to.equal(oldDelay);
await time.increaseBy.timestamp(MINSETBACK); await time.increaseBy.timestamp(MINSETBACK);
expect(await this.manager.getTargetAdminDelay(target)).to.equal(newDelay); expect(await this.manager.getTargetAdminDelay(this.other)).to.equal(newDelay);
}); });
}); });
describe('when reducing the delay', function () { describe('when reducing the delay', function () {
const oldDelay = time.duration.days(10); const oldDelay = time.duration.days(10);
const target = someAddress;
beforeEach('sets old delay', async function () { beforeEach('sets old delay', async function () {
await this.manager.$_setTargetAdminDelay(target, oldDelay); await this.manager.$_setTargetAdminDelay(this.other, oldDelay);
await time.increaseBy.timestamp(MINSETBACK); await time.increaseBy.timestamp(MINSETBACK);
expect(await this.manager.getTargetAdminDelay(target)).to.equal(oldDelay); expect(await this.manager.getTargetAdminDelay(this.other)).to.equal(oldDelay);
}); });
describe('when the delay difference is shorter than minimum setback', function () { describe('when the delay difference is shorter than minimum setback', function () {
const newDelay = oldDelay - 1n; const newDelay = oldDelay - 1n;
it('increases the delay after minsetback', async function () { it('increases the delay after minsetback', async function () {
const txResponse = await this.manager.connect(this.admin).setTargetAdminDelay(target, newDelay); const txResponse = await this.manager.connect(this.admin).setTargetAdminDelay(this.other, newDelay);
const setTargetAdminDelayAt = await time.clockFromReceipt.timestamp(txResponse); const setTargetAdminDelayAt = await time.clockFromReceipt.timestamp(txResponse);
expect(txResponse) expect(txResponse)
.to.emit(this.manager, 'TargetAdminDelayUpdated') .to.emit(this.manager, 'TargetAdminDelayUpdated')
.withArgs(target, newDelay, setTargetAdminDelayAt + MINSETBACK); .withArgs(this.other, newDelay, setTargetAdminDelayAt + MINSETBACK);
expect(await this.manager.getTargetAdminDelay(target)).to.equal(oldDelay); expect(await this.manager.getTargetAdminDelay(this.other)).to.equal(oldDelay);
await time.increaseBy.timestamp(MINSETBACK); await time.increaseBy.timestamp(MINSETBACK);
expect(await this.manager.getTargetAdminDelay(target)).to.equal(newDelay); expect(await this.manager.getTargetAdminDelay(this.other)).to.equal(newDelay);
}); });
}); });
@ -1048,16 +1037,16 @@ contract('AccessManager', function () {
it('increases the delay after delay difference', async function () { it('increases the delay after delay difference', async function () {
const setback = oldDelay - newDelay; const setback = oldDelay - newDelay;
const txResponse = await this.manager.connect(this.admin).setTargetAdminDelay(target, newDelay); const txResponse = await this.manager.connect(this.admin).setTargetAdminDelay(this.other, newDelay);
const setTargetAdminDelayAt = await time.clockFromReceipt.timestamp(txResponse); const setTargetAdminDelayAt = await time.clockFromReceipt.timestamp(txResponse);
expect(txResponse) expect(txResponse)
.to.emit(this.manager, 'TargetAdminDelayUpdated') .to.emit(this.manager, 'TargetAdminDelayUpdated')
.withArgs(target, newDelay, setTargetAdminDelayAt + setback); .withArgs(this.other, newDelay, setTargetAdminDelayAt + setback);
expect(await this.manager.getTargetAdminDelay(target)).to.equal(oldDelay); expect(await this.manager.getTargetAdminDelay(this.other)).to.equal(oldDelay);
await time.increaseBy.timestamp(setback); await time.increaseBy.timestamp(setback);
expect(await this.manager.getTargetAdminDelay(target)).to.equal(newDelay); expect(await this.manager.getTargetAdminDelay(this.other)).to.equal(newDelay);
}); });
}); });
}); });
@ -1083,20 +1072,20 @@ contract('AccessManager', function () {
}); });
it('changes the authority', async function () { it('changes the authority', async function () {
expect(await this.newManagedTarget.authority()).to.be.equal(this.manager.target); expect(await this.newManagedTarget.authority()).to.be.equal(this.manager);
await expect(this.manager.connect(this.admin).updateAuthority(this.newManagedTarget, this.newAuthority)) await expect(this.manager.connect(this.admin).updateAuthority(this.newManagedTarget, this.newAuthority))
.to.emit(this.newManagedTarget, 'AuthorityUpdated') // Managed contract is responsible of notifying the change through an event .to.emit(this.newManagedTarget, 'AuthorityUpdated') // Managed contract is responsible of notifying the change through an event
.withArgs(this.newAuthority.target); .withArgs(this.newAuthority);
expect(await this.newManagedTarget.authority()).to.be.equal(this.newAuthority.target); expect(await this.newManagedTarget.authority()).to.be.equal(this.newAuthority);
}); });
}); });
describe('#setTargetClosed', function () { describe('#setTargetClosed', function () {
describe('restrictions', function () { describe('restrictions', function () {
beforeEach('set method and args', function () { beforeEach('set method and args', function () {
const args = [someAddress, true]; const args = [this.other.address, true];
const method = this.manager.interface.getFunction('setTargetClosed(address,bool)'); const method = this.manager.interface.getFunction('setTargetClosed(address,bool)');
this.calldata = this.manager.interface.encodeFunctionData(method, args); this.calldata = this.manager.interface.encodeFunctionData(method, args);
}); });
@ -1107,26 +1096,26 @@ contract('AccessManager', function () {
it('closes and opens a target', async function () { it('closes and opens a target', async function () {
await expect(this.manager.connect(this.admin).setTargetClosed(this.target, true)) await expect(this.manager.connect(this.admin).setTargetClosed(this.target, true))
.to.emit(this.manager, 'TargetClosed') .to.emit(this.manager, 'TargetClosed')
.withArgs(this.target.target, true); .withArgs(this.target, true);
expect(await this.manager.isTargetClosed(this.target)).to.be.true; expect(await this.manager.isTargetClosed(this.target)).to.be.true;
await expect(this.manager.connect(this.admin).setTargetClosed(this.target, false)) await expect(this.manager.connect(this.admin).setTargetClosed(this.target, false))
.to.emit(this.manager, 'TargetClosed') .to.emit(this.manager, 'TargetClosed')
.withArgs(this.target.target, false); .withArgs(this.target, false);
expect(await this.manager.isTargetClosed(this.target)).to.be.false; expect(await this.manager.isTargetClosed(this.target)).to.be.false;
}); });
it('reverts if closing the manager', async function () { it('reverts if closing the manager', async function () {
await expect(this.manager.connect(this.admin).setTargetClosed(this.manager, true)) await expect(this.manager.connect(this.admin).setTargetClosed(this.manager, true))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerLockedAccount') .to.be.revertedWithCustomError(this.manager, 'AccessManagerLockedAccount')
.withArgs(this.manager.target); .withArgs(this.manager);
}); });
}); });
describe('#setTargetFunctionRole', function () { describe('#setTargetFunctionRole', function () {
describe('restrictions', function () { describe('restrictions', function () {
beforeEach('set method and args', function () { beforeEach('set method and args', function () {
const args = [someAddress, ['0x12345678'], 443342]; const args = [this.other.address, ['0x12345678'], 443342];
const method = this.manager.interface.getFunction('setTargetFunctionRole(address,bytes4[],uint64)'); const method = this.manager.interface.getFunction('setTargetFunctionRole(address,bytes4[],uint64)');
this.calldata = this.manager.interface.encodeFunctionData(method, args); this.calldata = this.manager.interface.encodeFunctionData(method, args);
}); });
@ -1148,7 +1137,7 @@ contract('AccessManager', function () {
for (const sig of sigs) { for (const sig of sigs) {
expect(allowRole) expect(allowRole)
.to.emit(this.manager, 'TargetFunctionRoleUpdated') .to.emit(this.manager, 'TargetFunctionRoleUpdated')
.withArgs(this.target.target, sig, this.roles.SOME.id); .withArgs(this.target, sig, this.roles.SOME.id);
expect(await this.manager.getTargetFunctionRole(this.target, sig)).to.equal(this.roles.SOME.id); expect(await this.manager.getTargetFunctionRole(this.target, sig)).to.equal(this.roles.SOME.id);
} }
@ -1156,7 +1145,7 @@ contract('AccessManager', function () {
this.manager.connect(this.admin).setTargetFunctionRole(this.target, [sigs[1]], this.roles.SOME_ADMIN.id), this.manager.connect(this.admin).setTargetFunctionRole(this.target, [sigs[1]], this.roles.SOME_ADMIN.id),
) )
.to.emit(this.manager, 'TargetFunctionRoleUpdated') .to.emit(this.manager, 'TargetFunctionRoleUpdated')
.withArgs(this.target.target, sigs[1], this.roles.SOME_ADMIN.id); .withArgs(this.target, sigs[1], this.roles.SOME_ADMIN.id);
for (const sig of sigs) { for (const sig of sigs) {
expect(await this.manager.getTargetFunctionRole(this.target, sig)).to.equal( expect(await this.manager.getTargetFunctionRole(this.target, sig)).to.equal(
@ -1182,7 +1171,7 @@ contract('AccessManager', function () {
describe('#grantRole', function () { describe('#grantRole', function () {
describe('restrictions', function () { describe('restrictions', function () {
beforeEach('set method and args', function () { beforeEach('set method and args', function () {
const args = [ANOTHER_ROLE, someAddress, 0]; const args = [ANOTHER_ROLE, this.other.address, 0];
const method = this.manager.interface.getFunction('grantRole(uint64,address,uint32)'); const method = this.manager.interface.getFunction('grantRole(uint64,address,uint32)');
this.calldata = this.manager.interface.encodeFunctionData(method, args); this.calldata = this.manager.interface.encodeFunctionData(method, args);
}); });
@ -1291,7 +1280,7 @@ contract('AccessManager', function () {
const grantedAt = await time.clockFromReceipt.timestamp(txResponse); const grantedAt = await time.clockFromReceipt.timestamp(txResponse);
expect(txResponse) expect(txResponse)
.to.emit(this.manager, 'RoleGranted') .to.emit(this.manager, 'RoleGranted')
.withArgs(ANOTHER_ROLE, this.user.address, executionDelay, grantedAt, true); .withArgs(ANOTHER_ROLE, this.user, executionDelay, grantedAt, true);
// Access is correctly stored // Access is correctly stored
const access = await this.manager.getAccess(ANOTHER_ROLE, this.user); const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
@ -1348,7 +1337,7 @@ contract('AccessManager', function () {
expect(txResponse) expect(txResponse)
.to.emit(this.manager, 'RoleGranted') .to.emit(this.manager, 'RoleGranted')
.withArgs(ANOTHER_ROLE, this.user.address, timestamp, this.newExecutionDelay, false); .withArgs(ANOTHER_ROLE, this.user, timestamp, this.newExecutionDelay, false);
// Access is correctly stored // Access is correctly stored
const access = await this.manager.getAccess(ANOTHER_ROLE, this.user); const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
@ -1384,13 +1373,7 @@ contract('AccessManager', function () {
it('emits event', function () { it('emits event', function () {
expect(this.txResponse) expect(this.txResponse)
.to.emit(this.manager, 'RoleGranted') .to.emit(this.manager, 'RoleGranted')
.withArgs( .withArgs(ANOTHER_ROLE, this.user, this.grantTimestamp + this.delay, this.newExecutionDelay, false);
ANOTHER_ROLE,
this.user.address,
this.grantTimestamp + this.delay,
this.newExecutionDelay,
false,
);
}); });
testAsDelay('execution delay effect', { testAsDelay('execution delay effect', {
@ -1465,7 +1448,7 @@ contract('AccessManager', function () {
expect(txResponse) expect(txResponse)
.to.emit(this.manager, 'RoleGranted') .to.emit(this.manager, 'RoleGranted')
.withArgs(ANOTHER_ROLE, this.user.address, timestamp, this.newExecutionDelay, false); .withArgs(ANOTHER_ROLE, this.user, timestamp, this.newExecutionDelay, false);
// Access is correctly stored // Access is correctly stored
const access = await this.manager.getAccess(ANOTHER_ROLE, this.user); const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
@ -1501,13 +1484,7 @@ contract('AccessManager', function () {
it('emits event', function () { it('emits event', function () {
expect(this.txResponse) expect(this.txResponse)
.to.emit(this.manager, 'RoleGranted') .to.emit(this.manager, 'RoleGranted')
.withArgs( .withArgs(ANOTHER_ROLE, this.user, this.grantTimestamp + this.delay, this.newExecutionDelay, false);
ANOTHER_ROLE,
this.user.address,
this.grantTimestamp + this.delay,
this.newExecutionDelay,
false,
);
}); });
testAsDelay('execution delay effect', { testAsDelay('execution delay effect', {
@ -1557,7 +1534,7 @@ contract('AccessManager', function () {
describe('#revokeRole', function () { describe('#revokeRole', function () {
describe('restrictions', function () { describe('restrictions', function () {
beforeEach('set method and args', async function () { beforeEach('set method and args', async function () {
const args = [ANOTHER_ROLE, someAddress]; const args = [ANOTHER_ROLE, this.other.address];
const method = this.manager.interface.getFunction('revokeRole(uint64,address)'); const method = this.manager.interface.getFunction('revokeRole(uint64,address)');
this.calldata = this.manager.interface.encodeFunctionData(method, args); this.calldata = this.manager.interface.encodeFunctionData(method, args);
@ -1588,7 +1565,7 @@ contract('AccessManager', function () {
await expect(this.manager.connect(this.admin).revokeRole(ANOTHER_ROLE, this.user)) await expect(this.manager.connect(this.admin).revokeRole(ANOTHER_ROLE, this.user))
.to.emit(this.manager, 'RoleRevoked') .to.emit(this.manager, 'RoleRevoked')
.withArgs(ANOTHER_ROLE, this.user.address); .withArgs(ANOTHER_ROLE, this.user);
expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([ expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
false, false,
@ -1613,7 +1590,7 @@ contract('AccessManager', function () {
await expect(this.manager.connect(this.admin).revokeRole(ANOTHER_ROLE, this.user)) await expect(this.manager.connect(this.admin).revokeRole(ANOTHER_ROLE, this.user))
.to.emit(this.manager, 'RoleRevoked') .to.emit(this.manager, 'RoleRevoked')
.withArgs(ANOTHER_ROLE, this.user.address); .withArgs(ANOTHER_ROLE, this.user);
expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([ expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
false, false,
@ -1670,7 +1647,7 @@ contract('AccessManager', function () {
]); ]);
await expect(this.manager.connect(this.caller).renounceRole(this.role.id, this.caller)) await expect(this.manager.connect(this.caller).renounceRole(this.role.id, this.caller))
.to.emit(this.manager, 'RoleRevoked') .to.emit(this.manager, 'RoleRevoked')
.withArgs(this.role.id, this.caller.address); .withArgs(this.role.id, this.caller);
expect(await this.manager.hasRole(this.role.id, this.caller).then(formatAccess)).to.be.deep.equal([ expect(await this.manager.hasRole(this.role.id, this.caller).then(formatAccess)).to.be.deep.equal([
false, false,
'0', '0',
@ -1685,7 +1662,7 @@ contract('AccessManager', function () {
it('reverts if renouncing with bad caller confirmation', async function () { it('reverts if renouncing with bad caller confirmation', async function () {
await expect( await expect(
this.manager.connect(this.caller).renounceRole(this.role.id, someAddress), this.manager.connect(this.caller).renounceRole(this.role.id, this.other),
).to.be.revertedWithCustomError(this.manager, 'AccessManagerBadConfirmation'); ).to.be.revertedWithCustomError(this.manager, 'AccessManagerBadConfirmation');
}); });
}); });
@ -1719,7 +1696,7 @@ contract('AccessManager', function () {
}), }),
) )
.to.emit(this.target, 'CalledRestricted') .to.emit(this.target, 'CalledRestricted')
.withArgs(this.user.address); .withArgs(this.user);
}); });
}); });
@ -1742,7 +1719,7 @@ contract('AccessManager', function () {
}), }),
) )
.to.emit(this.target, 'CalledUnrestricted') .to.emit(this.target, 'CalledUnrestricted')
.withArgs(this.user.address); .withArgs(this.user);
}); });
}); });
}); });
@ -1772,7 +1749,7 @@ contract('AccessManager', function () {
}); });
await expect(schedule()) await expect(schedule())
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
}, },
open: { open: {
@ -1790,7 +1767,7 @@ contract('AccessManager', function () {
}); });
await expect(schedule()) await expect(schedule())
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
}, },
}, },
@ -1800,7 +1777,7 @@ contract('AccessManager', function () {
// prepareOperation is not used here because it alters the next block timestamp // prepareOperation is not used here because it alters the next block timestamp
await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48)) await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
}, },
specificRoleIsRequired: { specificRoleIsRequired: {
@ -1812,7 +1789,7 @@ contract('AccessManager', function () {
// prepareOperation is not used here because it alters the next block timestamp // prepareOperation is not used here because it alters the next block timestamp
await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48)) await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
}, },
afterGrantDelay() { afterGrantDelay() {
@ -1828,7 +1805,7 @@ contract('AccessManager', function () {
// prepareOperation is not used here because it alters the next block timestamp // prepareOperation is not used here because it alters the next block timestamp
await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48)) await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
}, },
afterGrantDelay() { afterGrantDelay() {
@ -1836,7 +1813,7 @@ contract('AccessManager', function () {
// prepareOperation is not used here because it alters the next block timestamp // prepareOperation is not used here because it alters the next block timestamp
await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48)) await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
}, },
}, },
@ -1859,7 +1836,7 @@ contract('AccessManager', function () {
// prepareOperation is not used here because it alters the next block timestamp // prepareOperation is not used here because it alters the next block timestamp
await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48)) await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
}, },
}, },
@ -1874,7 +1851,7 @@ contract('AccessManager', function () {
}); });
await expect(schedule()) await expect(schedule())
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
}, },
}, },
@ -1896,7 +1873,7 @@ contract('AccessManager', function () {
expect(await this.manager.getSchedule(operationId)).to.equal(scheduledAt + this.delay); expect(await this.manager.getSchedule(operationId)).to.equal(scheduledAt + this.delay);
expect(txResponse) expect(txResponse)
.to.emit(this.manager, 'OperationScheduled') .to.emit(this.manager, 'OperationScheduled')
.withArgs(operationId, '1', scheduledAt + this.delay, this.target.target, this.calldata); .withArgs(operationId, '1', scheduledAt + this.delay, this.target, this.calldata);
}); });
it('schedules an operation at the minimum execution date if no specified execution date (when == 0)', async function () { it('schedules an operation at the minimum execution date if no specified execution date (when == 0)', async function () {
@ -1911,7 +1888,7 @@ contract('AccessManager', function () {
expect(await this.manager.getSchedule(operationId)).to.equal(scheduledAt + executionDelay); expect(await this.manager.getSchedule(operationId)).to.equal(scheduledAt + executionDelay);
expect(txResponse) expect(txResponse)
.to.emit(this.manager, 'OperationScheduled') .to.emit(this.manager, 'OperationScheduled')
.withArgs(operationId, '1', scheduledAt + executionDelay, this.target.target, this.calldata); .withArgs(operationId, '1', scheduledAt + executionDelay, this.target, this.calldata);
}); });
it('increases the nonce of an operation scheduled more than once', async function () { it('increases the nonce of an operation scheduled more than once', async function () {
@ -1928,14 +1905,7 @@ contract('AccessManager', function () {
}); });
await expect(op1.schedule()) await expect(op1.schedule())
.to.emit(this.manager, 'OperationScheduled') .to.emit(this.manager, 'OperationScheduled')
.withArgs( .withArgs(op1.operationId, 1n, op1.scheduledAt + this.delay, this.caller, this.target, this.calldata);
op1.operationId,
1n,
op1.scheduledAt + this.delay,
this.caller.address,
this.target.target,
this.calldata,
);
expect(expectedOperationId).to.equal(op1.operationId); expect(expectedOperationId).to.equal(op1.operationId);
// Consume // Consume
@ -1954,14 +1924,7 @@ contract('AccessManager', function () {
}); });
await expect(op2.schedule()) await expect(op2.schedule())
.to.emit(this.manager, 'OperationScheduled') .to.emit(this.manager, 'OperationScheduled')
.withArgs( .withArgs(op2.operationId, 2n, op2.scheduledAt + this.delay, this.caller, this.target, this.calldata);
op2.operationId,
2n,
op2.scheduledAt + this.delay,
this.caller.address,
this.target.target,
this.calldata,
);
expect(expectedOperationId).to.equal(op2.operationId); expect(expectedOperationId).to.equal(op2.operationId);
// Check final nonce // Check final nonce
@ -1981,7 +1944,7 @@ contract('AccessManager', function () {
await expect(schedule()) await expect(schedule())
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
it('reverts if an operation is already schedule', async function () { it('reverts if an operation is already schedule', async function () {
@ -2040,7 +2003,7 @@ contract('AccessManager', function () {
await expect(schedule()) await expect(schedule())
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.manager.target, calldata); .withArgs(this.caller, this.manager, calldata);
}); });
}); });
@ -2062,7 +2025,7 @@ contract('AccessManager', function () {
it('reverts as AccessManagerUnauthorizedCall', async function () { it('reverts as AccessManagerUnauthorizedCall', async function () {
await expect(this.manager.connect(this.caller).execute(this.target, this.calldata)) await expect(this.manager.connect(this.caller).execute(this.target, this.calldata))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
}, },
open: { open: {
@ -2076,7 +2039,7 @@ contract('AccessManager', function () {
it('reverts as AccessManagerUnauthorizedCall', async function () { it('reverts as AccessManagerUnauthorizedCall', async function () {
await expect(this.manager.connect(this.caller).execute(this.target, this.calldata)) await expect(this.manager.connect(this.caller).execute(this.target, this.calldata))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
}, },
}, },
@ -2094,7 +2057,7 @@ contract('AccessManager', function () {
it('reverts as AccessManagerUnauthorizedCall', async function () { it('reverts as AccessManagerUnauthorizedCall', async function () {
await expect(this.manager.connect(this.caller).execute(this.target, this.calldata)) await expect(this.manager.connect(this.caller).execute(this.target, this.calldata))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
}, },
afterGrantDelay: function self() { afterGrantDelay: function self() {
@ -2112,7 +2075,7 @@ contract('AccessManager', function () {
it('reverts as AccessManagerUnauthorizedCall', async function () { it('reverts as AccessManagerUnauthorizedCall', async function () {
await expect(this.manager.connect(this.caller).execute(this.target, this.calldata)) await expect(this.manager.connect(this.caller).execute(this.target, this.calldata))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
}, },
afterGrantDelay: function self() { afterGrantDelay: function self() {
@ -2143,7 +2106,7 @@ contract('AccessManager', function () {
it('reverts as AccessManagerUnauthorizedCall', async function () { it('reverts as AccessManagerUnauthorizedCall', async function () {
await expect(this.manager.connect(this.caller).execute(this.target, this.calldata)) await expect(this.manager.connect(this.caller).execute(this.target, this.calldata))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10)); .withArgs(this.caller, this.target, this.calldata.substring(0, 10));
}); });
}, },
}, },
@ -2197,9 +2160,9 @@ contract('AccessManager', function () {
}); });
it('keeps the original _executionId after finishing the call', async function () { it('keeps the original _executionId after finishing the call', async function () {
const executionIdBefore = await getStorageAt(this.manager.target, EXECUTION_ID_STORAGE_SLOT); const executionIdBefore = await ethers.provider.getStorage(this.manager, EXECUTION_ID_STORAGE_SLOT);
await this.manager.connect(this.caller).execute(this.target, this.calldata); await this.manager.connect(this.caller).execute(this.target, this.calldata);
const executionIdAfter = await getStorageAt(this.manager.target, EXECUTION_ID_STORAGE_SLOT); const executionIdAfter = await ethers.provider.getStorage(this.manager, EXECUTION_ID_STORAGE_SLOT);
expect(executionIdBefore).to.equal(executionIdAfter); expect(executionIdBefore).to.equal(executionIdAfter);
}); });
@ -2244,7 +2207,7 @@ contract('AccessManager', function () {
it('reverts as AccessManagerUnauthorizedConsume', async function () { it('reverts as AccessManagerUnauthorizedConsume', async function () {
await expect(this.manager.connect(this.caller).consumeScheduledOp(this.caller, this.calldata)) await expect(this.manager.connect(this.caller).consumeScheduledOp(this.caller, this.calldata))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedConsume') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedConsume')
.withArgs(this.caller.address); .withArgs(this.caller);
}); });
}); });
@ -2329,7 +2292,7 @@ contract('AccessManager', function () {
it('reverts as AccessManagerUnauthorizedCancel', async function () { it('reverts as AccessManagerUnauthorizedCancel', async function () {
await expect(this.manager.connect(this.other).cancel(this.caller, this.target, this.calldata)) await expect(this.manager.connect(this.other).cancel(this.caller, this.target, this.calldata))
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCancel') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCancel')
.withArgs(this.other.address, this.caller.address, this.target.target, this.method.selector); .withArgs(this.other, this.caller, this.target, this.method.selector);
}); });
}); });
}, },
@ -2379,7 +2342,7 @@ contract('AccessManager', function () {
}); });
it('initial state', async function () { it('initial state', async function () {
expect(await this.ownable.owner()).to.be.equal(this.manager.target); expect(await this.ownable.owner()).to.be.equal(this.manager);
}); });
describe('Contract is closed', function () { describe('Contract is closed', function () {
@ -2390,7 +2353,7 @@ contract('AccessManager', function () {
it('directly call: reverts', async function () { it('directly call: reverts', async function () {
await expect(this.ownable.connect(this.user).$_checkOwner()) await expect(this.ownable.connect(this.user).$_checkOwner())
.to.be.revertedWithCustomError(this.ownable, 'OwnableUnauthorizedAccount') .to.be.revertedWithCustomError(this.ownable, 'OwnableUnauthorizedAccount')
.withArgs(this.user.address); .withArgs(this.user);
}); });
it('relayed call (with role): reverts', async function () { it('relayed call (with role): reverts', async function () {
@ -2398,7 +2361,7 @@ contract('AccessManager', function () {
this.manager.connect(this.user).execute(this.ownable, this.ownable.$_checkOwner.getFragment().selector), this.manager.connect(this.user).execute(this.ownable, this.ownable.$_checkOwner.getFragment().selector),
) )
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.user.address, this.ownable.target, this.ownable.$_checkOwner.getFragment().selector); .withArgs(this.user, this.ownable, this.ownable.$_checkOwner.getFragment().selector);
}); });
it('relayed call (without role): reverts', async function () { it('relayed call (without role): reverts', async function () {
@ -2406,7 +2369,7 @@ contract('AccessManager', function () {
this.manager.connect(this.other).execute(this.ownable, this.ownable.$_checkOwner.getFragment().selector), this.manager.connect(this.other).execute(this.ownable, this.ownable.$_checkOwner.getFragment().selector),
) )
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.other.address, this.ownable.target, this.ownable.$_checkOwner.getFragment().selector); .withArgs(this.other, this.ownable, this.ownable.$_checkOwner.getFragment().selector);
}); });
}); });
@ -2423,7 +2386,7 @@ contract('AccessManager', function () {
it('directly call: reverts', async function () { it('directly call: reverts', async function () {
await expect(this.ownable.connect(this.user).$_checkOwner()) await expect(this.ownable.connect(this.user).$_checkOwner())
.to.be.revertedWithCustomError(this.ownable, 'OwnableUnauthorizedAccount') .to.be.revertedWithCustomError(this.ownable, 'OwnableUnauthorizedAccount')
.withArgs(this.user.address); .withArgs(this.user);
}); });
it('relayed call (with role): success', async function () { it('relayed call (with role): success', async function () {
@ -2435,7 +2398,7 @@ contract('AccessManager', function () {
this.manager.connect(this.other).execute(this.ownable, this.ownable.$_checkOwner.getFragment().selector), this.manager.connect(this.other).execute(this.ownable, this.ownable.$_checkOwner.getFragment().selector),
) )
.to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall') .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
.withArgs(this.other.address, this.ownable.target, this.ownable.$_checkOwner.getFragment().selector); .withArgs(this.other, this.ownable, this.ownable.$_checkOwner.getFragment().selector);
}); });
}); });
@ -2451,7 +2414,7 @@ contract('AccessManager', function () {
it('directly call: reverts', async function () { it('directly call: reverts', async function () {
await expect(this.ownable.connect(this.user).$_checkOwner()) await expect(this.ownable.connect(this.user).$_checkOwner())
.to.be.revertedWithCustomError(this.ownable, 'OwnableUnauthorizedAccount') .to.be.revertedWithCustomError(this.ownable, 'OwnableUnauthorizedAccount')
.withArgs(this.user.address); .withArgs(this.user);
}); });
it('relayed call (with role): success', async function () { it('relayed call (with role): success', async function () {

@ -1,4 +1,5 @@
const { ethers } = require('hardhat'); const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
async function fixture() { async function fixture() {

@ -1,5 +1,5 @@
const { expect } = require('chai'); const { expect } = require('chai');
const { bigint: time } = require('../helpers/time'); const time = require('../helpers/time');
function shouldBehaveLikeVesting() { function shouldBehaveLikeVesting() {
it('check vesting schedule', async function () { it('check vesting schedule', async function () {

@ -3,7 +3,7 @@ const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { min } = require('../helpers/math'); const { min } = require('../helpers/math');
const { bigint: time } = require('../helpers/time'); const time = require('../helpers/time');
const { shouldBehaveLikeVesting } = require('./VestingWallet.behavior'); const { shouldBehaveLikeVesting } = require('./VestingWallet.behavior');
@ -39,7 +39,7 @@ async function fixture() {
}, },
token: { token: {
checkRelease: async (tx, amount) => { checkRelease: async (tx, amount) => {
await expect(tx).to.emit(token, 'Transfer').withArgs(mock.target, beneficiary.address, amount); await expect(tx).to.emit(token, 'Transfer').withArgs(mock, beneficiary, amount);
await expect(tx).to.changeTokenBalances(token, [mock, beneficiary], [-amount, amount]); await expect(tx).to.changeTokenBalances(token, [mock, beneficiary], [-amount, amount]);
}, },
setupFailure: async () => { setupFailure: async () => {
@ -50,8 +50,8 @@ async function fixture() {
}; };
}, },
releasedEvent: 'ERC20Released', releasedEvent: 'ERC20Released',
argsVerify: [token.target], argsVerify: [token],
args: [ethers.Typed.address(token.target)], args: [ethers.Typed.address(token)],
}, },
}; };
@ -76,7 +76,7 @@ describe('VestingWallet', function () {
}); });
it('check vesting contract', async function () { it('check vesting contract', async function () {
expect(await this.mock.owner()).to.be.equal(this.beneficiary.address); expect(await this.mock.owner()).to.be.equal(this.beneficiary);
expect(await this.mock.start()).to.be.equal(this.start); expect(await this.mock.start()).to.be.equal(this.start);
expect(await this.mock.duration()).to.be.equal(this.duration); expect(await this.mock.duration()).to.be.equal(this.duration);
expect(await this.mock.end()).to.be.equal(this.start + this.duration); expect(await this.mock.end()).to.be.equal(this.start + this.duration);

@ -4,8 +4,8 @@ const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { GovernorHelper } = require('../helpers/governance'); const { GovernorHelper } = require('../helpers/governance');
const { getDomain, Ballot } = require('../helpers/eip712'); const { getDomain, Ballot } = require('../helpers/eip712');
const { bigint: Enums } = require('../helpers/enums'); const { ProposalState, VoteType } = require('../helpers/enums');
const { bigint: time } = require('../helpers/time'); const time = require('../helpers/time');
const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior'); const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior');
const { shouldBehaveLikeERC6372 } = require('./utils/ERC6372.behavior'); const { shouldBehaveLikeERC6372 } = require('./utils/ERC6372.behavior');
@ -101,7 +101,7 @@ describe('Governor', function () {
it('deployment check', async function () { it('deployment check', async function () {
expect(await this.mock.name()).to.equal(name); expect(await this.mock.name()).to.equal(name);
expect(await this.mock.token()).to.equal(this.token.target); expect(await this.mock.token()).to.equal(this.token);
expect(await this.mock.votingDelay()).to.equal(votingDelay); expect(await this.mock.votingDelay()).to.equal(votingDelay);
expect(await this.mock.votingPeriod()).to.equal(votingPeriod); expect(await this.mock.votingPeriod()).to.equal(votingPeriod);
expect(await this.mock.quorum(0)).to.equal(0n); expect(await this.mock.quorum(0)).to.equal(0n);
@ -128,7 +128,7 @@ describe('Governor', function () {
.to.emit(this.mock, 'ProposalCreated') .to.emit(this.mock, 'ProposalCreated')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
this.proposer.address, this.proposer,
this.proposal.targets, this.proposal.targets,
this.proposal.values, this.proposal.values,
this.proposal.signatures, this.proposal.signatures,
@ -140,21 +140,21 @@ describe('Governor', function () {
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await expect(this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For, reason: 'This is nice' })) await expect(this.helper.connect(this.voter1).vote({ support: VoteType.For, reason: 'This is nice' }))
.to.emit(this.mock, 'VoteCast') .to.emit(this.mock, 'VoteCast')
.withArgs(this.voter1.address, this.proposal.id, Enums.VoteType.For, ethers.parseEther('10'), 'This is nice'); .withArgs(this.voter1, this.proposal.id, VoteType.For, ethers.parseEther('10'), 'This is nice');
await expect(this.helper.connect(this.voter2).vote({ support: Enums.VoteType.For })) await expect(this.helper.connect(this.voter2).vote({ support: VoteType.For }))
.to.emit(this.mock, 'VoteCast') .to.emit(this.mock, 'VoteCast')
.withArgs(this.voter2.address, this.proposal.id, Enums.VoteType.For, ethers.parseEther('7'), ''); .withArgs(this.voter2, this.proposal.id, VoteType.For, ethers.parseEther('7'), '');
await expect(this.helper.connect(this.voter3).vote({ support: Enums.VoteType.Against })) await expect(this.helper.connect(this.voter3).vote({ support: VoteType.Against }))
.to.emit(this.mock, 'VoteCast') .to.emit(this.mock, 'VoteCast')
.withArgs(this.voter3.address, this.proposal.id, Enums.VoteType.Against, ethers.parseEther('5'), ''); .withArgs(this.voter3, this.proposal.id, VoteType.Against, ethers.parseEther('5'), '');
await expect(this.helper.connect(this.voter4).vote({ support: Enums.VoteType.Abstain })) await expect(this.helper.connect(this.voter4).vote({ support: VoteType.Abstain }))
.to.emit(this.mock, 'VoteCast') .to.emit(this.mock, 'VoteCast')
.withArgs(this.voter4.address, this.proposal.id, Enums.VoteType.Abstain, ethers.parseEther('2'), ''); .withArgs(this.voter4, this.proposal.id, VoteType.Abstain, ethers.parseEther('2'), '');
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
@ -165,7 +165,7 @@ describe('Governor', function () {
await expect(txExecute).to.emit(this.receiver, 'MockFunctionCalled'); await expect(txExecute).to.emit(this.receiver, 'MockFunctionCalled');
// After // After
expect(await this.mock.proposalProposer(this.proposal.id)).to.equal(this.proposer.address); expect(await this.mock.proposalProposer(this.proposal.id)).to.equal(this.proposer);
expect(await this.mock.hasVoted(this.proposal.id, this.owner)).to.be.false; expect(await this.mock.hasVoted(this.proposal.id, this.owner)).to.be.false;
expect(await this.mock.hasVoted(this.proposal.id, this.voter1)).to.be.true; expect(await this.mock.hasVoted(this.proposal.id, this.voter1)).to.be.true;
expect(await this.mock.hasVoted(this.proposal.id, this.voter2)).to.be.true; expect(await this.mock.hasVoted(this.proposal.id, this.voter2)).to.be.true;
@ -191,7 +191,7 @@ describe('Governor', function () {
await expect(async () => { await expect(async () => {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
return this.helper.execute(); return this.helper.execute();
}).to.changeEtherBalances([this.mock, this.userEOA], [-value, value]); }).to.changeEtherBalances([this.mock, this.userEOA], [-value, value]);
@ -208,14 +208,14 @@ describe('Governor', function () {
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await expect( await expect(
this.helper.vote({ this.helper.vote({
support: Enums.VoteType.For, support: VoteType.For,
voter: this.userEOA.address, voter: this.userEOA.address,
nonce, nonce,
signature: signBallot(this.userEOA), signature: signBallot(this.userEOA),
}), }),
) )
.to.emit(this.mock, 'VoteCast') .to.emit(this.mock, 'VoteCast')
.withArgs(this.userEOA.address, this.proposal.id, Enums.VoteType.For, ethers.parseEther('10'), ''); .withArgs(this.userEOA, this.proposal.id, VoteType.For, ethers.parseEther('10'), '');
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.execute(); await this.helper.execute();
@ -237,14 +237,14 @@ describe('Governor', function () {
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await expect( await expect(
this.helper.vote({ this.helper.vote({
support: Enums.VoteType.For, support: VoteType.For,
voter: wallet.target, voter: wallet.target,
nonce, nonce,
signature: signBallot(this.userEOA), signature: signBallot(this.userEOA),
}), }),
) )
.to.emit(this.mock, 'VoteCast') .to.emit(this.mock, 'VoteCast')
.withArgs(wallet.target, this.proposal.id, Enums.VoteType.For, ethers.parseEther('10'), ''); .withArgs(wallet, this.proposal.id, VoteType.For, ethers.parseEther('10'), '');
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.execute(); await this.helper.execute();
@ -266,7 +266,7 @@ describe('Governor', function () {
await this.helper.propose(); await this.helper.propose();
await expect(this.helper.propose()) await expect(this.helper.propose())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs(this.proposal.id, Enums.ProposalState.Pending, ethers.ZeroHash); .withArgs(this.proposal.id, ProposalState.Pending, ethers.ZeroHash);
}); });
it('if proposer has below threshold votes', async function () { it('if proposer has below threshold votes', async function () {
@ -275,25 +275,25 @@ describe('Governor', function () {
await this.mock.$_setProposalThreshold(threshold); await this.mock.$_setProposalThreshold(threshold);
await expect(this.helper.connect(this.voter1).propose()) await expect(this.helper.connect(this.voter1).propose())
.to.be.revertedWithCustomError(this.mock, 'GovernorInsufficientProposerVotes') .to.be.revertedWithCustomError(this.mock, 'GovernorInsufficientProposerVotes')
.withArgs(this.voter1.address, votes, threshold); .withArgs(this.voter1, votes, threshold);
}); });
}); });
describe('on vote', function () { describe('on vote', function () {
it('if proposal does not exist', async function () { it('if proposal does not exist', async function () {
await expect(this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For })) await expect(this.helper.connect(this.voter1).vote({ support: VoteType.For }))
.to.be.revertedWithCustomError(this.mock, 'GovernorNonexistentProposal') .to.be.revertedWithCustomError(this.mock, 'GovernorNonexistentProposal')
.withArgs(this.proposal.id); .withArgs(this.proposal.id);
}); });
it('if voting has not started', async function () { it('if voting has not started', async function () {
await this.helper.propose(); await this.helper.propose();
await expect(this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For })) await expect(this.helper.connect(this.voter1).vote({ support: VoteType.For }))
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Pending, ProposalState.Pending,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Active]), GovernorHelper.proposalStatesToBitMap([ProposalState.Active]),
); );
}); });
@ -309,21 +309,21 @@ describe('Governor', function () {
it('if vote was already casted', async function () { it('if vote was already casted', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await expect(this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For })) await expect(this.helper.connect(this.voter1).vote({ support: VoteType.For }))
.to.be.revertedWithCustomError(this.mock, 'GovernorAlreadyCastVote') .to.be.revertedWithCustomError(this.mock, 'GovernorAlreadyCastVote')
.withArgs(this.voter1.address); .withArgs(this.voter1);
}); });
it('if voting is over', async function () { it('if voting is over', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For })) await expect(this.helper.connect(this.voter1).vote({ support: VoteType.For }))
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Defeated, ProposalState.Defeated,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Active]), GovernorHelper.proposalStatesToBitMap([ProposalState.Active]),
); );
}); });
}); });
@ -341,13 +341,13 @@ describe('Governor', function () {
const nonce = await this.mock.nonces(this.userEOA); const nonce = await this.mock.nonces(this.userEOA);
function tamper(str, index, mask) { function tamper(str, index, mask) {
const arrayStr = ethers.toBeArray(BigInt(str)); const arrayStr = ethers.getBytes(str);
arrayStr[index] ^= mask; arrayStr[index] ^= mask;
return ethers.hexlify(arrayStr); return ethers.hexlify(arrayStr);
} }
const voteParams = { const voteParams = {
support: Enums.VoteType.For, support: VoteType.For,
voter: this.userEOA.address, voter: this.userEOA.address,
nonce, nonce,
signature: (...args) => signBallot(this.userEOA)(...args).then(sig => tamper(sig, 42, 0xff)), signature: (...args) => signBallot(this.userEOA)(...args).then(sig => tamper(sig, 42, 0xff)),
@ -362,7 +362,7 @@ describe('Governor', function () {
const nonce = await this.mock.nonces(this.userEOA); const nonce = await this.mock.nonces(this.userEOA);
const voteParams = { const voteParams = {
support: Enums.VoteType.For, support: VoteType.For,
voter: this.userEOA.address, voter: this.userEOA.address,
nonce: nonce + 1n, nonce: nonce + 1n,
signature: signBallot(this.userEOA), signature: signBallot(this.userEOA),
@ -378,7 +378,7 @@ describe('Governor', function () {
it('always', async function () { it('always', async function () {
await this.helper.connect(this.proposer).propose(); await this.helper.connect(this.proposer).propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.queue()).to.be.revertedWithCustomError(this.mock, 'GovernorQueueNotImplemented'); await expect(this.helper.queue()).to.be.revertedWithCustomError(this.mock, 'GovernorQueueNotImplemented');
}); });
@ -394,39 +394,39 @@ describe('Governor', function () {
it('if quorum is not reached', async function () { it('if quorum is not reached', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter3).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter3).vote({ support: VoteType.For });
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Active, ProposalState.Active,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
it('if score not reached', async function () { it('if score not reached', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.Against }); await this.helper.connect(this.voter1).vote({ support: VoteType.Against });
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Active, ProposalState.Active,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
it('if voting is not over', async function () { it('if voting is not over', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Active, ProposalState.Active,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
@ -443,7 +443,7 @@ describe('Governor', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()).to.be.revertedWithCustomError(this.mock, 'FailedInnerCall'); await expect(this.helper.execute()).to.be.revertedWithCustomError(this.mock, 'FailedInnerCall');
}); });
@ -461,7 +461,7 @@ describe('Governor', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()).to.be.revertedWith('CallReceiverMock: reverting'); await expect(this.helper.execute()).to.be.revertedWith('CallReceiverMock: reverting');
}); });
@ -469,15 +469,15 @@ describe('Governor', function () {
it('if proposal was already executed', async function () { it('if proposal was already executed', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.execute(); await this.helper.execute();
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Executed, ProposalState.Executed,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
}); });
@ -492,38 +492,38 @@ describe('Governor', function () {
it('Pending & Active', async function () { it('Pending & Active', async function () {
await this.helper.propose(); await this.helper.propose();
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Pending); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Pending);
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Pending); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Pending);
await this.helper.waitForSnapshot(1n); await this.helper.waitForSnapshot(1n);
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Active); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Active);
}); });
it('Defeated', async function () { it('Defeated', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Active); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Active);
await this.helper.waitForDeadline(1n); await this.helper.waitForDeadline(1n);
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Defeated); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Defeated);
}); });
it('Succeeded', async function () { it('Succeeded', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Active); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Active);
await this.helper.waitForDeadline(1n); await this.helper.waitForDeadline(1n);
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Succeeded); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Succeeded);
}); });
it('Executed', async function () { it('Executed', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.execute(); await this.helper.execute();
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Executed); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Executed);
}); });
}); });
@ -539,58 +539,58 @@ describe('Governor', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.cancel('internal'); await this.helper.cancel('internal');
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Canceled); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Canceled);
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await expect(this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For })) await expect(this.helper.connect(this.voter1).vote({ support: VoteType.For }))
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Canceled, ProposalState.Canceled,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Active]), GovernorHelper.proposalStatesToBitMap([ProposalState.Active]),
); );
}); });
it('after vote', async function () { it('after vote', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.cancel('internal'); await this.helper.cancel('internal');
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Canceled); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Canceled);
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Canceled, ProposalState.Canceled,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
it('after deadline', async function () { it('after deadline', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.cancel('internal'); await this.helper.cancel('internal');
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Canceled); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Canceled);
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Canceled, ProposalState.Canceled,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
it('after execution', async function () { it('after execution', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.execute(); await this.helper.execute();
@ -598,9 +598,9 @@ describe('Governor', function () {
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Executed, ProposalState.Executed,
GovernorHelper.proposalStatesToBitMap( GovernorHelper.proposalStatesToBitMap(
[Enums.ProposalState.Canceled, Enums.ProposalState.Expired, Enums.ProposalState.Executed], [ProposalState.Canceled, ProposalState.Expired, ProposalState.Executed],
{ inverted: true }, { inverted: true },
), ),
); );
@ -625,7 +625,7 @@ describe('Governor', function () {
await expect(this.helper.connect(this.owner).cancel('external')) await expect(this.helper.connect(this.owner).cancel('external'))
.to.be.revertedWithCustomError(this.mock, 'GovernorOnlyProposer') .to.be.revertedWithCustomError(this.mock, 'GovernorOnlyProposer')
.withArgs(this.owner.address); .withArgs(this.owner);
}); });
it('after vote started', async function () { it('after vote started', async function () {
@ -636,44 +636,44 @@ describe('Governor', function () {
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Active, ProposalState.Active,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Pending]), GovernorHelper.proposalStatesToBitMap([ProposalState.Pending]),
); );
}); });
it('after vote', async function () { it('after vote', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await expect(this.helper.cancel('external')) await expect(this.helper.cancel('external'))
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Active, ProposalState.Active,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Pending]), GovernorHelper.proposalStatesToBitMap([ProposalState.Pending]),
); );
}); });
it('after deadline', async function () { it('after deadline', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.cancel('external')) await expect(this.helper.cancel('external'))
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Succeeded, ProposalState.Succeeded,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Pending]), GovernorHelper.proposalStatesToBitMap([ProposalState.Pending]),
); );
}); });
it('after execution', async function () { it('after execution', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.execute(); await this.helper.execute();
@ -681,8 +681,8 @@ describe('Governor', function () {
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Executed, ProposalState.Executed,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Pending]), GovernorHelper.proposalStatesToBitMap([ProposalState.Pending]),
); );
}); });
}); });
@ -749,7 +749,7 @@ describe('Governor', function () {
.to.emit(this.mock, 'ProposalCreated') .to.emit(this.mock, 'ProposalCreated')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
this.proposer.address, this.proposer,
this.proposal.targets, this.proposal.targets,
this.proposal.values, this.proposal.values,
this.proposal.signatures, this.proposal.signatures,
@ -767,7 +767,7 @@ describe('Governor', function () {
.to.emit(this.mock, 'ProposalCreated') .to.emit(this.mock, 'ProposalCreated')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
this.voter1.address, this.voter1,
this.proposal.targets, this.proposal.targets,
this.proposal.values, this.proposal.values,
this.proposal.signatures, this.proposal.signatures,
@ -841,19 +841,19 @@ describe('Governor', function () {
it('setVotingDelay is protected', async function () { it('setVotingDelay is protected', async function () {
await expect(this.mock.connect(this.owner).setVotingDelay(0n)) await expect(this.mock.connect(this.owner).setVotingDelay(0n))
.to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor') .to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor')
.withArgs(this.owner.address); .withArgs(this.owner);
}); });
it('setVotingPeriod is protected', async function () { it('setVotingPeriod is protected', async function () {
await expect(this.mock.connect(this.owner).setVotingPeriod(32n)) await expect(this.mock.connect(this.owner).setVotingPeriod(32n))
.to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor') .to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor')
.withArgs(this.owner.address); .withArgs(this.owner);
}); });
it('setProposalThreshold is protected', async function () { it('setProposalThreshold is protected', async function () {
await expect(this.mock.connect(this.owner).setProposalThreshold(1_000_000_000_000_000_000n)) await expect(this.mock.connect(this.owner).setProposalThreshold(1_000_000_000_000_000_000n))
.to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor') .to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor')
.withArgs(this.owner.address); .withArgs(this.owner);
}); });
it('can setVotingDelay through governance', async function () { it('can setVotingDelay through governance', async function () {
@ -869,7 +869,7 @@ describe('Governor', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()).to.emit(this.mock, 'VotingDelaySet').withArgs(4n, 0n); await expect(this.helper.execute()).to.emit(this.mock, 'VotingDelaySet').withArgs(4n, 0n);
@ -890,7 +890,7 @@ describe('Governor', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()).to.emit(this.mock, 'VotingPeriodSet').withArgs(16n, 32n); await expect(this.helper.execute()).to.emit(this.mock, 'VotingPeriodSet').withArgs(16n, 32n);
@ -913,7 +913,7 @@ describe('Governor', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()) await expect(this.helper.execute())
@ -934,7 +934,7 @@ describe('Governor', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()) await expect(this.helper.execute())
@ -955,7 +955,7 @@ describe('Governor', function () {
}); });
it('can receive an ERC721 safeTransfer', async function () { it('can receive an ERC721 safeTransfer', async function () {
await this.token.connect(this.owner).safeTransferFrom(this.owner, this.mock.target, tokenId); await this.token.connect(this.owner).safeTransferFrom(this.owner, this.mock, tokenId);
}); });
}); });
@ -974,7 +974,7 @@ describe('Governor', function () {
it('can receive ERC1155 safeTransfer', async function () { it('can receive ERC1155 safeTransfer', async function () {
await this.token.connect(this.owner).safeTransferFrom( await this.token.connect(this.owner).safeTransferFrom(
this.owner, this.owner,
this.mock.target, this.mock,
...Object.entries(tokenIds)[0], // id + amount ...Object.entries(tokenIds)[0], // id + amount
'0x', '0x',
); );
@ -983,13 +983,7 @@ describe('Governor', function () {
it('can receive ERC1155 safeBatchTransfer', async function () { it('can receive ERC1155 safeBatchTransfer', async function () {
await this.token await this.token
.connect(this.owner) .connect(this.owner)
.safeBatchTransferFrom( .safeBatchTransferFrom(this.owner, this.mock, Object.keys(tokenIds), Object.values(tokenIds), '0x');
this.owner,
this.mock.target,
Object.keys(tokenIds),
Object.values(tokenIds),
'0x',
);
}); });
}); });
}); });

@ -4,10 +4,8 @@ const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic');
const { GovernorHelper } = require('../helpers/governance'); const { GovernorHelper } = require('../helpers/governance');
const { bigint: time } = require('../helpers/time'); const { OperationState } = require('../helpers/enums');
const { const time = require('../helpers/time');
bigint: { OperationState },
} = require('../helpers/enums');
const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior'); const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior');
@ -234,7 +232,7 @@ describe('TimelockController', function () {
), ),
) )
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.other.address, PROPOSER_ROLE); .withArgs(this.other, PROPOSER_ROLE);
}); });
it('enforce minimum delay', async function () { it('enforce minimum delay', async function () {
@ -380,7 +378,7 @@ describe('TimelockController', function () {
), ),
) )
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.other.address, EXECUTOR_ROLE); .withArgs(this.other, EXECUTOR_ROLE);
}); });
it('prevents reentrancy execution', async function () { it('prevents reentrancy execution', async function () {
@ -457,7 +455,7 @@ describe('TimelockController', function () {
.withArgs( .withArgs(
nonReentrantOperation.id, nonReentrantOperation.id,
0n, 0n,
getAddress(nonReentrantOperation.target), getAddress(nonReentrantOperation),
nonReentrantOperation.value, nonReentrantOperation.value,
nonReentrantOperation.data, nonReentrantOperation.data,
); );
@ -587,7 +585,7 @@ describe('TimelockController', function () {
), ),
) )
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.other.address, PROPOSER_ROLE); .withArgs(this.other, PROPOSER_ROLE);
}); });
it('enforce minimum delay', async function () { it('enforce minimum delay', async function () {
@ -725,7 +723,7 @@ describe('TimelockController', function () {
), ),
) )
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.other.address, EXECUTOR_ROLE); .withArgs(this.other, EXECUTOR_ROLE);
}); });
it('length mismatch #1', async function () { it('length mismatch #1', async function () {
@ -939,7 +937,7 @@ describe('TimelockController', function () {
it('prevent non-canceller from canceling', async function () { it('prevent non-canceller from canceling', async function () {
await expect(this.mock.connect(this.other).cancel(this.operation.id)) await expect(this.mock.connect(this.other).cancel(this.operation.id))
.to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount')
.withArgs(this.other.address, CANCELLER_ROLE); .withArgs(this.other, CANCELLER_ROLE);
}); });
}); });
}); });
@ -948,7 +946,7 @@ describe('TimelockController', function () {
it('prevent unauthorized maintenance', async function () { it('prevent unauthorized maintenance', async function () {
await expect(this.mock.connect(this.other).updateDelay(0n)) await expect(this.mock.connect(this.other).updateDelay(0n))
.to.be.revertedWithCustomError(this.mock, 'TimelockUnauthorizedCaller') .to.be.revertedWithCustomError(this.mock, 'TimelockUnauthorizedCaller')
.withArgs(this.other.address); .withArgs(this.other);
}); });
it('timelock scheduled maintenance', async function () { it('timelock scheduled maintenance', async function () {

@ -3,7 +3,7 @@ const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { GovernorHelper } = require('../../helpers/governance'); const { GovernorHelper } = require('../../helpers/governance');
const { bigint: Enums } = require('../../helpers/enums'); const { VoteType } = require('../../helpers/enums');
const TOKENS = [ const TOKENS = [
{ Token: '$ERC721Votes', mode: 'blocknumber' }, { Token: '$ERC721Votes', mode: 'blocknumber' },
@ -80,7 +80,7 @@ describe('GovernorERC721', function () {
it('deployment check', async function () { it('deployment check', async function () {
expect(await this.mock.name()).to.equal(name); expect(await this.mock.name()).to.equal(name);
expect(await this.mock.token()).to.equal(this.token.target); expect(await this.mock.token()).to.equal(this.token);
expect(await this.mock.votingDelay()).to.equal(votingDelay); expect(await this.mock.votingDelay()).to.equal(votingDelay);
expect(await this.mock.votingPeriod()).to.equal(votingPeriod); expect(await this.mock.votingPeriod()).to.equal(votingPeriod);
expect(await this.mock.quorum(0n)).to.equal(0n); expect(await this.mock.quorum(0n)).to.equal(0n);
@ -95,21 +95,21 @@ describe('GovernorERC721', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await expect(this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For })) await expect(this.helper.connect(this.voter1).vote({ support: VoteType.For }))
.to.emit(this.mock, 'VoteCast') .to.emit(this.mock, 'VoteCast')
.withArgs(this.voter1.address, this.proposal.id, Enums.VoteType.For, 1n, ''); .withArgs(this.voter1, this.proposal.id, VoteType.For, 1n, '');
await expect(this.helper.connect(this.voter2).vote({ support: Enums.VoteType.For })) await expect(this.helper.connect(this.voter2).vote({ support: VoteType.For }))
.to.emit(this.mock, 'VoteCast') .to.emit(this.mock, 'VoteCast')
.withArgs(this.voter2.address, this.proposal.id, Enums.VoteType.For, 2n, ''); .withArgs(this.voter2, this.proposal.id, VoteType.For, 2n, '');
await expect(this.helper.connect(this.voter3).vote({ support: Enums.VoteType.Against })) await expect(this.helper.connect(this.voter3).vote({ support: VoteType.Against }))
.to.emit(this.mock, 'VoteCast') .to.emit(this.mock, 'VoteCast')
.withArgs(this.voter3.address, this.proposal.id, Enums.VoteType.Against, 1n, ''); .withArgs(this.voter3, this.proposal.id, VoteType.Against, 1n, '');
await expect(this.helper.connect(this.voter4).vote({ support: Enums.VoteType.Abstain })) await expect(this.helper.connect(this.voter4).vote({ support: VoteType.Abstain }))
.to.emit(this.mock, 'VoteCast') .to.emit(this.mock, 'VoteCast')
.withArgs(this.voter4.address, this.proposal.id, Enums.VoteType.Abstain, 1n, ''); .withArgs(this.voter4, this.proposal.id, VoteType.Abstain, 1n, '');
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.execute(); await this.helper.execute();

@ -3,8 +3,8 @@ const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { GovernorHelper } = require('../../helpers/governance'); const { GovernorHelper } = require('../../helpers/governance');
const { bigint: Enums } = require('../../helpers/enums'); const { ProposalState, VoteType } = require('../../helpers/enums');
const { bigint: time } = require('../../helpers/time'); const time = require('../../helpers/time');
const TOKENS = [ const TOKENS = [
{ Token: '$ERC20Votes', mode: 'blocknumber' }, { Token: '$ERC20Votes', mode: 'blocknumber' },
@ -69,7 +69,7 @@ describe('GovernorPreventLateQuorum', function () {
it('deployment check', async function () { it('deployment check', async function () {
expect(await this.mock.name()).to.equal(name); expect(await this.mock.name()).to.equal(name);
expect(await this.mock.token()).to.equal(this.token.target); expect(await this.mock.token()).to.equal(this.token);
expect(await this.mock.votingDelay()).to.equal(votingDelay); expect(await this.mock.votingDelay()).to.equal(votingDelay);
expect(await this.mock.votingPeriod()).to.equal(votingPeriod); expect(await this.mock.votingPeriod()).to.equal(votingPeriod);
expect(await this.mock.quorum(0)).to.equal(quorum); expect(await this.mock.quorum(0)).to.equal(quorum);
@ -79,10 +79,10 @@ describe('GovernorPreventLateQuorum', function () {
it('nominal workflow unaffected', async function () { it('nominal workflow unaffected', async function () {
const txPropose = await this.helper.connect(this.proposer).propose(); const txPropose = await this.helper.connect(this.proposer).propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.connect(this.voter2).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter2).vote({ support: VoteType.For });
await this.helper.connect(this.voter3).vote({ support: Enums.VoteType.Against }); await this.helper.connect(this.voter3).vote({ support: VoteType.Against });
await this.helper.connect(this.voter4).vote({ support: Enums.VoteType.Abstain }); await this.helper.connect(this.voter4).vote({ support: VoteType.Abstain });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.execute(); await this.helper.execute();
@ -107,7 +107,7 @@ describe('GovernorPreventLateQuorum', function () {
.to.emit(this.mock, 'ProposalCreated') .to.emit(this.mock, 'ProposalCreated')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
this.proposer.address, this.proposer,
this.proposal.targets, this.proposal.targets,
this.proposal.values, this.proposal.values,
this.proposal.signatures, this.proposal.signatures,
@ -128,10 +128,10 @@ describe('GovernorPreventLateQuorum', function () {
expect(await this.mock.proposalDeadline(this.proposal.id)).to.equal(deadlineTimepoint); expect(await this.mock.proposalDeadline(this.proposal.id)).to.equal(deadlineTimepoint);
// wait for the last minute to vote // wait for the last minute to vote
await this.helper.waitForDeadline(-1n); await this.helper.waitForDeadline(-1n);
const txVote = await this.helper.connect(this.voter2).vote({ support: Enums.VoteType.For }); const txVote = await this.helper.connect(this.voter2).vote({ support: VoteType.For });
// cannot execute yet // cannot execute yet
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Active); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Active);
// compute new extended schedule // compute new extended schedule
const extendedDeadline = (await time.clockFromReceipt[mode](txVote)) + lateQuorumVoteExtension; const extendedDeadline = (await time.clockFromReceipt[mode](txVote)) + lateQuorumVoteExtension;
@ -139,12 +139,12 @@ describe('GovernorPreventLateQuorum', function () {
expect(await this.mock.proposalDeadline(this.proposal.id)).to.equal(extendedDeadline); expect(await this.mock.proposalDeadline(this.proposal.id)).to.equal(extendedDeadline);
// still possible to vote // still possible to vote
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.Against }); await this.helper.connect(this.voter1).vote({ support: VoteType.Against });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Active); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Active);
await this.helper.waitForDeadline(1n); await this.helper.waitForDeadline(1n);
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Defeated); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Defeated);
// check extension event // check extension event
await expect(txVote).to.emit(this.mock, 'ProposalExtended').withArgs(this.proposal.id, extendedDeadline); await expect(txVote).to.emit(this.mock, 'ProposalExtended').withArgs(this.proposal.id, extendedDeadline);
@ -154,7 +154,7 @@ describe('GovernorPreventLateQuorum', function () {
it('setLateQuorumVoteExtension is protected', async function () { it('setLateQuorumVoteExtension is protected', async function () {
await expect(this.mock.connect(this.owner).setLateQuorumVoteExtension(0n)) await expect(this.mock.connect(this.owner).setLateQuorumVoteExtension(0n))
.to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor') .to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor')
.withArgs(this.owner.address); .withArgs(this.owner);
}); });
it('can setLateQuorumVoteExtension through governance', async function () { it('can setLateQuorumVoteExtension through governance', async function () {
@ -170,7 +170,7 @@ describe('GovernorPreventLateQuorum', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()) await expect(this.helper.execute())

@ -5,7 +5,7 @@ const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs');
const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic');
const { GovernorHelper, timelockSalt } = require('../../helpers/governance'); const { GovernorHelper, timelockSalt } = require('../../helpers/governance');
const { bigint: Enums } = require('../../helpers/enums'); const { VoteType } = require('../../helpers/enums');
const TOKENS = [ const TOKENS = [
{ Token: '$ERC20Votes', mode: 'blocknumber' }, { Token: '$ERC20Votes', mode: 'blocknumber' },
@ -120,10 +120,10 @@ describe('GovernorStorage', function () {
it('queue and execute by id', async function () { it('queue and execute by id', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.connect(this.voter2).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter2).vote({ support: VoteType.For });
await this.helper.connect(this.voter3).vote({ support: Enums.VoteType.Against }); await this.helper.connect(this.voter3).vote({ support: VoteType.Against });
await this.helper.connect(this.voter4).vote({ support: Enums.VoteType.Abstain }); await this.helper.connect(this.voter4).vote({ support: VoteType.Abstain });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.mock.queue(this.proposal.id)) await expect(this.mock.queue(this.proposal.id))

@ -4,11 +4,11 @@ const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs');
const { GovernorHelper } = require('../../helpers/governance'); const { GovernorHelper } = require('../../helpers/governance');
const { bigint: Enums } = require('../../helpers/enums'); const { hashOperation } = require('../../helpers/access-manager');
const { bigint: time } = require('../../helpers/time');
const { max } = require('../../helpers/math'); const { max } = require('../../helpers/math');
const { selector } = require('../../helpers/methods'); const { selector } = require('../../helpers/methods');
const { hashOperation } = require('../../helpers/access-manager'); const { ProposalState, VoteType } = require('../../helpers/enums');
const time = require('../../helpers/time');
function prepareOperation({ sender, target, value = 0n, data = '0x' }) { function prepareOperation({ sender, target, value = 0n, data = '0x' }) {
return { return {
@ -94,12 +94,12 @@ describe('GovernorTimelockAccess', function () {
it('post deployment check', async function () { it('post deployment check', async function () {
expect(await this.mock.name()).to.equal(name); expect(await this.mock.name()).to.equal(name);
expect(await this.mock.token()).to.equal(this.token.target); expect(await this.mock.token()).to.equal(this.token);
expect(await this.mock.votingDelay()).to.equal(votingDelay); expect(await this.mock.votingDelay()).to.equal(votingDelay);
expect(await this.mock.votingPeriod()).to.equal(votingPeriod); expect(await this.mock.votingPeriod()).to.equal(votingPeriod);
expect(await this.mock.quorum(0n)).to.equal(0n); expect(await this.mock.quorum(0n)).to.equal(0n);
expect(await this.mock.accessManager()).to.equal(this.manager.target); expect(await this.mock.accessManager()).to.equal(this.manager);
}); });
it('sets base delay (seconds)', async function () { it('sets base delay (seconds)', async function () {
@ -108,7 +108,7 @@ describe('GovernorTimelockAccess', function () {
// Only through governance // Only through governance
await expect(this.mock.connect(this.voter1).setBaseDelaySeconds(baseDelay)) await expect(this.mock.connect(this.voter1).setBaseDelaySeconds(baseDelay))
.to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor') .to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor')
.withArgs(this.voter1.address); .withArgs(this.voter1);
this.proposal = await this.helper.setProposal( this.proposal = await this.helper.setProposal(
[ [
@ -122,7 +122,7 @@ describe('GovernorTimelockAccess', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()).to.emit(this.mock, 'BaseDelaySet').withArgs(0n, baseDelay); await expect(this.helper.execute()).to.emit(this.mock, 'BaseDelaySet').withArgs(0n, baseDelay);
@ -136,7 +136,7 @@ describe('GovernorTimelockAccess', function () {
// Only through governance // Only through governance
await expect(this.mock.connect(this.voter1).setAccessManagerIgnored(this.other, selectors, true)) await expect(this.mock.connect(this.voter1).setAccessManagerIgnored(this.other, selectors, true))
.to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor') .to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor')
.withArgs(this.voter1.address); .withArgs(this.voter1);
// Ignore // Ignore
await this.helper.setProposal( await this.helper.setProposal(
@ -154,14 +154,14 @@ describe('GovernorTimelockAccess', function () {
); );
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
const ignoreReceipt = this.helper.execute(); const ignoreReceipt = this.helper.execute();
for (const selector of selectors) { for (const selector of selectors) {
await expect(ignoreReceipt) await expect(ignoreReceipt)
.to.emit(this.mock, 'AccessManagerIgnoredSet') .to.emit(this.mock, 'AccessManagerIgnoredSet')
.withArgs(this.other.address, selector, true); .withArgs(this.other, selector, true);
expect(await this.mock.isAccessManagerIgnored(this.other, selector)).to.be.true; expect(await this.mock.isAccessManagerIgnored(this.other, selector)).to.be.true;
} }
@ -182,14 +182,14 @@ describe('GovernorTimelockAccess', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
const unignoreReceipt = this.helper.execute(); const unignoreReceipt = this.helper.execute();
for (const selector of selectors) { for (const selector of selectors) {
await expect(unignoreReceipt) await expect(unignoreReceipt)
.to.emit(this.mock, 'AccessManagerIgnoredSet') .to.emit(this.mock, 'AccessManagerIgnoredSet')
.withArgs(this.other.address, selector, false); .withArgs(this.other, selector, false);
expect(await this.mock.isAccessManagerIgnored(this.other, selector)).to.be.false; expect(await this.mock.isAccessManagerIgnored(this.other, selector)).to.be.false;
} }
}); });
@ -213,12 +213,12 @@ describe('GovernorTimelockAccess', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
const tx = this.helper.execute(); const tx = this.helper.execute();
for (const selector of selectors) { for (const selector of selectors) {
await expect(tx).to.emit(this.mock, 'AccessManagerIgnoredSet').withArgs(this.mock.target, selector, true); await expect(tx).to.emit(this.mock, 'AccessManagerIgnoredSet').withArgs(this.mock, selector, true);
expect(await this.mock.isAccessManagerIgnored(this.mock, selector)).to.be.true; expect(await this.mock.isAccessManagerIgnored(this.mock, selector)).to.be.true;
} }
}); });
@ -365,7 +365,7 @@ describe('GovernorTimelockAccess', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
if (await this.mock.proposalNeedsQueuing(this.proposal.id)) { if (await this.mock.proposalNeedsQueuing(this.proposal.id)) {
expect(await this.helper.queue()) expect(await this.helper.queue())
@ -391,7 +391,7 @@ describe('GovernorTimelockAccess', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await expect(this.helper.execute()) await expect(this.helper.execute())
@ -413,7 +413,7 @@ describe('GovernorTimelockAccess', function () {
// Go through all the governance process // Go through all the governance process
await original.propose(); await original.propose();
await original.waitForSnapshot(); await original.waitForSnapshot();
await original.connect(this.voter1).vote({ support: Enums.VoteType.For }); await original.connect(this.voter1).vote({ support: VoteType.For });
await original.waitForDeadline(); await original.waitForDeadline();
await original.queue(); await original.queue();
await original.waitForEta(); await original.waitForEta();
@ -428,7 +428,7 @@ describe('GovernorTimelockAccess', function () {
await rescheduled.setProposal([this.restricted.operation], 'descr'); await rescheduled.setProposal([this.restricted.operation], 'descr');
await rescheduled.propose(); await rescheduled.propose();
await rescheduled.waitForSnapshot(); await rescheduled.waitForSnapshot();
await rescheduled.connect(this.voter1).vote({ support: Enums.VoteType.For }); await rescheduled.connect(this.voter1).vote({ support: VoteType.For });
await rescheduled.waitForDeadline(); await rescheduled.waitForDeadline();
await rescheduled.queue(); // This will schedule it again in the manager await rescheduled.queue(); // This will schedule it again in the manager
await rescheduled.waitForEta(); await rescheduled.waitForEta();
@ -450,7 +450,7 @@ describe('GovernorTimelockAccess', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
const txQueue = await this.helper.queue(); const txQueue = await this.helper.queue();
await this.helper.waitForEta(); await this.helper.waitForEta();
@ -494,7 +494,7 @@ describe('GovernorTimelockAccess', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
const txQueue = await this.helper.queue(); const txQueue = await this.helper.queue();
await this.helper.waitForEta(); await this.helper.waitForEta();
@ -539,7 +539,7 @@ describe('GovernorTimelockAccess', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
@ -555,8 +555,8 @@ describe('GovernorTimelockAccess', function () {
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Canceled, ProposalState.Canceled,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
@ -568,7 +568,7 @@ describe('GovernorTimelockAccess', function () {
// Go through all the governance process // Go through all the governance process
await original.propose(); await original.propose();
await original.waitForSnapshot(); await original.waitForSnapshot();
await original.connect(this.voter1).vote({ support: Enums.VoteType.For }); await original.connect(this.voter1).vote({ support: VoteType.For });
await original.waitForDeadline(); await original.waitForDeadline();
await original.queue(); await original.queue();
@ -584,7 +584,7 @@ describe('GovernorTimelockAccess', function () {
// Queue the new proposal // Queue the new proposal
await rescheduled.propose(); await rescheduled.propose();
await rescheduled.waitForSnapshot(); await rescheduled.waitForSnapshot();
await rescheduled.connect(this.voter1).vote({ support: Enums.VoteType.For }); await rescheduled.connect(this.voter1).vote({ support: VoteType.For });
await rescheduled.waitForDeadline(); await rescheduled.waitForDeadline();
await rescheduled.queue(); // This will schedule it again in the manager await rescheduled.queue(); // This will schedule it again in the manager
@ -601,8 +601,8 @@ describe('GovernorTimelockAccess', function () {
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
original.currentProposal.id, original.currentProposal.id,
Enums.ProposalState.Canceled, ProposalState.Canceled,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
@ -611,7 +611,7 @@ describe('GovernorTimelockAccess', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
@ -627,8 +627,8 @@ describe('GovernorTimelockAccess', function () {
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Canceled, ProposalState.Canceled,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
@ -637,7 +637,7 @@ describe('GovernorTimelockAccess', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.cancel('internal')) await expect(this.helper.cancel('internal'))
@ -648,8 +648,8 @@ describe('GovernorTimelockAccess', function () {
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Canceled, ProposalState.Canceled,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
@ -668,7 +668,7 @@ describe('GovernorTimelockAccess', function () {
for (const p of [proposal1, proposal2]) { for (const p of [proposal1, proposal2]) {
await p.propose(); await p.propose();
await p.waitForSnapshot(); await p.waitForSnapshot();
await p.connect(this.voter1).vote({ support: Enums.VoteType.For }); await p.connect(this.voter1).vote({ support: VoteType.For });
await p.waitForDeadline(); await p.waitForDeadline();
} }
@ -717,13 +717,13 @@ describe('GovernorTimelockAccess', function () {
it('internal setter', async function () { it('internal setter', async function () {
await expect(this.mock.$_setAccessManagerIgnored(this.receiver, this.restricted.selector, true)) await expect(this.mock.$_setAccessManagerIgnored(this.receiver, this.restricted.selector, true))
.to.emit(this.mock, 'AccessManagerIgnoredSet') .to.emit(this.mock, 'AccessManagerIgnoredSet')
.withArgs(this.receiver.target, this.restricted.selector, true); .withArgs(this.receiver, this.restricted.selector, true);
expect(await this.mock.isAccessManagerIgnored(this.receiver, this.restricted.selector)).to.be.true; expect(await this.mock.isAccessManagerIgnored(this.receiver, this.restricted.selector)).to.be.true;
await expect(this.mock.$_setAccessManagerIgnored(this.mock, '0x12341234', false)) await expect(this.mock.$_setAccessManagerIgnored(this.mock, '0x12341234', false))
.to.emit(this.mock, 'AccessManagerIgnoredSet') .to.emit(this.mock, 'AccessManagerIgnoredSet')
.withArgs(this.mock.target, '0x12341234', false); .withArgs(this.mock, '0x12341234', false);
expect(await this.mock.isAccessManagerIgnored(this.mock, '0x12341234')).to.be.false; expect(await this.mock.isAccessManagerIgnored(this.mock, '0x12341234')).to.be.false;
}); });
@ -752,7 +752,7 @@ describe('GovernorTimelockAccess', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()).to.emit(this.mock, 'AccessManagerIgnoredSet'); await expect(this.helper.execute()).to.emit(this.mock, 'AccessManagerIgnoredSet');
@ -787,24 +787,22 @@ describe('GovernorTimelockAccess', function () {
await this.helper.setProposal([{ target, data }], 'descr #1'); await this.helper.setProposal([{ target, data }], 'descr #1');
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance')
.withArgs(this.manager.target, 0n, amount); .withArgs(this.manager, 0n, amount);
await this.mock.$_setAccessManagerIgnored(target, selector, true); await this.mock.$_setAccessManagerIgnored(target, selector, true);
await this.helper.setProposal([{ target, data }], 'descr #2'); await this.helper.setProposal([{ target, data }], 'descr #2');
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()) await expect(this.helper.execute()).to.emit(this.token, 'Transfer').withArgs(this.mock, this.voter4, amount);
.to.emit(this.token, 'Transfer')
.withArgs(this.mock.target, this.voter4.address, amount);
}); });
}); });
@ -834,7 +832,7 @@ describe('GovernorTimelockAccess', function () {
await this.helper.setProposal([this.operation], `descr`); await this.helper.setProposal([this.operation], `descr`);
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await this.helper.waitForEta(); await this.helper.waitForEta();
@ -856,7 +854,7 @@ describe('GovernorTimelockAccess', function () {
await this.helper.setProposal([this.operation], `descr`); await this.helper.setProposal([this.operation], `descr`);
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.execute(); // Don't revert await this.helper.execute(); // Don't revert
}); });

@ -4,8 +4,8 @@ const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs');
const { GovernorHelper } = require('../../helpers/governance'); const { GovernorHelper } = require('../../helpers/governance');
const { bigint: Enums } = require('../../helpers/enums'); const { ProposalState, VoteType } = require('../../helpers/enums');
const { bigint: time } = require('../../helpers/time'); const time = require('../../helpers/time');
const TOKENS = [ const TOKENS = [
{ Token: '$ERC20Votes', mode: 'blocknumber' }, { Token: '$ERC20Votes', mode: 'blocknumber' },
@ -81,13 +81,13 @@ describe('GovernorTimelockCompound', function () {
it('post deployment check', async function () { it('post deployment check', async function () {
expect(await this.mock.name()).to.equal(name); expect(await this.mock.name()).to.equal(name);
expect(await this.mock.token()).to.equal(this.token.target); expect(await this.mock.token()).to.equal(this.token);
expect(await this.mock.votingDelay()).to.equal(votingDelay); expect(await this.mock.votingDelay()).to.equal(votingDelay);
expect(await this.mock.votingPeriod()).to.equal(votingPeriod); expect(await this.mock.votingPeriod()).to.equal(votingPeriod);
expect(await this.mock.quorum(0n)).to.equal(0n); expect(await this.mock.quorum(0n)).to.equal(0n);
expect(await this.mock.timelock()).to.equal(this.timelock.target); expect(await this.mock.timelock()).to.equal(this.timelock);
expect(await this.timelock.admin()).to.equal(this.mock.target); expect(await this.timelock.admin()).to.equal(this.mock);
}); });
it('nominal', async function () { it('nominal', async function () {
@ -96,10 +96,10 @@ describe('GovernorTimelockCompound', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.connect(this.voter2).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter2).vote({ support: VoteType.For });
await this.helper.connect(this.voter3).vote({ support: Enums.VoteType.Against }); await this.helper.connect(this.voter3).vote({ support: VoteType.Against });
await this.helper.connect(this.voter4).vote({ support: Enums.VoteType.Abstain }); await this.helper.connect(this.voter4).vote({ support: VoteType.Abstain });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
const txQueue = await this.helper.queue(); const txQueue = await this.helper.queue();
@ -129,15 +129,15 @@ describe('GovernorTimelockCompound', function () {
it('if already queued', async function () { it('if already queued', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await expect(this.helper.queue()) await expect(this.helper.queue())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Queued, ProposalState.Queued,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded]),
); );
}); });
@ -150,7 +150,7 @@ describe('GovernorTimelockCompound', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.queue()) await expect(this.helper.queue())
.to.be.revertedWithCustomError(this.mock, 'GovernorAlreadyQueuedProposal') .to.be.revertedWithCustomError(this.mock, 'GovernorAlreadyQueuedProposal')
@ -165,10 +165,10 @@ describe('GovernorTimelockCompound', function () {
it('if not queued', async function () { it('if not queued', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(1n); await this.helper.waitForDeadline(1n);
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Succeeded); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Succeeded);
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.mock, 'GovernorNotQueuedProposal') .to.be.revertedWithCustomError(this.mock, 'GovernorNotQueuedProposal')
@ -178,11 +178,11 @@ describe('GovernorTimelockCompound', function () {
it('if too early', async function () { it('if too early', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Queued); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Queued);
await expect(this.helper.execute()).to.be.rejectedWith( await expect(this.helper.execute()).to.be.rejectedWith(
"Timelock::executeTransaction: Transaction hasn't surpassed time lock", "Timelock::executeTransaction: Transaction hasn't surpassed time lock",
@ -192,26 +192,26 @@ describe('GovernorTimelockCompound', function () {
it('if too late', async function () { it('if too late', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await this.helper.waitForEta(time.duration.days(30)); await this.helper.waitForEta(time.duration.days(30));
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Expired); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Expired);
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Expired, ProposalState.Expired,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
it('if already executed', async function () { it('if already executed', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await this.helper.waitForEta(); await this.helper.waitForEta();
@ -221,8 +221,8 @@ describe('GovernorTimelockCompound', function () {
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Executed, ProposalState.Executed,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
}); });
@ -281,28 +281,28 @@ describe('GovernorTimelockCompound', function () {
it('cancel before queue prevents scheduling', async function () { it('cancel before queue prevents scheduling', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.cancel('internal')) await expect(this.helper.cancel('internal'))
.to.emit(this.mock, 'ProposalCanceled') .to.emit(this.mock, 'ProposalCanceled')
.withArgs(this.proposal.id); .withArgs(this.proposal.id);
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Canceled); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Canceled);
await expect(this.helper.queue()) await expect(this.helper.queue())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Canceled, ProposalState.Canceled,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded]),
); );
}); });
it('cancel after queue prevents executing', async function () { it('cancel after queue prevents executing', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
@ -310,14 +310,14 @@ describe('GovernorTimelockCompound', function () {
.to.emit(this.mock, 'ProposalCanceled') .to.emit(this.mock, 'ProposalCanceled')
.withArgs(this.proposal.id); .withArgs(this.proposal.id);
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Canceled); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Canceled);
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Canceled, ProposalState.Canceled,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
}); });
@ -335,7 +335,7 @@ describe('GovernorTimelockCompound', function () {
.relay(this.token, 0, this.token.interface.encodeFunctionData('transfer', [this.other.address, 1n])), .relay(this.token, 0, this.token.interface.encodeFunctionData('transfer', [this.other.address, 1n])),
) )
.to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor') .to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor')
.withArgs(this.owner.address); .withArgs(this.owner);
}); });
it('can be executed through governance', async function () { it('can be executed through governance', async function () {
@ -355,7 +355,7 @@ describe('GovernorTimelockCompound', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await this.helper.waitForEta(); await this.helper.waitForEta();
@ -364,7 +364,7 @@ describe('GovernorTimelockCompound', function () {
await expect(txExecute).to.changeTokenBalances(this.token, [this.mock, this.other], [-1n, 1n]); await expect(txExecute).to.changeTokenBalances(this.token, [this.mock, this.other], [-1n, 1n]);
await expect(txExecute).to.emit(this.token, 'Transfer').withArgs(this.mock.target, this.other.address, 1n); await expect(txExecute).to.emit(this.token, 'Transfer').withArgs(this.mock, this.other, 1n);
}); });
}); });
@ -376,7 +376,7 @@ describe('GovernorTimelockCompound', function () {
it('is protected', async function () { it('is protected', async function () {
await expect(this.mock.connect(this.owner).updateTimelock(this.newTimelock)) await expect(this.mock.connect(this.owner).updateTimelock(this.newTimelock))
.to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor') .to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor')
.withArgs(this.owner.address); .withArgs(this.owner);
}); });
it('can be executed through governance to', async function () { it('can be executed through governance to', async function () {
@ -396,16 +396,16 @@ describe('GovernorTimelockCompound', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await this.helper.waitForEta(); await this.helper.waitForEta();
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.emit(this.mock, 'TimelockChange') .to.emit(this.mock, 'TimelockChange')
.withArgs(this.timelock.target, this.newTimelock.target); .withArgs(this.timelock, this.newTimelock);
expect(await this.mock.timelock()).to.equal(this.newTimelock.target); expect(await this.mock.timelock()).to.equal(this.newTimelock);
}); });
}); });
@ -432,15 +432,15 @@ describe('GovernorTimelockCompound', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await this.helper.waitForEta(); await this.helper.waitForEta();
await expect(this.helper.execute()).to.emit(this.timelock, 'NewPendingAdmin').withArgs(newGovernor.target); await expect(this.helper.execute()).to.emit(this.timelock, 'NewPendingAdmin').withArgs(newGovernor);
await newGovernor.__acceptAdmin(); await newGovernor.__acceptAdmin();
expect(await this.timelock.admin()).to.equal(newGovernor.target); expect(await this.timelock.admin()).to.equal(newGovernor);
}); });
}); });
}); });

@ -4,8 +4,8 @@ const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs');
const { GovernorHelper, timelockSalt } = require('../../helpers/governance'); const { GovernorHelper, timelockSalt } = require('../../helpers/governance');
const { bigint: Enums } = require('../../helpers/enums'); const { OperationState, ProposalState, VoteType } = require('../../helpers/enums');
const { bigint: time } = require('../../helpers/time'); const time = require('../../helpers/time');
const TOKENS = [ const TOKENS = [
{ Token: '$ERC20Votes', mode: 'blocknumber' }, { Token: '$ERC20Votes', mode: 'blocknumber' },
@ -95,12 +95,12 @@ describe('GovernorTimelockControl', function () {
it('post deployment check', async function () { it('post deployment check', async function () {
expect(await this.mock.name()).to.equal(name); expect(await this.mock.name()).to.equal(name);
expect(await this.mock.token()).to.equal(this.token.target); expect(await this.mock.token()).to.equal(this.token);
expect(await this.mock.votingDelay()).to.equal(votingDelay); expect(await this.mock.votingDelay()).to.equal(votingDelay);
expect(await this.mock.votingPeriod()).to.equal(votingPeriod); expect(await this.mock.votingPeriod()).to.equal(votingPeriod);
expect(await this.mock.quorum(0n)).to.equal(0n); expect(await this.mock.quorum(0n)).to.equal(0n);
expect(await this.mock.timelock()).to.equal(this.timelock.target); expect(await this.mock.timelock()).to.equal(this.timelock);
}); });
it('nominal', async function () { it('nominal', async function () {
@ -109,10 +109,10 @@ describe('GovernorTimelockControl', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.connect(this.voter2).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter2).vote({ support: VoteType.For });
await this.helper.connect(this.voter3).vote({ support: Enums.VoteType.Against }); await this.helper.connect(this.voter3).vote({ support: VoteType.Against });
await this.helper.connect(this.voter4).vote({ support: Enums.VoteType.Abstain }); await this.helper.connect(this.voter4).vote({ support: VoteType.Abstain });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
expect(await this.mock.proposalNeedsQueuing(this.proposal.id)).to.be.true; expect(await this.mock.proposalNeedsQueuing(this.proposal.id)).to.be.true;
@ -145,15 +145,15 @@ describe('GovernorTimelockControl', function () {
it('if already queued', async function () { it('if already queued', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await expect(this.helper.queue()) await expect(this.helper.queue())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Queued, ProposalState.Queued,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded]),
); );
}); });
}); });
@ -162,34 +162,34 @@ describe('GovernorTimelockControl', function () {
it('if not queued', async function () { it('if not queued', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(1n); await this.helper.waitForDeadline(1n);
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Succeeded); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Succeeded);
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.timelock, 'TimelockUnexpectedOperationState') .to.be.revertedWithCustomError(this.timelock, 'TimelockUnexpectedOperationState')
.withArgs(this.proposal.timelockid, GovernorHelper.proposalStatesToBitMap(Enums.OperationState.Ready)); .withArgs(this.proposal.timelockid, GovernorHelper.proposalStatesToBitMap(OperationState.Ready));
}); });
it('if too early', async function () { it('if too early', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Queued); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Queued);
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.timelock, 'TimelockUnexpectedOperationState') .to.be.revertedWithCustomError(this.timelock, 'TimelockUnexpectedOperationState')
.withArgs(this.proposal.timelockid, GovernorHelper.proposalStatesToBitMap(Enums.OperationState.Ready)); .withArgs(this.proposal.timelockid, GovernorHelper.proposalStatesToBitMap(OperationState.Ready));
}); });
it('if already executed', async function () { it('if already executed', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await this.helper.waitForEta(); await this.helper.waitForEta();
@ -199,15 +199,15 @@ describe('GovernorTimelockControl', function () {
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Executed, ProposalState.Executed,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
it('if already executed by another proposer', async function () { it('if already executed by another proposer', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await this.helper.waitForEta(); await this.helper.waitForEta();
@ -222,8 +222,8 @@ describe('GovernorTimelockControl', function () {
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Executed, ProposalState.Executed,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
}); });
@ -233,28 +233,28 @@ describe('GovernorTimelockControl', function () {
it('cancel before queue prevents scheduling', async function () { it('cancel before queue prevents scheduling', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.cancel('internal')) await expect(this.helper.cancel('internal'))
.to.emit(this.mock, 'ProposalCanceled') .to.emit(this.mock, 'ProposalCanceled')
.withArgs(this.proposal.id); .withArgs(this.proposal.id);
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Canceled); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Canceled);
await expect(this.helper.queue()) await expect(this.helper.queue())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Canceled, ProposalState.Canceled,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded]),
); );
}); });
it('cancel after queue prevents executing', async function () { it('cancel after queue prevents executing', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
@ -262,31 +262,31 @@ describe('GovernorTimelockControl', function () {
.to.emit(this.mock, 'ProposalCanceled') .to.emit(this.mock, 'ProposalCanceled')
.withArgs(this.proposal.id); .withArgs(this.proposal.id);
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Canceled); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Canceled);
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Canceled, ProposalState.Canceled,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
it('cancel on timelock is reflected on governor', async function () { it('cancel on timelock is reflected on governor', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Queued); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Queued);
await expect(this.timelock.connect(this.owner).cancel(this.proposal.timelockid)) await expect(this.timelock.connect(this.owner).cancel(this.proposal.timelockid))
.to.emit(this.timelock, 'Cancelled') .to.emit(this.timelock, 'Cancelled')
.withArgs(this.proposal.timelockid); .withArgs(this.proposal.timelockid);
expect(await this.mock.state(this.proposal.id)).to.equal(Enums.ProposalState.Canceled); expect(await this.mock.state(this.proposal.id)).to.equal(ProposalState.Canceled);
}); });
}); });
@ -303,7 +303,7 @@ describe('GovernorTimelockControl', function () {
.relay(this.token, 0n, this.token.interface.encodeFunctionData('transfer', [this.other.address, 1n])), .relay(this.token, 0n, this.token.interface.encodeFunctionData('transfer', [this.other.address, 1n])),
) )
.to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor') .to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor')
.withArgs(this.owner.address); .withArgs(this.owner);
}); });
it('can be executed through governance', async function () { it('can be executed through governance', async function () {
@ -323,7 +323,7 @@ describe('GovernorTimelockControl', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await this.helper.waitForEta(); await this.helper.waitForEta();
@ -332,7 +332,7 @@ describe('GovernorTimelockControl', function () {
await expect(txExecute).to.changeTokenBalances(this.token, [this.mock, this.other], [-1n, 1n]); await expect(txExecute).to.changeTokenBalances(this.token, [this.mock, this.other], [-1n, 1n]);
await expect(txExecute).to.emit(this.token, 'Transfer').withArgs(this.mock.target, this.other.address, 1n); await expect(txExecute).to.emit(this.token, 'Transfer').withArgs(this.mock, this.other, 1n);
}); });
it('is payable and can transfer eth to EOA', async function () { it('is payable and can transfer eth to EOA', async function () {
@ -352,7 +352,7 @@ describe('GovernorTimelockControl', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await this.helper.waitForEta(); await this.helper.waitForEta();
@ -397,7 +397,7 @@ describe('GovernorTimelockControl', function () {
it('is protected', async function () { it('is protected', async function () {
await expect(this.mock.connect(this.owner).updateTimelock(this.newTimelock)) await expect(this.mock.connect(this.owner).updateTimelock(this.newTimelock))
.to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor') .to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor')
.withArgs(this.owner.address); .withArgs(this.owner);
}); });
it('can be executed through governance to', async function () { it('can be executed through governance to', async function () {
@ -413,16 +413,16 @@ describe('GovernorTimelockControl', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await this.helper.waitForEta(); await this.helper.waitForEta();
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.emit(this.mock, 'TimelockChange') .to.emit(this.mock, 'TimelockChange')
.withArgs(this.timelock.target, this.newTimelock.target); .withArgs(this.timelock, this.newTimelock);
expect(await this.mock.timelock()).to.equal(this.newTimelock.target); expect(await this.mock.timelock()).to.equal(this.newTimelock);
}); });
}); });
@ -489,7 +489,7 @@ describe('GovernorTimelockControl', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.queue(); await this.helper.queue();
await this.helper.waitForEta(); await this.helper.waitForEta();

@ -3,8 +3,8 @@ const { expect } = require('chai');
const { loadFixture, mine } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture, mine } = require('@nomicfoundation/hardhat-network-helpers');
const { GovernorHelper } = require('../../helpers/governance'); const { GovernorHelper } = require('../../helpers/governance');
const { bigint: Enums } = require('../../helpers/enums'); const { ProposalState, VoteType } = require('../../helpers/enums');
const { bigint: time } = require('../../helpers/time'); const time = require('../../helpers/time');
const TOKENS = [ const TOKENS = [
{ Token: '$ERC20Votes', mode: 'blocknumber' }, { Token: '$ERC20Votes', mode: 'blocknumber' },
@ -63,7 +63,7 @@ describe('GovernorVotesQuorumFraction', function () {
it('deployment check', async function () { it('deployment check', async function () {
expect(await this.mock.name()).to.equal(name); expect(await this.mock.name()).to.equal(name);
expect(await this.mock.token()).to.equal(this.token.target); expect(await this.mock.token()).to.equal(this.token);
expect(await this.mock.votingDelay()).to.equal(votingDelay); expect(await this.mock.votingDelay()).to.equal(votingDelay);
expect(await this.mock.votingPeriod()).to.equal(votingPeriod); expect(await this.mock.votingPeriod()).to.equal(votingPeriod);
expect(await this.mock.quorum(0)).to.equal(0n); expect(await this.mock.quorum(0)).to.equal(0n);
@ -77,7 +77,7 @@ describe('GovernorVotesQuorumFraction', function () {
it('quroum reached', async function () { it('quroum reached', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.execute(); await this.helper.execute();
}); });
@ -85,14 +85,14 @@ describe('GovernorVotesQuorumFraction', function () {
it('quroum not reached', async function () { it('quroum not reached', async function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter2).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter2).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()) await expect(this.helper.execute())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState')
.withArgs( .withArgs(
this.proposal.id, this.proposal.id,
Enums.ProposalState.Defeated, ProposalState.Defeated,
GovernorHelper.proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]), GovernorHelper.proposalStatesToBitMap([ProposalState.Succeeded, ProposalState.Queued]),
); );
}); });
@ -100,7 +100,7 @@ describe('GovernorVotesQuorumFraction', function () {
it('updateQuorumNumerator is protected', async function () { it('updateQuorumNumerator is protected', async function () {
await expect(this.mock.connect(this.owner).updateQuorumNumerator(newRatio)) await expect(this.mock.connect(this.owner).updateQuorumNumerator(newRatio))
.to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor') .to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor')
.withArgs(this.owner.address); .withArgs(this.owner);
}); });
it('can updateQuorumNumerator through governance', async function () { it('can updateQuorumNumerator through governance', async function () {
@ -116,7 +116,7 @@ describe('GovernorVotesQuorumFraction', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await expect(this.helper.execute()).to.emit(this.mock, 'QuorumNumeratorUpdated').withArgs(ratio, newRatio); await expect(this.helper.execute()).to.emit(this.mock, 'QuorumNumeratorUpdated').withArgs(ratio, newRatio);
@ -150,7 +150,7 @@ describe('GovernorVotesQuorumFraction', function () {
await this.helper.propose(); await this.helper.propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter1).vote({ support: VoteType.For });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
const quorumDenominator = await this.mock.quorumDenominator(); const quorumDenominator = await this.mock.quorumDenominator();

@ -3,7 +3,7 @@ const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { GovernorHelper } = require('../../helpers/governance'); const { GovernorHelper } = require('../../helpers/governance');
const { bigint: Enums } = require('../../helpers/enums'); const { VoteType } = require('../../helpers/enums');
const { getDomain, ExtendedBallot } = require('../../helpers/eip712'); const { getDomain, ExtendedBallot } = require('../../helpers/eip712');
const TOKENS = [ const TOKENS = [
@ -65,7 +65,7 @@ describe('GovernorWithParams', function () {
it('deployment check', async function () { it('deployment check', async function () {
expect(await this.mock.name()).to.equal(name); expect(await this.mock.name()).to.equal(name);
expect(await this.mock.token()).to.equal(this.token.target); expect(await this.mock.token()).to.equal(this.token);
expect(await this.mock.votingDelay()).to.equal(votingDelay); expect(await this.mock.votingDelay()).to.equal(votingDelay);
expect(await this.mock.votingPeriod()).to.equal(votingPeriod); expect(await this.mock.votingPeriod()).to.equal(votingPeriod);
}); });
@ -73,10 +73,10 @@ describe('GovernorWithParams', function () {
it('nominal is unaffected', async function () { it('nominal is unaffected', async function () {
await this.helper.connect(this.proposer).propose(); await this.helper.connect(this.proposer).propose();
await this.helper.waitForSnapshot(); await this.helper.waitForSnapshot();
await this.helper.connect(this.voter1).vote({ support: Enums.VoteType.For, reason: 'This is nice' }); await this.helper.connect(this.voter1).vote({ support: VoteType.For, reason: 'This is nice' });
await this.helper.connect(this.voter2).vote({ support: Enums.VoteType.For }); await this.helper.connect(this.voter2).vote({ support: VoteType.For });
await this.helper.connect(this.voter3).vote({ support: Enums.VoteType.Against }); await this.helper.connect(this.voter3).vote({ support: VoteType.Against });
await this.helper.connect(this.voter4).vote({ support: Enums.VoteType.Abstain }); await this.helper.connect(this.voter4).vote({ support: VoteType.Abstain });
await this.helper.waitForDeadline(); await this.helper.waitForDeadline();
await this.helper.execute(); await this.helper.execute();
@ -95,7 +95,7 @@ describe('GovernorWithParams', function () {
await expect( await expect(
this.helper.connect(this.voter2).vote({ this.helper.connect(this.voter2).vote({
support: Enums.VoteType.For, support: VoteType.For,
reason: 'no particular reason', reason: 'no particular reason',
params: params.encoded, params: params.encoded,
}), }),
@ -106,7 +106,7 @@ describe('GovernorWithParams', function () {
.withArgs( .withArgs(
this.voter2.address, this.voter2.address,
this.proposal.id, this.proposal.id,
Enums.VoteType.For, VoteType.For,
weight, weight,
'no particular reason', 'no particular reason',
params.encoded, params.encoded,
@ -128,7 +128,7 @@ describe('GovernorWithParams', function () {
const nonce = await this.mock.nonces(this.other); const nonce = await this.mock.nonces(this.other);
const data = { const data = {
proposalId: this.proposal.id, proposalId: this.proposal.id,
support: Enums.VoteType.For, support: VoteType.For,
voter: this.other.address, voter: this.other.address,
nonce, nonce,
reason: 'no particular reason', reason: 'no particular reason',
@ -161,7 +161,7 @@ describe('GovernorWithParams', function () {
const nonce = await this.mock.nonces(this.other); const nonce = await this.mock.nonces(this.other);
const data = { const data = {
proposalId: this.proposal.id, proposalId: this.proposal.id,
support: Enums.VoteType.For, support: VoteType.For,
voter: wallet.target, voter: wallet.target,
nonce, nonce,
reason: 'no particular reason', reason: 'no particular reason',
@ -192,7 +192,7 @@ describe('GovernorWithParams', function () {
const nonce = await this.mock.nonces(this.other); const nonce = await this.mock.nonces(this.other);
const data = { const data = {
proposalId: this.proposal.id, proposalId: this.proposal.id,
support: Enums.VoteType.For, support: VoteType.For,
voter: this.other.address, voter: this.other.address,
nonce, nonce,
reason: 'no particular reason', reason: 'no particular reason',
@ -225,7 +225,7 @@ describe('GovernorWithParams', function () {
const nonce = await this.mock.nonces(this.other); const nonce = await this.mock.nonces(this.other);
const data = { const data = {
proposalId: this.proposal.id, proposalId: this.proposal.id,
support: Enums.VoteType.For, support: VoteType.For,
voter: this.other.address, voter: this.other.address,
nonce: nonce + 1n, nonce: nonce + 1n,
reason: 'no particular reason', reason: 'no particular reason',

@ -1,4 +1,6 @@
const { bigint: time } = require('../../helpers/time'); const { expect } = require('chai');
const time = require('../../helpers/time');
function shouldBehaveLikeERC6372(mode = 'blocknumber') { function shouldBehaveLikeERC6372(mode = 'blocknumber') {
describe('should implement ERC-6372', function () { describe('should implement ERC-6372', function () {

@ -3,7 +3,7 @@ const { expect } = require('chai');
const { mine } = require('@nomicfoundation/hardhat-network-helpers'); const { mine } = require('@nomicfoundation/hardhat-network-helpers');
const { getDomain, Delegation } = require('../../helpers/eip712'); const { getDomain, Delegation } = require('../../helpers/eip712');
const { bigint: time } = require('../../helpers/time'); const time = require('../../helpers/time');
const { shouldBehaveLikeERC6372 } = require('./ERC6372.behavior'); const { shouldBehaveLikeERC6372 } = require('./ERC6372.behavior');
@ -30,10 +30,10 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true }
await expect(this.votes.connect(this.alice).delegate(this.alice)) await expect(this.votes.connect(this.alice).delegate(this.alice))
.to.emit(this.votes, 'DelegateChanged') .to.emit(this.votes, 'DelegateChanged')
.withArgs(this.alice.address, ethers.ZeroAddress, this.alice.address) .withArgs(this.alice, ethers.ZeroAddress, this.alice)
.to.not.emit(this.votes, 'DelegateVotesChanged'); .to.not.emit(this.votes, 'DelegateVotesChanged');
expect(await this.votes.delegates(this.alice)).to.equal(this.alice.address); expect(await this.votes.delegates(this.alice)).to.equal(this.alice);
}); });
it('delegation with tokens', async function () { it('delegation with tokens', async function () {
@ -47,11 +47,11 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true }
await expect(tx) await expect(tx)
.to.emit(this.votes, 'DelegateChanged') .to.emit(this.votes, 'DelegateChanged')
.withArgs(this.alice.address, ethers.ZeroAddress, this.alice.address) .withArgs(this.alice, ethers.ZeroAddress, this.alice)
.to.emit(this.votes, 'DelegateVotesChanged') .to.emit(this.votes, 'DelegateVotesChanged')
.withArgs(this.alice.address, 0n, weight); .withArgs(this.alice, 0n, weight);
expect(await this.votes.delegates(this.alice)).to.equal(this.alice.address); expect(await this.votes.delegates(this.alice)).to.equal(this.alice);
expect(await this.votes.getVotes(this.alice)).to.equal(weight); expect(await this.votes.getVotes(this.alice)).to.equal(weight);
expect(await this.votes.getPastVotes(this.alice, timepoint - 1n)).to.equal(0n); expect(await this.votes.getPastVotes(this.alice, timepoint - 1n)).to.equal(0n);
await mine(); await mine();
@ -63,7 +63,7 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true }
await this.votes.$_mint(this.alice, token); await this.votes.$_mint(this.alice, token);
const weight = getWeight(token); const weight = getWeight(token);
expect(await this.votes.delegates(this.alice)).to.equal(this.alice.address); expect(await this.votes.delegates(this.alice)).to.equal(this.alice);
expect(await this.votes.getVotes(this.alice)).to.equal(weight); expect(await this.votes.getVotes(this.alice)).to.equal(weight);
expect(await this.votes.getVotes(this.bob)).to.equal(0); expect(await this.votes.getVotes(this.bob)).to.equal(0);
@ -72,13 +72,13 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true }
await expect(tx) await expect(tx)
.to.emit(this.votes, 'DelegateChanged') .to.emit(this.votes, 'DelegateChanged')
.withArgs(this.alice.address, this.alice.address, this.bob.address) .withArgs(this.alice, this.alice, this.bob)
.to.emit(this.votes, 'DelegateVotesChanged') .to.emit(this.votes, 'DelegateVotesChanged')
.withArgs(this.alice.address, weight, 0) .withArgs(this.alice, weight, 0)
.to.emit(this.votes, 'DelegateVotesChanged') .to.emit(this.votes, 'DelegateVotesChanged')
.withArgs(this.bob.address, 0, weight); .withArgs(this.bob, 0, weight);
expect(await this.votes.delegates(this.alice)).to.equal(this.bob.address); expect(await this.votes.delegates(this.alice)).to.equal(this.bob);
expect(await this.votes.getVotes(this.alice)).to.equal(0n); expect(await this.votes.getVotes(this.alice)).to.equal(0n);
expect(await this.votes.getVotes(this.bob)).to.equal(weight); expect(await this.votes.getVotes(this.bob)).to.equal(weight);
@ -93,7 +93,7 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true }
const nonce = 0n; const nonce = 0n;
it('accept signed delegation', async function () { it('accept signed delegation', async function () {
await this.votes.$_mint(this.delegator.address, token); await this.votes.$_mint(this.delegator, token);
const weight = getWeight(token); const weight = getWeight(token);
const { r, s, v } = await this.delegator const { r, s, v } = await this.delegator
@ -108,18 +108,18 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true }
) )
.then(ethers.Signature.from); .then(ethers.Signature.from);
expect(await this.votes.delegates(this.delegator.address)).to.equal(ethers.ZeroAddress); expect(await this.votes.delegates(this.delegator)).to.equal(ethers.ZeroAddress);
const tx = await this.votes.delegateBySig(this.delegatee, nonce, ethers.MaxUint256, v, r, s); const tx = await this.votes.delegateBySig(this.delegatee, nonce, ethers.MaxUint256, v, r, s);
const timepoint = await time.clockFromReceipt[mode](tx); const timepoint = await time.clockFromReceipt[mode](tx);
await expect(tx) await expect(tx)
.to.emit(this.votes, 'DelegateChanged') .to.emit(this.votes, 'DelegateChanged')
.withArgs(this.delegator.address, ethers.ZeroAddress, this.delegatee.address) .withArgs(this.delegator, ethers.ZeroAddress, this.delegatee)
.to.emit(this.votes, 'DelegateVotesChanged') .to.emit(this.votes, 'DelegateVotesChanged')
.withArgs(this.delegatee.address, 0, weight); .withArgs(this.delegatee, 0, weight);
expect(await this.votes.delegates(this.delegator.address)).to.equal(this.delegatee.address); expect(await this.votes.delegates(this.delegator.address)).to.equal(this.delegatee);
expect(await this.votes.getVotes(this.delegator.address)).to.equal(0n); expect(await this.votes.getVotes(this.delegator.address)).to.equal(0n);
expect(await this.votes.getVotes(this.delegatee)).to.equal(weight); expect(await this.votes.getVotes(this.delegatee)).to.equal(weight);
expect(await this.votes.getPastVotes(this.delegatee, timepoint - 1n)).to.equal(0n); expect(await this.votes.getPastVotes(this.delegatee, timepoint - 1n)).to.equal(0n);
@ -144,7 +144,7 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true }
await expect(this.votes.delegateBySig(this.delegatee, nonce, ethers.MaxUint256, v, r, s)) await expect(this.votes.delegateBySig(this.delegatee, nonce, ethers.MaxUint256, v, r, s))
.to.be.revertedWithCustomError(this.votes, 'InvalidAccountNonce') .to.be.revertedWithCustomError(this.votes, 'InvalidAccountNonce')
.withArgs(this.delegator.address, nonce + 1n); .withArgs(this.delegator, nonce + 1n);
}); });
it('rejects bad delegatee', async function () { it('rejects bad delegatee', async function () {
@ -167,9 +167,9 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true }
log => this.votes.interface.parseLog(log)?.name === 'DelegateChanged', log => this.votes.interface.parseLog(log)?.name === 'DelegateChanged',
); );
const { args } = this.votes.interface.parseLog(delegateChanged); const { args } = this.votes.interface.parseLog(delegateChanged);
expect(args.delegator).to.not.be.equal(this.delegator.address); expect(args.delegator).to.not.be.equal(this.delegator);
expect(args.fromDelegate).to.equal(ethers.ZeroAddress); expect(args.fromDelegate).to.equal(ethers.ZeroAddress);
expect(args.toDelegate).to.equal(this.other.address); expect(args.toDelegate).to.equal(this.other);
}); });
it('rejects bad nonce', async function () { it('rejects bad nonce', async function () {
@ -187,7 +187,7 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true }
await expect(this.votes.delegateBySig(this.delegatee, nonce + 1n, ethers.MaxUint256, v, r, s)) await expect(this.votes.delegateBySig(this.delegatee, nonce + 1n, ethers.MaxUint256, v, r, s))
.to.be.revertedWithCustomError(this.votes, 'InvalidAccountNonce') .to.be.revertedWithCustomError(this.votes, 'InvalidAccountNonce')
.withArgs(this.delegator.address, 0); .withArgs(this.delegator, 0);
}); });
it('rejects expired permit', async function () { it('rejects expired permit', async function () {

@ -4,7 +4,7 @@ const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { sum } = require('../../helpers/math'); const { sum } = require('../../helpers/math');
const { zip } = require('../../helpers/iterate'); const { zip } = require('../../helpers/iterate');
const { bigint: time } = require('../../helpers/time'); const time = require('../../helpers/time');
const { shouldBehaveLikeVotes } = require('./Votes.behavior'); const { shouldBehaveLikeVotes } = require('./Votes.behavior');
@ -71,7 +71,7 @@ describe('Votes', function () {
expect(await this.votes.getVotes(this.accounts[0])).to.equal(this.amounts[this.accounts[0].address]); expect(await this.votes.getVotes(this.accounts[0])).to.equal(this.amounts[this.accounts[0].address]);
expect(await this.votes.getVotes(this.accounts[1])).to.equal(0n); expect(await this.votes.getVotes(this.accounts[1])).to.equal(0n);
expect(await this.votes.delegates(this.accounts[0])).to.equal(this.accounts[0].address); expect(await this.votes.delegates(this.accounts[0])).to.equal(this.accounts[0]);
expect(await this.votes.delegates(this.accounts[1])).to.equal(ethers.ZeroAddress); expect(await this.votes.delegates(this.accounts[1])).to.equal(ethers.ZeroAddress);
await this.votes.delegate(this.accounts[1], ethers.Typed.address(this.accounts[0])); await this.votes.delegate(this.accounts[1], ethers.Typed.address(this.accounts[0]));
@ -80,13 +80,13 @@ describe('Votes', function () {
this.amounts[this.accounts[0].address] + this.amounts[this.accounts[1].address], this.amounts[this.accounts[0].address] + this.amounts[this.accounts[1].address],
); );
expect(await this.votes.getVotes(this.accounts[1])).to.equal(0n); expect(await this.votes.getVotes(this.accounts[1])).to.equal(0n);
expect(await this.votes.delegates(this.accounts[0])).to.equal(this.accounts[0].address); expect(await this.votes.delegates(this.accounts[0])).to.equal(this.accounts[0]);
expect(await this.votes.delegates(this.accounts[1])).to.equal(this.accounts[0].address); expect(await this.votes.delegates(this.accounts[1])).to.equal(this.accounts[0]);
}); });
it('cross delegates', async function () { it('cross delegates', async function () {
await this.votes.delegate(this.accounts[0], ethers.Typed.address(this.accounts[1].address)); await this.votes.delegate(this.accounts[0], ethers.Typed.address(this.accounts[1]));
await this.votes.delegate(this.accounts[1], ethers.Typed.address(this.accounts[0].address)); await this.votes.delegate(this.accounts[1], ethers.Typed.address(this.accounts[0]));
expect(await this.votes.getVotes(this.accounts[0])).to.equal(this.amounts[this.accounts[1].address]); expect(await this.votes.getVotes(this.accounts[0])).to.equal(this.amounts[this.accounts[1].address]);
expect(await this.votes.getVotes(this.accounts[1])).to.equal(this.amounts[this.accounts[0].address]); expect(await this.votes.getVotes(this.accounts[1])).to.equal(this.amounts[this.accounts[0].address]);

@ -1,7 +1,8 @@
const { ethers } = require('hardhat'); const { ethers } = require('hardhat');
const { MAX_UINT64 } = require('./constants'); const { MAX_UINT64 } = require('./constants');
const { namespaceSlot } = require('./namespaced-storage'); const time = require('./time');
const { bigint: time } = require('./time'); const { upgradeableSlot } = require('./storage');
function buildBaseRoles() { function buildBaseRoles() {
const roles = { const roles = {
@ -45,8 +46,8 @@ const formatAccess = access => [access[0], access[1].toString()];
const MINSETBACK = time.duration.days(5); const MINSETBACK = time.duration.days(5);
const EXPIRATION = time.duration.weeks(1); const EXPIRATION = time.duration.weeks(1);
const EXECUTION_ID_STORAGE_SLOT = namespaceSlot('AccessManager', 3n); const EXECUTION_ID_STORAGE_SLOT = upgradeableSlot('AccessManager', 3n);
const CONSUMING_SCHEDULE_STORAGE_SLOT = namespaceSlot('AccessManaged', 0n); const CONSUMING_SCHEDULE_STORAGE_SLOT = upgradeableSlot('AccessManaged', 0n);
/** /**
* @requires this.{manager, caller, target, calldata} * @requires this.{manager, caller, target, calldata}

@ -1,6 +0,0 @@
const { ethers } = require('hardhat');
module.exports = {
// TODO: remove conversion toNumber() when bigint are supported
getChainId: () => ethers.provider.getNetwork().then(network => ethers.toNumber(network.chainId)),
};

@ -1,4 +1,4 @@
const { ethers } = require('ethers'); const { ethers } = require('hardhat');
const types = require('./eip712-types'); const types = require('./eip712-types');
async function getDomain(contract) { async function getDomain(contract) {
@ -11,8 +11,7 @@ async function getDomain(contract) {
const domain = { const domain = {
name, name,
version, version,
// TODO: remove check when contracts are all migrated to ethers chainId,
chainId: web3.utils.isBN(chainId) ? chainId.toNumber() : chainId,
verifyingContract, verifyingContract,
salt, salt,
}; };

@ -1,22 +1,12 @@
function Enum(...options) { function Enum(...options) {
return Object.fromEntries(options.map((key, i) => [key, web3.utils.toBN(i)]));
}
function EnumBigInt(...options) {
return Object.fromEntries(options.map((key, i) => [key, BigInt(i)])); return Object.fromEntries(options.map((key, i) => [key, BigInt(i)]));
} }
// TODO: remove web3, simplify code module.exports = {
function createExport(Enum) {
return {
Enum, Enum,
ProposalState: Enum('Pending', 'Active', 'Canceled', 'Defeated', 'Succeeded', 'Queued', 'Expired', 'Executed'), ProposalState: Enum('Pending', 'Active', 'Canceled', 'Defeated', 'Succeeded', 'Queued', 'Expired', 'Executed'),
VoteType: Enum('Against', 'For', 'Abstain'), VoteType: Enum('Against', 'For', 'Abstain'),
Rounding: Enum('Floor', 'Ceil', 'Trunc', 'Expand'), Rounding: Enum('Floor', 'Ceil', 'Trunc', 'Expand'),
OperationState: Enum('Unset', 'Waiting', 'Ready', 'Done'), OperationState: Enum('Unset', 'Waiting', 'Ready', 'Done'),
RevertType: Enum('None', 'RevertWithoutMessage', 'RevertWithMessage', 'RevertWithCustomError', 'Panic'), RevertType: Enum('None', 'RevertWithoutMessage', 'RevertWithMessage', 'RevertWithCustomError', 'Panic'),
}; };
}
module.exports = createExport(Enum);
module.exports.bigint = createExport(EnumBigInt);

@ -172,7 +172,7 @@ class GovernorHelper {
if (!Array.isArray(proposalStates)) { if (!Array.isArray(proposalStates)) {
proposalStates = [proposalStates]; proposalStates = [proposalStates];
} }
const statesCount = BigInt(Object.keys(ProposalState).length); const statesCount = ethers.toBigInt(Object.keys(ProposalState).length);
let result = 0n; let result = 0n;
for (const state of unique(...proposalStates)) { for (const state of unique(...proposalStates)) {

@ -4,10 +4,7 @@ const min = (...values) => values.slice(1).reduce((x, y) => (x < y ? x : y), val
const sum = (...values) => values.slice(1).reduce((x, y) => x + y, values.at(0)); const sum = (...values) => values.slice(1).reduce((x, y) => x + y, values.at(0));
module.exports = { module.exports = {
// re-export min, max & sum of integer / bignumber
min, min,
max, max,
sum, sum,
// deprecated: BN version of sum
BNsum: (...args) => args.slice(1).reduce((x, y) => x.add(y), args.at(0)),
}; };

@ -1,22 +0,0 @@
const { ethers, artifacts } = require('hardhat');
const { erc7201slot } = require('./erc1967');
function namespaceId(contractName) {
return `openzeppelin.storage.${contractName}`;
}
function namespaceSlot(contractName, offset) {
try {
// Try to get the artifact paths, will throw if it doesn't exist
artifacts._getArtifactPathSync(`${contractName}Upgradeable`);
return offset + ethers.toBigInt(erc7201slot(namespaceId(contractName)));
} catch (_) {
return offset;
}
}
module.exports = {
namespaceSlot,
namespaceLocation: erc7201slot,
namespaceId,
};

@ -1,5 +1,5 @@
const { ethers } = require('hardhat'); const { ethers } = require('hardhat');
const { getStorageAt, setStorageAt } = require('@nomicfoundation/hardhat-network-helpers'); const { setStorageAt } = require('@nomicfoundation/hardhat-network-helpers');
const ImplementationLabel = 'eip1967.proxy.implementation'; const ImplementationLabel = 'eip1967.proxy.implementation';
const AdminLabel = 'eip1967.proxy.admin'; const AdminLabel = 'eip1967.proxy.admin';
@ -7,11 +7,10 @@ const BeaconLabel = 'eip1967.proxy.beacon';
const erc1967slot = label => ethers.toBeHex(ethers.toBigInt(ethers.id(label)) - 1n); const erc1967slot = label => ethers.toBeHex(ethers.toBigInt(ethers.id(label)) - 1n);
const erc7201slot = label => ethers.toBeHex(ethers.toBigInt(ethers.keccak256(erc1967slot(label))) & ~0xffn); const erc7201slot = label => ethers.toBeHex(ethers.toBigInt(ethers.keccak256(erc1967slot(label))) & ~0xffn);
const erc7201format = contractName => `openzeppelin.storage.${contractName}`;
const getSlot = (address, slot) => const getSlot = (address, slot) =>
(ethers.isAddressable(address) ? address.getAddress() : Promise.resolve(address)).then(address => ethers.provider.getStorage(address, ethers.isBytesLike(slot) ? slot : erc1967slot(slot));
getStorageAt(address, ethers.isBytesLike(slot) ? slot : erc1967slot(slot)),
);
const setSlot = (address, slot, value) => const setSlot = (address, slot, value) =>
Promise.all([ Promise.all([
@ -22,6 +21,16 @@ const setSlot = (address, slot, value) =>
const getAddressInSlot = (address, slot) => const getAddressInSlot = (address, slot) =>
getSlot(address, slot).then(slotValue => ethers.AbiCoder.defaultAbiCoder().decode(['address'], slotValue)[0]); getSlot(address, slot).then(slotValue => ethers.AbiCoder.defaultAbiCoder().decode(['address'], slotValue)[0]);
const upgradeableSlot = (contractName, offset) => {
try {
// Try to get the artifact paths, will throw if it doesn't exist
artifacts._getArtifactPathSync(`${contractName}Upgradeable`);
return offset + ethers.toBigInt(erc7201slot(erc7201format(contractName)));
} catch (_) {
return offset;
}
};
module.exports = { module.exports = {
ImplementationLabel, ImplementationLabel,
AdminLabel, AdminLabel,
@ -31,7 +40,9 @@ module.exports = {
BeaconSlot: erc1967slot(BeaconLabel), BeaconSlot: erc1967slot(BeaconLabel),
erc1967slot, erc1967slot,
erc7201slot, erc7201slot,
erc7201format,
setSlot, setSlot,
getSlot, getSlot,
getAddressInSlot, getAddressInSlot,
upgradeableSlot,
}; };

@ -3,12 +3,12 @@ const { time, mine, mineUpTo } = require('@nomicfoundation/hardhat-network-helpe
const { mapValues } = require('./iterate'); const { mapValues } = require('./iterate');
const clock = { const clock = {
blocknumber: () => time.latestBlock(), blocknumber: () => time.latestBlock().then(ethers.toBigInt),
timestamp: () => time.latest(), timestamp: () => time.latest().then(ethers.toBigInt),
}; };
const clockFromReceipt = { const clockFromReceipt = {
blocknumber: receipt => Promise.resolve(receipt.blockNumber), blocknumber: receipt => Promise.resolve(ethers.toBigInt(receipt.blockNumber)),
timestamp: receipt => ethers.provider.getBlock(receipt.blockNumber).then(block => block.timestamp), timestamp: receipt => ethers.provider.getBlock(receipt.blockNumber).then(block => ethers.toBigInt(block.timestamp)),
}; };
const increaseBy = { const increaseBy = {
blockNumber: mine, blockNumber: mine,
@ -19,7 +19,7 @@ const increaseTo = {
blocknumber: mineUpTo, blocknumber: mineUpTo,
timestamp: (to, mine = true) => (mine ? time.increaseTo(to) : time.setNextBlockTimestamp(to)), timestamp: (to, mine = true) => (mine ? time.increaseTo(to) : time.setNextBlockTimestamp(to)),
}; };
const duration = time.duration; const duration = mapValues(time.duration, fn => n => ethers.toBigInt(fn(ethers.toNumber(n))));
module.exports = { module.exports = {
clock, clock,
@ -28,12 +28,3 @@ module.exports = {
increaseTo, increaseTo,
duration, duration,
}; };
// TODO: deprecate the old version in favor of this one
module.exports.bigint = {
clock: mapValues(clock, fn => () => fn().then(ethers.toBigInt)),
clockFromReceipt: mapValues(clockFromReceipt, fn => receipt => fn(receipt).then(ethers.toBigInt)),
increaseBy: increaseBy,
increaseTo: increaseTo,
duration: mapValues(duration, fn => n => ethers.toBigInt(fn(ethers.toNumber(n)))),
};

@ -1,5 +1,7 @@
const { network } = require('hardhat'); const { network } = require('hardhat');
const { expect } = require('chai');
const { mine } = require('@nomicfoundation/hardhat-network-helpers'); const { mine } = require('@nomicfoundation/hardhat-network-helpers');
const { unique } = require('./iterate'); const { unique } = require('./iterate');
async function batchInBlock(txs) { async function batchInBlock(txs) {

@ -30,7 +30,7 @@ describe('ERC2771Context', function () {
}); });
it('returns the trusted forwarder', async function () { it('returns the trusted forwarder', async function () {
expect(await this.context.trustedForwarder()).to.equal(this.forwarder.target); expect(await this.context.trustedForwarder()).to.equal(this.forwarder);
}); });
describe('when called directly', function () { describe('when called directly', function () {
@ -57,14 +57,14 @@ describe('ERC2771Context', function () {
expect(await this.forwarder.verify(req)).to.equal(true); expect(await this.forwarder.verify(req)).to.equal(true);
await expect(this.forwarder.execute(req)).to.emit(this.context, 'Sender').withArgs(this.sender.address); await expect(this.forwarder.execute(req)).to.emit(this.context, 'Sender').withArgs(this.sender);
}); });
it('returns the original sender when calldata length is less than 20 bytes (address length)', async function () { it('returns the original sender when calldata length is less than 20 bytes (address length)', async function () {
// The forwarder doesn't produce calls with calldata length less than 20 bytes so `this.forwarderAsSigner` is used instead. // The forwarder doesn't produce calls with calldata length less than 20 bytes so `this.forwarderAsSigner` is used instead.
await expect(this.context.connect(this.forwarderAsSigner).msgSender()) await expect(this.context.connect(this.forwarderAsSigner).msgSender())
.to.emit(this.context, 'Sender') .to.emit(this.context, 'Sender')
.withArgs(this.forwarder.target); .withArgs(this.forwarder);
}); });
}); });
@ -128,6 +128,6 @@ describe('ERC2771Context', function () {
expect(await this.forwarder.verify(req)).to.equal(true); expect(await this.forwarder.verify(req)).to.equal(true);
await expect(this.forwarder.execute(req)).to.emit(this.context, 'Sender').withArgs(this.sender.address); await expect(this.forwarder.execute(req)).to.emit(this.context, 'Sender').withArgs(this.sender);
}); });
}); });

@ -3,8 +3,8 @@ const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { getDomain, ForwardRequest } = require('../helpers/eip712'); const { getDomain, ForwardRequest } = require('../helpers/eip712');
const { bigint: time } = require('../helpers/time');
const { sum } = require('../helpers/math'); const { sum } = require('../helpers/math');
const time = require('../helpers/time');
async function fixture() { async function fixture() {
const [sender, refundReceiver, another, ...accounts] = await ethers.getSigners(); const [sender, refundReceiver, another, ...accounts] = await ethers.getSigners();
@ -140,7 +140,7 @@ describe('ERC2771Forwarder', function () {
} else { } else {
await expect(promise) await expect(promise)
.to.be.revertedWithCustomError(this.forwarder, 'ERC2771UntrustfulTarget') .to.be.revertedWithCustomError(this.forwarder, 'ERC2771UntrustfulTarget')
.withArgs(request.to, this.forwarder.target); .withArgs(request.to, this.forwarder);
} }
}); });
} }
@ -299,7 +299,7 @@ describe('ERC2771Forwarder', function () {
} else { } else {
await expect(promise) await expect(promise)
.to.be.revertedWithCustomError(this.forwarder, 'ERC2771UntrustfulTarget') .to.be.revertedWithCustomError(this.forwarder, 'ERC2771UntrustfulTarget')
.withArgs(this.requests[idx].to, this.forwarder.target); .withArgs(this.requests[idx].to, this.forwarder);
} }
}); });
} }

@ -2,7 +2,7 @@ const { ethers } = require('hardhat');
const { expect } = require('chai'); const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { getAddressInSlot, setSlot, ImplementationSlot, AdminSlot, BeaconSlot } = require('../../helpers/erc1967'); const { getAddressInSlot, setSlot, ImplementationSlot, AdminSlot, BeaconSlot } = require('../../helpers/storage');
async function fixture() { async function fixture() {
const [, admin, anotherAccount] = await ethers.getSigners(); const [, admin, anotherAccount] = await ethers.getSigners();
@ -26,8 +26,8 @@ describe('ERC1967Utils', function () {
describe('getImplementation', function () { describe('getImplementation', function () {
it('returns current implementation and matches implementation slot value', async function () { it('returns current implementation and matches implementation slot value', async function () {
expect(await this.utils.$getImplementation()).to.equal(this.v1.target); expect(await this.utils.$getImplementation()).to.equal(this.v1);
expect(await getAddressInSlot(this.utils, ImplementationSlot)).to.equal(this.v1.target); expect(await getAddressInSlot(this.utils, ImplementationSlot)).to.equal(this.v1);
}); });
}); });
@ -36,14 +36,14 @@ describe('ERC1967Utils', function () {
const newImplementation = this.v2; const newImplementation = this.v2;
const tx = await this.utils.$upgradeToAndCall(newImplementation, '0x'); const tx = await this.utils.$upgradeToAndCall(newImplementation, '0x');
expect(await getAddressInSlot(this.utils, ImplementationSlot)).to.equal(newImplementation.target); expect(await getAddressInSlot(this.utils, ImplementationSlot)).to.equal(newImplementation);
await expect(tx).to.emit(this.utils, 'Upgraded').withArgs(newImplementation.target); await expect(tx).to.emit(this.utils, 'Upgraded').withArgs(newImplementation);
}); });
it('reverts when implementation does not contain code', async function () { it('reverts when implementation does not contain code', async function () {
await expect(this.utils.$upgradeToAndCall(this.anotherAccount, '0x')) await expect(this.utils.$upgradeToAndCall(this.anotherAccount, '0x'))
.to.be.revertedWithCustomError(this.utils, 'ERC1967InvalidImplementation') .to.be.revertedWithCustomError(this.utils, 'ERC1967InvalidImplementation')
.withArgs(this.anotherAccount.address); .withArgs(this.anotherAccount);
}); });
describe('when data is empty', function () { describe('when data is empty', function () {
@ -72,8 +72,8 @@ describe('ERC1967Utils', function () {
describe('getAdmin', function () { describe('getAdmin', function () {
it('returns current admin and matches admin slot value', async function () { it('returns current admin and matches admin slot value', async function () {
expect(await this.utils.$getAdmin()).to.equal(this.admin.address); expect(await this.utils.$getAdmin()).to.equal(this.admin);
expect(await getAddressInSlot(this.utils, AdminSlot)).to.equal(this.admin.address); expect(await getAddressInSlot(this.utils, AdminSlot)).to.equal(this.admin);
}); });
}); });
@ -82,8 +82,8 @@ describe('ERC1967Utils', function () {
const newAdmin = this.anotherAccount; const newAdmin = this.anotherAccount;
const tx = await this.utils.$changeAdmin(newAdmin); const tx = await this.utils.$changeAdmin(newAdmin);
expect(await getAddressInSlot(this.utils, AdminSlot)).to.equal(newAdmin.address); expect(await getAddressInSlot(this.utils, AdminSlot)).to.equal(newAdmin);
await expect(tx).to.emit(this.utils, 'AdminChanged').withArgs(this.admin.address, newAdmin.address); await expect(tx).to.emit(this.utils, 'AdminChanged').withArgs(this.admin, newAdmin);
}); });
it('reverts when setting the address zero as admin', async function () { it('reverts when setting the address zero as admin', async function () {
@ -102,8 +102,8 @@ describe('ERC1967Utils', function () {
describe('getBeacon', function () { describe('getBeacon', function () {
it('returns current beacon and matches beacon slot value', async function () { it('returns current beacon and matches beacon slot value', async function () {
expect(await this.utils.$getBeacon()).to.equal(this.beacon.target); expect(await this.utils.$getBeacon()).to.equal(this.beacon);
expect(await getAddressInSlot(this.utils, BeaconSlot)).to.equal(this.beacon.target); expect(await getAddressInSlot(this.utils, BeaconSlot)).to.equal(this.beacon);
}); });
}); });
@ -112,14 +112,14 @@ describe('ERC1967Utils', function () {
const newBeacon = await ethers.deployContract('UpgradeableBeaconMock', [this.v2]); const newBeacon = await ethers.deployContract('UpgradeableBeaconMock', [this.v2]);
const tx = await this.utils.$upgradeBeaconToAndCall(newBeacon, '0x'); const tx = await this.utils.$upgradeBeaconToAndCall(newBeacon, '0x');
expect(await getAddressInSlot(this.utils, BeaconSlot)).to.equal(newBeacon.target); expect(await getAddressInSlot(this.utils, BeaconSlot)).to.equal(newBeacon);
await expect(tx).to.emit(this.utils, 'BeaconUpgraded').withArgs(newBeacon.target); await expect(tx).to.emit(this.utils, 'BeaconUpgraded').withArgs(newBeacon);
}); });
it('reverts when beacon does not contain code', async function () { it('reverts when beacon does not contain code', async function () {
await expect(this.utils.$upgradeBeaconToAndCall(this.anotherAccount, '0x')) await expect(this.utils.$upgradeBeaconToAndCall(this.anotherAccount, '0x'))
.to.be.revertedWithCustomError(this.utils, 'ERC1967InvalidBeacon') .to.be.revertedWithCustomError(this.utils, 'ERC1967InvalidBeacon')
.withArgs(this.anotherAccount.address); .withArgs(this.anotherAccount);
}); });
it("reverts when beacon's implementation does not contain code", async function () { it("reverts when beacon's implementation does not contain code", async function () {
@ -127,7 +127,7 @@ describe('ERC1967Utils', function () {
await expect(this.utils.$upgradeBeaconToAndCall(newBeacon, '0x')) await expect(this.utils.$upgradeBeaconToAndCall(newBeacon, '0x'))
.to.be.revertedWithCustomError(this.utils, 'ERC1967InvalidImplementation') .to.be.revertedWithCustomError(this.utils, 'ERC1967InvalidImplementation')
.withArgs(this.anotherAccount.address); .withArgs(this.anotherAccount);
}); });
describe('when data is empty', function () { describe('when data is empty', function () {
@ -154,7 +154,7 @@ describe('ERC1967Utils', function () {
const newBeacon = await ethers.deployContract('UpgradeableBeaconReentrantMock'); const newBeacon = await ethers.deployContract('UpgradeableBeaconReentrantMock');
await expect(this.utils.$upgradeBeaconToAndCall(newBeacon, '0x')) await expect(this.utils.$upgradeBeaconToAndCall(newBeacon, '0x'))
.to.be.revertedWithCustomError(newBeacon, 'BeaconProxyBeaconSlotAddress') .to.be.revertedWithCustomError(newBeacon, 'BeaconProxyBeaconSlotAddress')
.withArgs(newBeacon.target); .withArgs(newBeacon);
}); });
}); });
}); });

@ -1,19 +1,19 @@
const { ethers } = require('hardhat'); const { ethers } = require('hardhat');
const { expect } = require('chai'); const { expect } = require('chai');
const { getAddressInSlot, ImplementationSlot } = require('../helpers/erc1967'); const { getAddressInSlot, ImplementationSlot } = require('../helpers/storage');
module.exports = function shouldBehaveLikeProxy() { module.exports = function shouldBehaveLikeProxy() {
it('cannot be initialized with a non-contract address', async function () { it('cannot be initialized with a non-contract address', async function () {
const initializeData = '0x'; const initializeData = '0x';
await expect(this.createProxy(this.nonContractAddress, initializeData)) await expect(this.createProxy(this.nonContractAddress, initializeData))
.to.be.revertedWithCustomError(await ethers.getContractFactory('ERC1967Proxy'), 'ERC1967InvalidImplementation') .to.be.revertedWithCustomError(await ethers.getContractFactory('ERC1967Proxy'), 'ERC1967InvalidImplementation')
.withArgs(this.nonContractAddress.address); .withArgs(this.nonContractAddress);
}); });
const assertProxyInitialization = function ({ value, balance }) { const assertProxyInitialization = function ({ value, balance }) {
it('sets the implementation address', async function () { it('sets the implementation address', async function () {
expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.implementation.target); expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.implementation);
}); });
it('initializes the proxy', async function () { it('initializes the proxy', async function () {

@ -1,7 +1,8 @@
const { ethers } = require('hardhat'); const { ethers } = require('hardhat');
const { expect } = require('chai'); const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { getAddressInSlot, BeaconSlot } = require('../../helpers/erc1967');
const { getAddressInSlot, BeaconSlot } = require('../../helpers/storage');
async function fixture() { async function fixture() {
const [admin, other] = await ethers.getSigners(); const [admin, other] = await ethers.getSigners();
@ -27,7 +28,7 @@ describe('BeaconProxy', function () {
await expect(this.newBeaconProxy(notBeacon, '0x')) await expect(this.newBeaconProxy(notBeacon, '0x'))
.to.be.revertedWithCustomError(this.factory, 'ERC1967InvalidBeacon') .to.be.revertedWithCustomError(this.factory, 'ERC1967InvalidBeacon')
.withArgs(notBeacon.address); .withArgs(notBeacon);
}); });
it('non-compliant beacon', async function () { it('non-compliant beacon', async function () {
@ -48,7 +49,7 @@ describe('BeaconProxy', function () {
describe('initialization', function () { describe('initialization', function () {
async function assertInitialized({ value, balance }) { async function assertInitialized({ value, balance }) {
const beaconAddress = await getAddressInSlot(this.proxy, BeaconSlot); const beaconAddress = await getAddressInSlot(this.proxy, BeaconSlot);
expect(beaconAddress).to.equal(this.beacon.target); expect(beaconAddress).to.equal(this.beacon);
const dummy = this.v1.attach(this.proxy); const dummy = this.v1.attach(this.proxy);
expect(await dummy.value()).to.equal(value); expect(await dummy.value()).to.equal(value);

@ -20,36 +20,36 @@ describe('UpgradeableBeacon', function () {
it('cannot be created with non-contract implementation', async function () { it('cannot be created with non-contract implementation', async function () {
await expect(ethers.deployContract('UpgradeableBeacon', [this.other, this.admin])) await expect(ethers.deployContract('UpgradeableBeacon', [this.other, this.admin]))
.to.be.revertedWithCustomError(this.beacon, 'BeaconInvalidImplementation') .to.be.revertedWithCustomError(this.beacon, 'BeaconInvalidImplementation')
.withArgs(this.other.address); .withArgs(this.other);
}); });
describe('once deployed', async function () { describe('once deployed', async function () {
it('emits Upgraded event to the first implementation', async function () { it('emits Upgraded event to the first implementation', async function () {
await expect(this.beacon.deploymentTransaction()).to.emit(this.beacon, 'Upgraded').withArgs(this.v1.target); await expect(this.beacon.deploymentTransaction()).to.emit(this.beacon, 'Upgraded').withArgs(this.v1);
}); });
it('returns implementation', async function () { it('returns implementation', async function () {
expect(await this.beacon.implementation()).to.equal(this.v1.target); expect(await this.beacon.implementation()).to.equal(this.v1);
}); });
it('can be upgraded by the admin', async function () { it('can be upgraded by the admin', async function () {
await expect(this.beacon.connect(this.admin).upgradeTo(this.v2)) await expect(this.beacon.connect(this.admin).upgradeTo(this.v2))
.to.emit(this.beacon, 'Upgraded') .to.emit(this.beacon, 'Upgraded')
.withArgs(this.v2.target); .withArgs(this.v2);
expect(await this.beacon.implementation()).to.equal(this.v2.target); expect(await this.beacon.implementation()).to.equal(this.v2);
}); });
it('cannot be upgraded to a non-contract', async function () { it('cannot be upgraded to a non-contract', async function () {
await expect(this.beacon.connect(this.admin).upgradeTo(this.other)) await expect(this.beacon.connect(this.admin).upgradeTo(this.other))
.to.be.revertedWithCustomError(this.beacon, 'BeaconInvalidImplementation') .to.be.revertedWithCustomError(this.beacon, 'BeaconInvalidImplementation')
.withArgs(this.other.address); .withArgs(this.other);
}); });
it('cannot be upgraded by other account', async function () { it('cannot be upgraded by other account', async function () {
await expect(this.beacon.connect(this.other).upgradeTo(this.v2)) await expect(this.beacon.connect(this.other).upgradeTo(this.v2))
.to.be.revertedWithCustomError(this.beacon, 'OwnableUnauthorizedAccount') .to.be.revertedWithCustomError(this.beacon, 'OwnableUnauthorizedAccount')
.withArgs(this.other.address); .withArgs(this.other);
}); });
}); });
}); });

@ -1,7 +1,8 @@
const { ethers } = require('hardhat'); const { ethers } = require('hardhat');
const { expect } = require('chai'); const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { getAddressInSlot, ImplementationSlot } = require('../../helpers/erc1967');
const { getAddressInSlot, ImplementationSlot } = require('../../helpers/storage');
async function fixture() { async function fixture() {
const [admin, other] = await ethers.getSigners(); const [admin, other] = await ethers.getSigners();
@ -27,7 +28,7 @@ describe('ProxyAdmin', function () {
}); });
it('has an owner', async function () { it('has an owner', async function () {
expect(await this.proxyAdmin.owner()).to.equal(this.admin.address); expect(await this.proxyAdmin.owner()).to.equal(this.admin);
}); });
it('has an interface version', async function () { it('has an interface version', async function () {
@ -39,14 +40,14 @@ describe('ProxyAdmin', function () {
it('fails to upgrade', async function () { it('fails to upgrade', async function () {
await expect(this.proxyAdmin.connect(this.other).upgradeAndCall(this.proxy, this.v2, '0x')) await expect(this.proxyAdmin.connect(this.other).upgradeAndCall(this.proxy, this.v2, '0x'))
.to.be.revertedWithCustomError(this.proxyAdmin, 'OwnableUnauthorizedAccount') .to.be.revertedWithCustomError(this.proxyAdmin, 'OwnableUnauthorizedAccount')
.withArgs(this.other.address); .withArgs(this.other);
}); });
}); });
context('with authorized account', function () { context('with authorized account', function () {
it('upgrades implementation', async function () { it('upgrades implementation', async function () {
await this.proxyAdmin.connect(this.admin).upgradeAndCall(this.proxy, this.v2, '0x'); await this.proxyAdmin.connect(this.admin).upgradeAndCall(this.proxy, this.v2, '0x');
expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.be.equal(this.v2.target); expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.be.equal(this.v2);
}); });
}); });
}); });
@ -57,7 +58,7 @@ describe('ProxyAdmin', function () {
const data = this.v1.interface.encodeFunctionData('initializeNonPayableWithValue', [1337n]); const data = this.v1.interface.encodeFunctionData('initializeNonPayableWithValue', [1337n]);
await expect(this.proxyAdmin.connect(this.other).upgradeAndCall(this.proxy, this.v2, data)) await expect(this.proxyAdmin.connect(this.other).upgradeAndCall(this.proxy, this.v2, data))
.to.be.revertedWithCustomError(this.proxyAdmin, 'OwnableUnauthorizedAccount') .to.be.revertedWithCustomError(this.proxyAdmin, 'OwnableUnauthorizedAccount')
.withArgs(this.other.address); .withArgs(this.other);
}); });
}); });
@ -73,7 +74,7 @@ describe('ProxyAdmin', function () {
it('upgrades implementation', async function () { it('upgrades implementation', async function () {
const data = this.v2.interface.encodeFunctionData('initializeNonPayableWithValue', [1337n]); const data = this.v2.interface.encodeFunctionData('initializeNonPayableWithValue', [1337n]);
await this.proxyAdmin.connect(this.admin).upgradeAndCall(this.proxy, this.v2, data); await this.proxyAdmin.connect(this.admin).upgradeAndCall(this.proxy, this.v2, data);
expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.be.equal(this.v2.target); expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.be.equal(this.v2);
}); });
}); });
}); });

@ -1,8 +1,8 @@
const { ethers } = require('hardhat'); const { ethers } = require('hardhat');
const { expect } = require('chai'); const { expect } = require('chai');
const { getAddressInSlot, ImplementationSlot, AdminSlot } = require('../../helpers/erc1967');
const { impersonate } = require('../../helpers/account'); const { impersonate } = require('../../helpers/account');
const { getAddressInSlot, ImplementationSlot, AdminSlot } = require('../../helpers/storage');
// createProxy, initialOwner, accounts // createProxy, initialOwner, accounts
module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() {
@ -43,7 +43,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() {
describe('implementation', function () { describe('implementation', function () {
it('returns the current implementation address', async function () { it('returns the current implementation address', async function () {
expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.implementationV0.target); expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.implementationV0);
}); });
it('delegates to the implementation', async function () { it('delegates to the implementation', async function () {
@ -53,26 +53,26 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() {
describe('proxy admin', function () { describe('proxy admin', function () {
it('emits AdminChanged event during construction', async function () { it('emits AdminChanged event during construction', async function () {
await expect(this.tx).to.emit(this.proxy, 'AdminChanged').withArgs(ethers.ZeroAddress, this.proxyAdmin.target); await expect(this.tx).to.emit(this.proxy, 'AdminChanged').withArgs(ethers.ZeroAddress, this.proxyAdmin);
}); });
it('sets the proxy admin in storage with the correct initial owner', async function () { it('sets the proxy admin in storage with the correct initial owner', async function () {
expect(await getAddressInSlot(this.proxy, AdminSlot)).to.equal(this.proxyAdmin.target); expect(await getAddressInSlot(this.proxy, AdminSlot)).to.equal(this.proxyAdmin);
expect(await this.proxyAdmin.owner()).to.equal(this.owner.address); expect(await this.proxyAdmin.owner()).to.equal(this.owner);
}); });
it('can overwrite the admin by the implementation', async function () { it('can overwrite the admin by the implementation', async function () {
await this.instance.unsafeOverrideAdmin(this.other); await this.instance.unsafeOverrideAdmin(this.other);
const ERC1967AdminSlotValue = await getAddressInSlot(this.proxy, AdminSlot); const ERC1967AdminSlotValue = await getAddressInSlot(this.proxy, AdminSlot);
expect(ERC1967AdminSlotValue).to.equal(this.other.address); expect(ERC1967AdminSlotValue).to.equal(this.other);
expect(ERC1967AdminSlotValue).to.not.equal(this.proxyAdmin.address); expect(ERC1967AdminSlotValue).to.not.equal(this.proxyAdmin);
// Still allows previous admin to execute admin operations // Still allows previous admin to execute admin operations
await expect(this.proxy.connect(this.proxyAdminAsSigner).upgradeToAndCall(this.implementationV1, '0x')) await expect(this.proxy.connect(this.proxyAdminAsSigner).upgradeToAndCall(this.implementationV1, '0x'))
.to.emit(this.proxy, 'Upgraded') .to.emit(this.proxy, 'Upgraded')
.withArgs(this.implementationV1.target); .withArgs(this.implementationV1);
}); });
}); });
@ -99,11 +99,11 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() {
}); });
it('upgrades to the requested implementation', async function () { it('upgrades to the requested implementation', async function () {
expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.behavior.target); expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.behavior);
}); });
it('emits an event', async function () { it('emits an event', async function () {
await expect(this.tx).to.emit(this.proxy, 'Upgraded').withArgs(this.behavior.target); await expect(this.tx).to.emit(this.proxy, 'Upgraded').withArgs(this.behavior);
}); });
it('calls the initializer function', async function () { it('calls the initializer function', async function () {
@ -160,9 +160,9 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() {
}); });
it('upgrades to the requested version and emits an event', async function () { it('upgrades to the requested version and emits an event', async function () {
expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.behaviorV1.target); expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.behaviorV1);
await expect(this.tx).to.emit(this.proxy, 'Upgraded').withArgs(this.behaviorV1.target); await expect(this.tx).to.emit(this.proxy, 'Upgraded').withArgs(this.behaviorV1);
}); });
it("calls the 'initialize' function and sends given value to the proxy", async function () { it("calls the 'initialize' function and sends given value to the proxy", async function () {
@ -184,9 +184,9 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() {
}); });
it('upgrades to the requested version and emits an event', async function () { it('upgrades to the requested version and emits an event', async function () {
expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.behaviorV2.target); expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.behaviorV2);
await expect(this.tx).to.emit(this.proxy, 'Upgraded').withArgs(this.behaviorV2.target); await expect(this.tx).to.emit(this.proxy, 'Upgraded').withArgs(this.behaviorV2);
}); });
it("calls the 'migrate' function and sends given value to the proxy", async function () { it("calls the 'migrate' function and sends given value to the proxy", async function () {
@ -209,9 +209,9 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() {
}); });
it('upgrades to the requested version and emits an event', async function () { it('upgrades to the requested version and emits an event', async function () {
expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.behaviorV3.target); expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.behaviorV3);
await expect(this.tx).to.emit(this.proxy, 'Upgraded').withArgs(this.behaviorV3.target); await expect(this.tx).to.emit(this.proxy, 'Upgraded').withArgs(this.behaviorV3);
}); });
it("calls the 'migrate' function and sends given value to the proxy", async function () { it("calls the 'migrate' function and sends given value to the proxy", async function () {
@ -255,7 +255,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() {
it('executes the proxy function if the sender is the admin', async function () { it('executes the proxy function if the sender is the admin', async function () {
await expect(this.proxy.connect(this.proxyAdminAsSigner).upgradeToAndCall(this.clashingImplV1, '0x')) await expect(this.proxy.connect(this.proxyAdminAsSigner).upgradeToAndCall(this.clashingImplV1, '0x'))
.to.emit(this.proxy, 'Upgraded') .to.emit(this.proxy, 'Upgraded')
.withArgs(this.clashingImplV1.target); .withArgs(this.clashingImplV1);
}); });
it('delegates the call to implementation when sender is not the admin', async function () { it('delegates the call to implementation when sender is not the admin', async function () {

@ -2,7 +2,7 @@ const { ethers } = require('hardhat');
const { expect } = require('chai'); const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { getAddressInSlot, ImplementationSlot } = require('../../helpers/erc1967'); const { getAddressInSlot, ImplementationSlot } = require('../../helpers/storage');
async function fixture() { async function fixture() {
const implInitial = await ethers.deployContract('UUPSUpgradeableMock'); const implInitial = await ethers.deployContract('UUPSUpgradeableMock');
@ -40,9 +40,9 @@ describe('UUPSUpgradeable', function () {
it('upgrade to upgradeable implementation', async function () { it('upgrade to upgradeable implementation', async function () {
await expect(this.instance.upgradeToAndCall(this.implUpgradeOk, '0x')) await expect(this.instance.upgradeToAndCall(this.implUpgradeOk, '0x'))
.to.emit(this.instance, 'Upgraded') .to.emit(this.instance, 'Upgraded')
.withArgs(this.implUpgradeOk.target); .withArgs(this.implUpgradeOk);
expect(await getAddressInSlot(this.instance, ImplementationSlot)).to.equal(this.implUpgradeOk.target); expect(await getAddressInSlot(this.instance, ImplementationSlot)).to.equal(this.implUpgradeOk);
}); });
it('upgrade to upgradeable implementation with call', async function () { it('upgrade to upgradeable implementation with call', async function () {
@ -52,9 +52,9 @@ describe('UUPSUpgradeable', function () {
this.instance.upgradeToAndCall(this.implUpgradeOk, this.implUpgradeOk.interface.encodeFunctionData('increment')), this.instance.upgradeToAndCall(this.implUpgradeOk, this.implUpgradeOk.interface.encodeFunctionData('increment')),
) )
.to.emit(this.instance, 'Upgraded') .to.emit(this.instance, 'Upgraded')
.withArgs(this.implUpgradeOk.target); .withArgs(this.implUpgradeOk);
expect(await getAddressInSlot(this.instance, ImplementationSlot)).to.equal(this.implUpgradeOk.target); expect(await getAddressInSlot(this.instance, ImplementationSlot)).to.equal(this.implUpgradeOk);
expect(await this.instance.current()).to.equal(1n); expect(await this.instance.current()).to.equal(1n);
}); });
@ -96,16 +96,16 @@ describe('UUPSUpgradeable', function () {
it('upgrade to and unsafe upgradeable implementation', async function () { it('upgrade to and unsafe upgradeable implementation', async function () {
await expect(this.instance.upgradeToAndCall(this.implUpgradeUnsafe, '0x')) await expect(this.instance.upgradeToAndCall(this.implUpgradeUnsafe, '0x'))
.to.emit(this.instance, 'Upgraded') .to.emit(this.instance, 'Upgraded')
.withArgs(this.implUpgradeUnsafe.target); .withArgs(this.implUpgradeUnsafe);
expect(await getAddressInSlot(this.instance, ImplementationSlot)).to.equal(this.implUpgradeUnsafe.target); expect(await getAddressInSlot(this.instance, ImplementationSlot)).to.equal(this.implUpgradeUnsafe);
}); });
// delegate to a non existing upgradeTo function causes a low level revert // delegate to a non existing upgradeTo function causes a low level revert
it('reject upgrade to non uups implementation', async function () { it('reject upgrade to non uups implementation', async function () {
await expect(this.instance.upgradeToAndCall(this.implUpgradeNonUUPS, '0x')) await expect(this.instance.upgradeToAndCall(this.implUpgradeNonUUPS, '0x'))
.to.be.revertedWithCustomError(this.instance, 'ERC1967InvalidImplementation') .to.be.revertedWithCustomError(this.instance, 'ERC1967InvalidImplementation')
.withArgs(this.implUpgradeNonUUPS.target); .withArgs(this.implUpgradeNonUUPS);
}); });
it('reject proxy address as implementation', async function () { it('reject proxy address as implementation', async function () {
@ -115,6 +115,6 @@ describe('UUPSUpgradeable', function () {
await expect(this.instance.upgradeToAndCall(otherInstance, '0x')) await expect(this.instance.upgradeToAndCall(otherInstance, '0x'))
.to.be.revertedWithCustomError(this.instance, 'ERC1967InvalidImplementation') .to.be.revertedWithCustomError(this.instance, 'ERC1967InvalidImplementation')
.withArgs(otherInstance.target); .withArgs(otherInstance);
}); });
}); });

@ -8,16 +8,12 @@ async function fixture() {
return { signers, addresses }; return { signers, addresses };
} }
contract('Environment sanity', function (accounts) { describe('Environment sanity', function () {
beforeEach(async function () { beforeEach(async function () {
Object.assign(this, await loadFixture(fixture)); Object.assign(this, await loadFixture(fixture));
}); });
describe('[skip-on-coverage] signers', function () { describe('[skip-on-coverage] signers', function () {
it('match accounts', async function () {
expect(this.addresses).to.deep.equal(accounts);
});
it('signer #0 is skipped', async function () { it('signer #0 is skipped', async function () {
const signer = await ethers.provider.getSigner(0); const signer = await ethers.provider.getSigner(0);
expect(this.addresses).to.not.include(await signer.getAddress()); expect(this.addresses).to.not.include(await signer.getAddress());

@ -2,9 +2,7 @@ const { ethers } = require('hardhat');
const { expect } = require('chai'); const { expect } = require('chai');
const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs');
const { const { RevertType } = require('../../helpers/enums');
bigint: { RevertType },
} = require('../../helpers/enums');
const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior'); const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior');
function shouldBehaveLikeERC1155() { function shouldBehaveLikeERC1155() {
@ -118,9 +116,7 @@ function shouldBehaveLikeERC1155() {
}); });
it('emits an ApprovalForAll log', async function () { it('emits an ApprovalForAll log', async function () {
await expect(this.tx) await expect(this.tx).to.emit(this.token, 'ApprovalForAll').withArgs(this.holder, this.proxy, true);
.to.emit(this.token, 'ApprovalForAll')
.withArgs(this.holder.address, this.proxy.address, true);
}); });
it('can unset approval for an operator', async function () { it('can unset approval for an operator', async function () {
@ -148,7 +144,7 @@ function shouldBehaveLikeERC1155() {
.safeTransferFrom(this.holder, this.recipient, firstTokenId, firstTokenValue + 1n, '0x'), .safeTransferFrom(this.holder, this.recipient, firstTokenId, firstTokenValue + 1n, '0x'),
) )
.to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
.withArgs(this.holder.address, firstTokenValue, firstTokenValue + 1n, firstTokenId); .withArgs(this.holder, firstTokenValue, firstTokenValue + 1n, firstTokenId);
}); });
it('reverts when transferring to zero address', async function () { it('reverts when transferring to zero address', async function () {
@ -173,13 +169,7 @@ function shouldBehaveLikeERC1155() {
it('emits a TransferSingle log', async function () { it('emits a TransferSingle log', async function () {
await expect(this.tx) await expect(this.tx)
.to.emit(this.token, 'TransferSingle') .to.emit(this.token, 'TransferSingle')
.withArgs( .withArgs(this.args.operator, this.args.from, this.args.to, this.args.id, this.args.value);
this.args.operator.address ?? this.args.operator.target ?? this.args.operator,
this.args.from.address ?? this.args.from.target ?? this.args.from,
this.args.to.address ?? this.args.to.target ?? this.args.to,
this.args.id,
this.args.value,
);
}); });
} }
@ -219,7 +209,7 @@ function shouldBehaveLikeERC1155() {
.safeTransferFrom(this.holder, this.recipient, firstTokenId, firstTokenValue, '0x'), .safeTransferFrom(this.holder, this.recipient, firstTokenId, firstTokenValue, '0x'),
) )
.to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll') .to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll')
.withArgs(this.proxy.address, this.holder.address); .withArgs(this.proxy, this.holder);
}); });
}); });
@ -278,14 +268,7 @@ function shouldBehaveLikeERC1155() {
it('calls onERC1155Received', async function () { it('calls onERC1155Received', async function () {
await expect(this.tx) await expect(this.tx)
.to.emit(this.receiver, 'Received') .to.emit(this.receiver, 'Received')
.withArgs( .withArgs(this.args.operator, this.args.from, this.args.id, this.args.value, this.args.data, anyValue);
this.args.operator.address,
this.args.from.address,
this.args.id,
this.args.value,
this.args.data,
anyValue,
);
}); });
}); });
@ -309,14 +292,7 @@ function shouldBehaveLikeERC1155() {
it('calls onERC1155Received', async function () { it('calls onERC1155Received', async function () {
await expect(this.tx) await expect(this.tx)
.to.emit(this.receiver, 'Received') .to.emit(this.receiver, 'Received')
.withArgs( .withArgs(this.args.operator, this.args.from, this.args.id, this.args.value, this.args.data, anyValue);
this.args.operator.address,
this.args.from.address,
this.args.id,
this.args.value,
this.args.data,
anyValue,
);
}); });
}); });
}); });
@ -335,7 +311,7 @@ function shouldBehaveLikeERC1155() {
.safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'), .safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'),
) )
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver') .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
.withArgs(receiver.target); .withArgs(receiver);
}); });
}); });
@ -370,7 +346,7 @@ function shouldBehaveLikeERC1155() {
.safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'), .safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'),
) )
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver') .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
.withArgs(receiver.target); .withArgs(receiver);
}); });
}); });
@ -411,7 +387,7 @@ function shouldBehaveLikeERC1155() {
describe('to a contract that does not implement the required function', function () { describe('to a contract that does not implement the required function', function () {
it('reverts', async function () { it('reverts', async function () {
const invalidReceiver = this.token.target; const invalidReceiver = this.token;
await expect( await expect(
this.token this.token
@ -443,7 +419,7 @@ function shouldBehaveLikeERC1155() {
), ),
) )
.to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
.withArgs(this.holder.address, secondTokenValue, secondTokenValue + 1n, secondTokenId); .withArgs(this.holder, secondTokenValue, secondTokenValue + 1n, secondTokenId);
}); });
it("reverts when ids array length doesn't match values array length", async function () { it("reverts when ids array length doesn't match values array length", async function () {
@ -510,13 +486,7 @@ function shouldBehaveLikeERC1155() {
it('emits a TransferBatch log', async function () { it('emits a TransferBatch log', async function () {
await expect(this.tx) await expect(this.tx)
.to.emit(this.token, 'TransferBatch') .to.emit(this.token, 'TransferBatch')
.withArgs( .withArgs(this.args.operator, this.args.from, this.args.to, this.args.ids, this.args.values);
this.args.operator.address ?? this.args.operator.target ?? this.args.operator,
this.args.from.address ?? this.args.from.target ?? this.args.from,
this.args.to.address ?? this.args.to.target ?? this.args.to,
this.args.ids,
this.args.values,
);
}); });
} }
@ -557,7 +527,7 @@ function shouldBehaveLikeERC1155() {
), ),
) )
.to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll') .to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll')
.withArgs(this.proxy.address, this.holder.address); .withArgs(this.proxy, this.holder);
}); });
}); });
@ -616,14 +586,7 @@ function shouldBehaveLikeERC1155() {
it('calls onERC1155BatchReceived', async function () { it('calls onERC1155BatchReceived', async function () {
await expect(this.tx) await expect(this.tx)
.to.emit(this.receiver, 'BatchReceived') .to.emit(this.receiver, 'BatchReceived')
.withArgs( .withArgs(this.holder, this.holder, this.args.ids, this.args.values, this.args.data, anyValue);
this.holder.address,
this.holder.address,
this.args.ids,
this.args.values,
this.args.data,
anyValue,
);
}); });
}); });
@ -647,14 +610,7 @@ function shouldBehaveLikeERC1155() {
it('calls onERC1155Received', async function () { it('calls onERC1155Received', async function () {
await expect(this.tx) await expect(this.tx)
.to.emit(this.receiver, 'BatchReceived') .to.emit(this.receiver, 'BatchReceived')
.withArgs( .withArgs(this.holder, this.holder, this.args.ids, this.args.values, this.args.data, anyValue);
this.holder.address,
this.holder.address,
this.args.ids,
this.args.values,
this.args.data,
anyValue,
);
}); });
}); });
}); });
@ -679,7 +635,7 @@ function shouldBehaveLikeERC1155() {
), ),
) )
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver') .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
.withArgs(receiver.target); .withArgs(receiver);
}); });
}); });
@ -726,7 +682,7 @@ function shouldBehaveLikeERC1155() {
), ),
) )
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver') .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
.withArgs(receiver.target); .withArgs(receiver);
}); });
}); });
@ -779,7 +735,7 @@ function shouldBehaveLikeERC1155() {
describe('to a contract that does not implement the required function', function () { describe('to a contract that does not implement the required function', function () {
it('reverts', async function () { it('reverts', async function () {
const invalidReceiver = this.token.target; const invalidReceiver = this.token;
await expect( await expect(
this.token this.token

@ -46,7 +46,7 @@ describe('ERC1155', function () {
it('emits a TransferSingle event', async function () { it('emits a TransferSingle event', async function () {
await expect(this.tx) await expect(this.tx)
.to.emit(this.token, 'TransferSingle') .to.emit(this.token, 'TransferSingle')
.withArgs(this.operator.address, ethers.ZeroAddress, this.holder.address, tokenId, mintValue); .withArgs(this.operator, ethers.ZeroAddress, this.holder, tokenId, mintValue);
}); });
it('credits the minted token value', async function () { it('credits the minted token value', async function () {
@ -80,7 +80,7 @@ describe('ERC1155', function () {
it('emits a TransferBatch event', async function () { it('emits a TransferBatch event', async function () {
await expect(this.tx) await expect(this.tx)
.to.emit(this.token, 'TransferBatch') .to.emit(this.token, 'TransferBatch')
.withArgs(this.operator.address, ethers.ZeroAddress, this.holder.address, tokenBatchIds, mintValues); .withArgs(this.operator, ethers.ZeroAddress, this.holder, tokenBatchIds, mintValues);
}); });
it('credits the minted batch of tokens', async function () { it('credits the minted batch of tokens', async function () {
@ -104,7 +104,7 @@ describe('ERC1155', function () {
it('reverts when burning a non-existent token id', async function () { it('reverts when burning a non-existent token id', async function () {
await expect(this.token.$_burn(this.holder, tokenId, mintValue)) await expect(this.token.$_burn(this.holder, tokenId, mintValue))
.to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
.withArgs(this.holder.address, 0, mintValue, tokenId); .withArgs(this.holder, 0, mintValue, tokenId);
}); });
it('reverts when burning more than available tokens', async function () { it('reverts when burning more than available tokens', async function () {
@ -112,7 +112,7 @@ describe('ERC1155', function () {
await expect(this.token.$_burn(this.holder, tokenId, mintValue + 1n)) await expect(this.token.$_burn(this.holder, tokenId, mintValue + 1n))
.to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
.withArgs(this.holder.address, mintValue, mintValue + 1n, tokenId); .withArgs(this.holder, mintValue, mintValue + 1n, tokenId);
}); });
describe('with minted-then-burnt tokens', function () { describe('with minted-then-burnt tokens', function () {
@ -124,7 +124,7 @@ describe('ERC1155', function () {
it('emits a TransferSingle event', async function () { it('emits a TransferSingle event', async function () {
await expect(this.tx) await expect(this.tx)
.to.emit(this.token, 'TransferSingle') .to.emit(this.token, 'TransferSingle')
.withArgs(this.operator.address, this.holder.address, ethers.ZeroAddress, tokenId, burnValue); .withArgs(this.operator, this.holder, ethers.ZeroAddress, tokenId, burnValue);
}); });
it('accounts for both minting and burning', async function () { it('accounts for both minting and burning', async function () {
@ -153,7 +153,7 @@ describe('ERC1155', function () {
it('reverts when burning a non-existent token id', async function () { it('reverts when burning a non-existent token id', async function () {
await expect(this.token.$_burnBatch(this.holder, tokenBatchIds, burnValues)) await expect(this.token.$_burnBatch(this.holder, tokenBatchIds, burnValues))
.to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
.withArgs(this.holder.address, 0, burnValues[0], tokenBatchIds[0]); .withArgs(this.holder, 0, burnValues[0], tokenBatchIds[0]);
}); });
describe('with minted-then-burnt tokens', function () { describe('with minted-then-burnt tokens', function () {
@ -165,7 +165,7 @@ describe('ERC1155', function () {
it('emits a TransferBatch event', async function () { it('emits a TransferBatch event', async function () {
await expect(this.tx) await expect(this.tx)
.to.emit(this.token, 'TransferBatch') .to.emit(this.token, 'TransferBatch')
.withArgs(this.operator.address, this.holder.address, ethers.ZeroAddress, tokenBatchIds, burnValues); .withArgs(this.operator, this.holder, ethers.ZeroAddress, tokenBatchIds, burnValues);
}); });
it('accounts for both minting and burning', async function () { it('accounts for both minting and burning', async function () {

@ -37,7 +37,7 @@ describe('ERC1155Burnable', function () {
it("unapproved accounts cannot burn the holder's tokens", async function () { it("unapproved accounts cannot burn the holder's tokens", async function () {
await expect(this.token.connect(this.other).burn(this.holder, ids[0], values[0] - 1n)) await expect(this.token.connect(this.other).burn(this.holder, ids[0], values[0] - 1n))
.to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll') .to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll')
.withArgs(this.other.address, this.holder.address); .withArgs(this.other, this.holder);
}); });
}); });
@ -60,7 +60,7 @@ describe('ERC1155Burnable', function () {
it("unapproved accounts cannot burn the holder's tokens", async function () { it("unapproved accounts cannot burn the holder's tokens", async function () {
await expect(this.token.connect(this.other).burnBatch(this.holder, ids, [values[0] - 1n, values[1] - 2n])) await expect(this.token.connect(this.other).burnBatch(this.holder, ids, [values[0] - 1n, values[1] - 2n]))
.to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll') .to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll')
.withArgs(this.other.address, this.holder.address); .withArgs(this.other, this.holder);
}); });
}); });
}); });

@ -8,7 +8,7 @@ async function fixture() {
return { token, holder, operator, receiver, other }; return { token, holder, operator, receiver, other };
} }
contract('ERC1155Pausable', function () { describe('ERC1155Pausable', function () {
const firstTokenId = 37n; const firstTokenId = 37n;
const firstTokenValue = 42n; const firstTokenValue = 42n;
const secondTokenId = 19842n; const secondTokenId = 19842n;

@ -58,7 +58,7 @@ function shouldBehaveLikeERC20(initialSupply, opts = {}) {
it('emits a transfer event', async function () { it('emits a transfer event', async function () {
await expect(this.tx) await expect(this.tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.initialHolder.address, this.anotherAccount.address, value); .withArgs(this.initialHolder, this.anotherAccount, value);
}); });
if (forcedApproval) { if (forcedApproval) {
@ -85,7 +85,7 @@ function shouldBehaveLikeERC20(initialSupply, opts = {}) {
this.token.connect(this.recipient).transferFrom(this.initialHolder, this.anotherAccount, value), this.token.connect(this.recipient).transferFrom(this.initialHolder, this.anotherAccount, value),
) )
.to.revertedWithCustomError(this.token, 'ERC20InsufficientBalance') .to.revertedWithCustomError(this.token, 'ERC20InsufficientBalance')
.withArgs(this.initialHolder.address, value - 1n, value); .withArgs(this.initialHolder, value - 1n, value);
}); });
}); });
@ -102,7 +102,7 @@ function shouldBehaveLikeERC20(initialSupply, opts = {}) {
this.token.connect(this.recipient).transferFrom(this.initialHolder, this.anotherAccount, value), this.token.connect(this.recipient).transferFrom(this.initialHolder, this.anotherAccount, value),
) )
.to.be.revertedWithCustomError(this.token, 'ERC20InsufficientAllowance') .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientAllowance')
.withArgs(this.recipient.address, allowance, value); .withArgs(this.recipient, allowance, value);
}); });
it('reverts when the token owner does not have enough balance', async function () { it('reverts when the token owner does not have enough balance', async function () {
@ -112,7 +112,7 @@ function shouldBehaveLikeERC20(initialSupply, opts = {}) {
this.token.connect(this.recipient).transferFrom(this.initialHolder, this.anotherAccount, value), this.token.connect(this.recipient).transferFrom(this.initialHolder, this.anotherAccount, value),
) )
.to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance')
.withArgs(this.initialHolder.address, value - 1n, value); .withArgs(this.initialHolder, value - 1n, value);
}); });
}); });
@ -166,7 +166,7 @@ function shouldBehaveLikeERC20Transfer(balance) {
const value = balance + 1n; const value = balance + 1n;
await expect(this.transfer(this.initialHolder, this.recipient, value)) await expect(this.transfer(this.initialHolder, this.recipient, value))
.to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance')
.withArgs(this.initialHolder.address, balance, value); .withArgs(this.initialHolder, balance, value);
}); });
describe('when the sender transfers all balance', function () { describe('when the sender transfers all balance', function () {
@ -181,9 +181,7 @@ function shouldBehaveLikeERC20Transfer(balance) {
}); });
it('emits a transfer event', async function () { it('emits a transfer event', async function () {
await expect(this.tx) await expect(this.tx).to.emit(this.token, 'Transfer').withArgs(this.initialHolder, this.recipient, value);
.to.emit(this.token, 'Transfer')
.withArgs(this.initialHolder.address, this.recipient.address, value);
}); });
}); });
@ -199,9 +197,7 @@ function shouldBehaveLikeERC20Transfer(balance) {
}); });
it('emits a transfer event', async function () { it('emits a transfer event', async function () {
await expect(this.tx) await expect(this.tx).to.emit(this.token, 'Transfer').withArgs(this.initialHolder, this.recipient, value);
.to.emit(this.token, 'Transfer')
.withArgs(this.initialHolder.address, this.recipient.address, value);
}); });
}); });
}); });
@ -221,7 +217,7 @@ function shouldBehaveLikeERC20Approve(supply) {
it('emits an approval event', async function () { it('emits an approval event', async function () {
await expect(this.approve(this.initialHolder, this.recipient, value)) await expect(this.approve(this.initialHolder, this.recipient, value))
.to.emit(this.token, 'Approval') .to.emit(this.token, 'Approval')
.withArgs(this.initialHolder.address, this.recipient.address, value); .withArgs(this.initialHolder, this.recipient, value);
}); });
it('approves the requested value when there was no approved value before', async function () { it('approves the requested value when there was no approved value before', async function () {
@ -244,7 +240,7 @@ function shouldBehaveLikeERC20Approve(supply) {
it('emits an approval event', async function () { it('emits an approval event', async function () {
await expect(this.approve(this.initialHolder, this.recipient, value)) await expect(this.approve(this.initialHolder, this.recipient, value))
.to.emit(this.token, 'Approval') .to.emit(this.token, 'Approval')
.withArgs(this.initialHolder.address, this.recipient.address, value); .withArgs(this.initialHolder, this.recipient, value);
}); });
it('approves the requested value when there was no approved value before', async function () { it('approves the requested value when there was no approved value before', async function () {

@ -73,9 +73,7 @@ describe('ERC20', function () {
}); });
it('emits Transfer event', async function () { it('emits Transfer event', async function () {
await expect(this.tx) await expect(this.tx).to.emit(this.token, 'Transfer').withArgs(ethers.ZeroAddress, this.recipient, value);
.to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.recipient.address, value);
}); });
}); });
}); });
@ -91,7 +89,7 @@ describe('ERC20', function () {
it('rejects burning more than balance', async function () { it('rejects burning more than balance', async function () {
await expect(this.token.$_burn(this.initialHolder, initialSupply + 1n)) await expect(this.token.$_burn(this.initialHolder, initialSupply + 1n))
.to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance')
.withArgs(this.initialHolder.address, initialSupply, initialSupply + 1n); .withArgs(this.initialHolder, initialSupply, initialSupply + 1n);
}); });
const describeBurn = function (description, value) { const describeBurn = function (description, value) {
@ -111,7 +109,7 @@ describe('ERC20', function () {
it('emits Transfer event', async function () { it('emits Transfer event', async function () {
await expect(this.tx) await expect(this.tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.initialHolder.address, ethers.ZeroAddress, value); .withArgs(this.initialHolder, ethers.ZeroAddress, value);
}); });
}); });
}; };
@ -130,9 +128,7 @@ describe('ERC20', function () {
it('from is the zero address', async function () { it('from is the zero address', async function () {
const tx = await this.token.$_update(ethers.ZeroAddress, this.initialHolder, value); const tx = await this.token.$_update(ethers.ZeroAddress, this.initialHolder, value);
await expect(tx) await expect(tx).to.emit(this.token, 'Transfer').withArgs(ethers.ZeroAddress, this.initialHolder, value);
.to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.initialHolder.address, value);
expect(await this.token.totalSupply()).to.equal(this.totalSupply + value); expect(await this.token.totalSupply()).to.equal(this.totalSupply + value);
await expect(tx).to.changeTokenBalance(this.token, this.initialHolder, value); await expect(tx).to.changeTokenBalance(this.token, this.initialHolder, value);
@ -140,9 +136,7 @@ describe('ERC20', function () {
it('to is the zero address', async function () { it('to is the zero address', async function () {
const tx = await this.token.$_update(this.initialHolder, ethers.ZeroAddress, value); const tx = await this.token.$_update(this.initialHolder, ethers.ZeroAddress, value);
await expect(tx) await expect(tx).to.emit(this.token, 'Transfer').withArgs(this.initialHolder, ethers.ZeroAddress, value);
.to.emit(this.token, 'Transfer')
.withArgs(this.initialHolder.address, ethers.ZeroAddress, value);
expect(await this.token.totalSupply()).to.equal(this.totalSupply - value); expect(await this.token.totalSupply()).to.equal(this.totalSupply - value);
await expect(tx).to.changeTokenBalance(this.token, this.initialHolder, -value); await expect(tx).to.changeTokenBalance(this.token, this.initialHolder, -value);
@ -161,15 +155,13 @@ describe('ERC20', function () {
it('reverts without balance', async function () { it('reverts without balance', async function () {
await expect(this.token.$_update(this.recipient, this.recipient, value)) await expect(this.token.$_update(this.recipient, this.recipient, value))
.to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance')
.withArgs(this.recipient.address, 0n, value); .withArgs(this.recipient, 0n, value);
}); });
it('executes with balance', async function () { it('executes with balance', async function () {
const tx = await this.token.$_update(this.initialHolder, this.initialHolder, value); const tx = await this.token.$_update(this.initialHolder, this.initialHolder, value);
await expect(tx).to.changeTokenBalance(this.token, this.initialHolder, 0n); await expect(tx).to.changeTokenBalance(this.token, this.initialHolder, 0n);
await expect(tx) await expect(tx).to.emit(this.token, 'Transfer').withArgs(this.initialHolder, this.initialHolder, value);
.to.emit(this.token, 'Transfer')
.withArgs(this.initialHolder.address, this.initialHolder.address, value);
}); });
}); });
}); });

@ -26,7 +26,7 @@ describe('ERC20Burnable', function () {
await expect(this.token.connect(this.owner).burn(value)) await expect(this.token.connect(this.owner).burn(value))
.to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance')
.withArgs(this.owner.address, this.initialBalance, value); .withArgs(this.owner, this.initialBalance, value);
}); });
describe('on success', function () { describe('on success', function () {
@ -44,9 +44,7 @@ describe('ERC20Burnable', function () {
}); });
it('emits a transfer event', async function () { it('emits a transfer event', async function () {
await expect(this.tx) await expect(this.tx).to.emit(this.token, 'Transfer').withArgs(this.owner, ethers.ZeroAddress, value);
.to.emit(this.token, 'Transfer')
.withArgs(this.owner.address, ethers.ZeroAddress, value);
}); });
}); });
} }
@ -62,7 +60,7 @@ describe('ERC20Burnable', function () {
await expect(this.token.connect(this.burner).burnFrom(this.owner, value)) await expect(this.token.connect(this.burner).burnFrom(this.owner, value))
.to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance')
.withArgs(this.owner.address, this.initialBalance, value); .withArgs(this.owner, this.initialBalance, value);
}); });
it('if not enough allowance', async function () { it('if not enough allowance', async function () {
@ -72,7 +70,7 @@ describe('ERC20Burnable', function () {
await expect(this.token.connect(this.burner).burnFrom(this.owner, allowance + 1n)) await expect(this.token.connect(this.burner).burnFrom(this.owner, allowance + 1n))
.to.be.revertedWithCustomError(this.token, 'ERC20InsufficientAllowance') .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientAllowance')
.withArgs(this.burner.address, allowance, allowance + 1n); .withArgs(this.burner, allowance, allowance + 1n);
}); });
}); });
@ -98,9 +96,7 @@ describe('ERC20Burnable', function () {
}); });
it('emits a transfer event', async function () { it('emits a transfer event', async function () {
await expect(this.tx) await expect(this.tx).to.emit(this.token, 'Transfer').withArgs(this.owner, ethers.ZeroAddress, value);
.to.emit(this.token, 'Transfer')
.withArgs(this.owner.address, ethers.ZeroAddress, value);
}); });
}); });
} }

@ -56,13 +56,13 @@ describe('ERC20FlashMint', function () {
const tx = await this.token.flashLoan(receiver, this.token, loanValue, '0x'); const tx = await this.token.flashLoan(receiver, this.token, loanValue, '0x');
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, receiver.target, loanValue) .withArgs(ethers.ZeroAddress, receiver, loanValue)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(receiver.target, ethers.ZeroAddress, loanValue) .withArgs(receiver, ethers.ZeroAddress, loanValue)
.to.emit(receiver, 'BalanceOf') .to.emit(receiver, 'BalanceOf')
.withArgs(this.token.target, receiver.target, loanValue) .withArgs(this.token, receiver, loanValue)
.to.emit(receiver, 'TotalSupply') .to.emit(receiver, 'TotalSupply')
.withArgs(this.token.target, initialSupply + loanValue); .withArgs(this.token, initialSupply + loanValue);
await expect(tx).to.changeTokenBalance(this.token, receiver, 0); await expect(tx).to.changeTokenBalance(this.token, receiver, 0);
expect(await this.token.totalSupply()).to.equal(initialSupply); expect(await this.token.totalSupply()).to.equal(initialSupply);
@ -73,14 +73,14 @@ describe('ERC20FlashMint', function () {
const receiver = await ethers.deployContract('ERC3156FlashBorrowerMock', [false, true]); const receiver = await ethers.deployContract('ERC3156FlashBorrowerMock', [false, true]);
await expect(this.token.flashLoan(receiver, this.token, loanValue, '0x')) await expect(this.token.flashLoan(receiver, this.token, loanValue, '0x'))
.to.be.revertedWithCustomError(this.token, 'ERC3156InvalidReceiver') .to.be.revertedWithCustomError(this.token, 'ERC3156InvalidReceiver')
.withArgs(receiver.target); .withArgs(receiver);
}); });
it('missing approval', async function () { it('missing approval', async function () {
const receiver = await ethers.deployContract('ERC3156FlashBorrowerMock', [true, false]); const receiver = await ethers.deployContract('ERC3156FlashBorrowerMock', [true, false]);
await expect(this.token.flashLoan(receiver, this.token, loanValue, '0x')) await expect(this.token.flashLoan(receiver, this.token, loanValue, '0x'))
.to.be.revertedWithCustomError(this.token, 'ERC20InsufficientAllowance') .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientAllowance')
.withArgs(this.token.target, 0, loanValue); .withArgs(this.token, 0, loanValue);
}); });
it('unavailable funds', async function () { it('unavailable funds', async function () {
@ -88,7 +88,7 @@ describe('ERC20FlashMint', function () {
const data = this.token.interface.encodeFunctionData('transfer', [this.other.address, 10]); const data = this.token.interface.encodeFunctionData('transfer', [this.other.address, 10]);
await expect(this.token.flashLoan(receiver, this.token, loanValue, data)) await expect(this.token.flashLoan(receiver, this.token, loanValue, data))
.to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance')
.withArgs(receiver.target, loanValue - 10n, loanValue); .withArgs(receiver, loanValue - 10n, loanValue);
}); });
it('more than maxFlashLoan', async function () { it('more than maxFlashLoan', async function () {
@ -109,7 +109,7 @@ describe('ERC20FlashMint', function () {
const tx = await this.token.$_mint(this.receiver, receiverInitialBalance); const tx = await this.token.$_mint(this.receiver, receiverInitialBalance);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.receiver.target, receiverInitialBalance); .withArgs(ethers.ZeroAddress, this.receiver, receiverInitialBalance);
await expect(tx).to.changeTokenBalance(this.token, this.receiver, receiverInitialBalance); await expect(tx).to.changeTokenBalance(this.token, this.receiver, receiverInitialBalance);
await this.token.setFlashFee(flashFee); await this.token.setFlashFee(flashFee);
@ -120,13 +120,13 @@ describe('ERC20FlashMint', function () {
const tx = await this.token.flashLoan(this.receiver, this.token, loanValue, '0x'); const tx = await this.token.flashLoan(this.receiver, this.token, loanValue, '0x');
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.receiver.target, loanValue) .withArgs(ethers.ZeroAddress, this.receiver, loanValue)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.receiver.target, ethers.ZeroAddress, loanValue + flashFee) .withArgs(this.receiver, ethers.ZeroAddress, loanValue + flashFee)
.to.emit(this.receiver, 'BalanceOf') .to.emit(this.receiver, 'BalanceOf')
.withArgs(this.token.target, this.receiver.target, receiverInitialBalance + loanValue) .withArgs(this.token, this.receiver, receiverInitialBalance + loanValue)
.to.emit(this.receiver, 'TotalSupply') .to.emit(this.receiver, 'TotalSupply')
.withArgs(this.token.target, initialSupply + receiverInitialBalance + loanValue); .withArgs(this.token, initialSupply + receiverInitialBalance + loanValue);
await expect(tx).to.changeTokenBalances(this.token, [this.receiver, ethers.ZeroAddress], [-flashFee, 0]); await expect(tx).to.changeTokenBalances(this.token, [this.receiver, ethers.ZeroAddress], [-flashFee, 0]);
expect(await this.token.totalSupply()).to.equal(initialSupply + receiverInitialBalance - flashFee); expect(await this.token.totalSupply()).to.equal(initialSupply + receiverInitialBalance - flashFee);
@ -136,20 +136,20 @@ describe('ERC20FlashMint', function () {
it('custom flash fee receiver', async function () { it('custom flash fee receiver', async function () {
const flashFeeReceiverAddress = this.anotherAccount; const flashFeeReceiverAddress = this.anotherAccount;
await this.token.setFlashFeeReceiver(flashFeeReceiverAddress); await this.token.setFlashFeeReceiver(flashFeeReceiverAddress);
expect(await this.token.$_flashFeeReceiver()).to.equal(flashFeeReceiverAddress.address); expect(await this.token.$_flashFeeReceiver()).to.equal(flashFeeReceiverAddress);
const tx = await this.token.flashLoan(this.receiver, this.token, loanValue, '0x'); const tx = await this.token.flashLoan(this.receiver, this.token, loanValue, '0x');
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.receiver.target, loanValue) .withArgs(ethers.ZeroAddress, this.receiver, loanValue)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.receiver.target, ethers.ZeroAddress, loanValue) .withArgs(this.receiver, ethers.ZeroAddress, loanValue)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.receiver.target, flashFeeReceiverAddress.address, flashFee) .withArgs(this.receiver, flashFeeReceiverAddress, flashFee)
.to.emit(this.receiver, 'BalanceOf') .to.emit(this.receiver, 'BalanceOf')
.withArgs(this.token.target, this.receiver.target, receiverInitialBalance + loanValue) .withArgs(this.token, this.receiver, receiverInitialBalance + loanValue)
.to.emit(this.receiver, 'TotalSupply') .to.emit(this.receiver, 'TotalSupply')
.withArgs(this.token.target, initialSupply + receiverInitialBalance + loanValue); .withArgs(this.token, initialSupply + receiverInitialBalance + loanValue);
await expect(tx).to.changeTokenBalances( await expect(tx).to.changeTokenBalances(
this.token, this.token,
[this.receiver, flashFeeReceiverAddress], [this.receiver, flashFeeReceiverAddress],

@ -3,7 +3,7 @@ const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { getDomain, domainSeparator, Permit } = require('../../../helpers/eip712'); const { getDomain, domainSeparator, Permit } = require('../../../helpers/eip712');
const { bigint: time } = require('../../../helpers/time'); const time = require('../../../helpers/time');
const name = 'My Token'; const name = 'My Token';
const symbol = 'MTKN'; const symbol = 'MTKN';
@ -81,7 +81,7 @@ describe('ERC20Permit', function () {
await expect(this.token.permit(this.owner, this.spender, value, maxDeadline, v, r, s)) await expect(this.token.permit(this.owner, this.spender, value, maxDeadline, v, r, s))
.to.be.revertedWithCustomError(this.token, 'ERC2612InvalidSigner') .to.be.revertedWithCustomError(this.token, 'ERC2612InvalidSigner')
.withArgs(recovered, this.owner.address); .withArgs(recovered, this.owner);
}); });
it('rejects other signature', async function () { it('rejects other signature', async function () {
@ -91,7 +91,7 @@ describe('ERC20Permit', function () {
await expect(this.token.permit(this.owner, this.spender, value, maxDeadline, v, r, s)) await expect(this.token.permit(this.owner, this.spender, value, maxDeadline, v, r, s))
.to.be.revertedWithCustomError(this.token, 'ERC2612InvalidSigner') .to.be.revertedWithCustomError(this.token, 'ERC2612InvalidSigner')
.withArgs(this.other.address, this.owner.address); .withArgs(this.other, this.owner);
}); });
it('rejects expired permit', async function () { it('rejects expired permit', async function () {

@ -4,7 +4,7 @@ const { loadFixture, mine } = require('@nomicfoundation/hardhat-network-helpers'
const { getDomain, Delegation } = require('../../../helpers/eip712'); const { getDomain, Delegation } = require('../../../helpers/eip712');
const { batchInBlock } = require('../../../helpers/txpool'); const { batchInBlock } = require('../../../helpers/txpool');
const { bigint: time } = require('../../../helpers/time'); const time = require('../../../helpers/time');
const { shouldBehaveLikeVotes } = require('../../../governance/utils/Votes.behavior'); const { shouldBehaveLikeVotes } = require('../../../governance/utils/Votes.behavior');
@ -75,11 +75,11 @@ describe('ERC20Votes', function () {
await expect(tx) await expect(tx)
.to.emit(this.token, 'DelegateChanged') .to.emit(this.token, 'DelegateChanged')
.withArgs(this.holder.address, ethers.ZeroAddress, this.holder.address) .withArgs(this.holder, ethers.ZeroAddress, this.holder)
.to.emit(this.token, 'DelegateVotesChanged') .to.emit(this.token, 'DelegateVotesChanged')
.withArgs(this.holder.address, 0n, supply); .withArgs(this.holder, 0n, supply);
expect(await this.token.delegates(this.holder)).to.equal(this.holder.address); expect(await this.token.delegates(this.holder)).to.equal(this.holder);
expect(await this.token.getVotes(this.holder)).to.equal(supply); expect(await this.token.getVotes(this.holder)).to.equal(supply);
expect(await this.token.getPastVotes(this.holder, timepoint - 1n)).to.equal(0n); expect(await this.token.getPastVotes(this.holder, timepoint - 1n)).to.equal(0n);
await mine(); await mine();
@ -91,10 +91,10 @@ describe('ERC20Votes', function () {
await expect(this.token.connect(this.holder).delegate(this.holder)) await expect(this.token.connect(this.holder).delegate(this.holder))
.to.emit(this.token, 'DelegateChanged') .to.emit(this.token, 'DelegateChanged')
.withArgs(this.holder.address, ethers.ZeroAddress, this.holder.address) .withArgs(this.holder, ethers.ZeroAddress, this.holder)
.to.not.emit(this.token, 'DelegateVotesChanged'); .to.not.emit(this.token, 'DelegateVotesChanged');
expect(await this.token.delegates(this.holder)).to.equal(this.holder.address); expect(await this.token.delegates(this.holder)).to.equal(this.holder);
}); });
}); });
@ -125,11 +125,11 @@ describe('ERC20Votes', function () {
await expect(tx) await expect(tx)
.to.emit(this.token, 'DelegateChanged') .to.emit(this.token, 'DelegateChanged')
.withArgs(this.holder.address, ethers.ZeroAddress, this.holder.address) .withArgs(this.holder, ethers.ZeroAddress, this.holder)
.to.emit(this.token, 'DelegateVotesChanged') .to.emit(this.token, 'DelegateVotesChanged')
.withArgs(this.holder.address, 0n, supply); .withArgs(this.holder, 0n, supply);
expect(await this.token.delegates(this.holder)).to.equal(this.holder.address); expect(await this.token.delegates(this.holder)).to.equal(this.holder);
expect(await this.token.getVotes(this.holder)).to.equal(supply); expect(await this.token.getVotes(this.holder)).to.equal(supply);
expect(await this.token.getPastVotes(this.holder, timepoint - 1n)).to.equal(0n); expect(await this.token.getPastVotes(this.holder, timepoint - 1n)).to.equal(0n);
@ -154,7 +154,7 @@ describe('ERC20Votes', function () {
await expect(this.token.delegateBySig(this.holder, nonce, ethers.MaxUint256, v, r, s)) await expect(this.token.delegateBySig(this.holder, nonce, ethers.MaxUint256, v, r, s))
.to.be.revertedWithCustomError(this.token, 'InvalidAccountNonce') .to.be.revertedWithCustomError(this.token, 'InvalidAccountNonce')
.withArgs(this.holder.address, nonce + 1n); .withArgs(this.holder, nonce + 1n);
}); });
it('rejects bad delegatee', async function () { it('rejects bad delegatee', async function () {
@ -175,9 +175,9 @@ describe('ERC20Votes', function () {
const { args } = await tx const { args } = await tx
.wait() .wait()
.then(receipt => receipt.logs.find(event => event.fragment.name == 'DelegateChanged')); .then(receipt => receipt.logs.find(event => event.fragment.name == 'DelegateChanged'));
expect(args[0]).to.not.equal(this.holder.address); expect(args[0]).to.not.equal(this.holder);
expect(args[1]).to.equal(ethers.ZeroAddress); expect(args[1]).to.equal(ethers.ZeroAddress);
expect(args[2]).to.equal(this.delegatee.address); expect(args[2]).to.equal(this.delegatee);
}); });
it('rejects bad nonce', async function () { it('rejects bad nonce', async function () {
@ -238,20 +238,20 @@ describe('ERC20Votes', function () {
}); });
it('call', async function () { it('call', async function () {
expect(await this.token.delegates(this.holder)).to.equal(this.holder.address); expect(await this.token.delegates(this.holder)).to.equal(this.holder);
const tx = await this.token.connect(this.holder).delegate(this.delegatee); const tx = await this.token.connect(this.holder).delegate(this.delegatee);
const timepoint = await time.clockFromReceipt[mode](tx); const timepoint = await time.clockFromReceipt[mode](tx);
await expect(tx) await expect(tx)
.to.emit(this.token, 'DelegateChanged') .to.emit(this.token, 'DelegateChanged')
.withArgs(this.holder.address, this.holder.address, this.delegatee.address) .withArgs(this.holder, this.holder, this.delegatee)
.to.emit(this.token, 'DelegateVotesChanged') .to.emit(this.token, 'DelegateVotesChanged')
.withArgs(this.holder.address, supply, 0n) .withArgs(this.holder, supply, 0n)
.to.emit(this.token, 'DelegateVotesChanged') .to.emit(this.token, 'DelegateVotesChanged')
.withArgs(this.delegatee.address, 0n, supply); .withArgs(this.delegatee, 0n, supply);
expect(await this.token.delegates(this.holder)).to.equal(this.delegatee.address); expect(await this.token.delegates(this.holder)).to.equal(this.delegatee);
expect(await this.token.getVotes(this.holder)).to.equal(0n); expect(await this.token.getVotes(this.holder)).to.equal(0n);
expect(await this.token.getVotes(this.delegatee)).to.equal(supply); expect(await this.token.getVotes(this.delegatee)).to.equal(supply);
@ -271,7 +271,7 @@ describe('ERC20Votes', function () {
it('no delegation', async function () { it('no delegation', async function () {
await expect(this.token.connect(this.holder).transfer(this.recipient, 1n)) await expect(this.token.connect(this.holder).transfer(this.recipient, 1n))
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.recipient.address, 1n) .withArgs(this.holder, this.recipient, 1n)
.to.not.emit(this.token, 'DelegateVotesChanged'); .to.not.emit(this.token, 'DelegateVotesChanged');
this.holderVotes = 0n; this.holderVotes = 0n;
@ -284,9 +284,9 @@ describe('ERC20Votes', function () {
const tx = await this.token.connect(this.holder).transfer(this.recipient, 1n); const tx = await this.token.connect(this.holder).transfer(this.recipient, 1n);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.recipient.address, 1n) .withArgs(this.holder, this.recipient, 1n)
.to.emit(this.token, 'DelegateVotesChanged') .to.emit(this.token, 'DelegateVotesChanged')
.withArgs(this.holder.address, supply, supply - 1n); .withArgs(this.holder, supply, supply - 1n);
const { logs } = await tx.wait(); const { logs } = await tx.wait();
const { index } = logs.find(event => event.fragment.name == 'DelegateVotesChanged'); const { index } = logs.find(event => event.fragment.name == 'DelegateVotesChanged');
@ -304,9 +304,9 @@ describe('ERC20Votes', function () {
const tx = await this.token.connect(this.holder).transfer(this.recipient, 1n); const tx = await this.token.connect(this.holder).transfer(this.recipient, 1n);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.recipient.address, 1n) .withArgs(this.holder, this.recipient, 1n)
.to.emit(this.token, 'DelegateVotesChanged') .to.emit(this.token, 'DelegateVotesChanged')
.withArgs(this.recipient.address, 0n, 1n); .withArgs(this.recipient, 0n, 1n);
const { logs } = await tx.wait(); const { logs } = await tx.wait();
const { index } = logs.find(event => event.fragment.name == 'DelegateVotesChanged'); const { index } = logs.find(event => event.fragment.name == 'DelegateVotesChanged');
@ -325,11 +325,11 @@ describe('ERC20Votes', function () {
const tx = await this.token.connect(this.holder).transfer(this.recipient, 1n); const tx = await this.token.connect(this.holder).transfer(this.recipient, 1n);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.recipient.address, 1n) .withArgs(this.holder, this.recipient, 1n)
.to.emit(this.token, 'DelegateVotesChanged') .to.emit(this.token, 'DelegateVotesChanged')
.withArgs(this.holder.address, supply, supply - 1n) .withArgs(this.holder, supply, supply - 1n)
.to.emit(this.token, 'DelegateVotesChanged') .to.emit(this.token, 'DelegateVotesChanged')
.withArgs(this.recipient.address, 0n, 1n); .withArgs(this.recipient, 0n, 1n);
const { logs } = await tx.wait(); const { logs } = await tx.wait();
const { index } = logs.find(event => event.fragment.name == 'DelegateVotesChanged'); const { index } = logs.find(event => event.fragment.name == 'DelegateVotesChanged');

@ -48,7 +48,7 @@ describe('ERC20Wrapper', function () {
}); });
it('has underlying', async function () { it('has underlying', async function () {
expect(await this.token.underlying()).to.be.equal(this.underlying.target); expect(await this.token.underlying()).to.be.equal(this.underlying);
}); });
describe('deposit', function () { describe('deposit', function () {
@ -58,9 +58,9 @@ describe('ERC20Wrapper', function () {
const tx = await this.token.connect(this.initialHolder).depositFor(this.initialHolder, initialSupply); const tx = await this.token.connect(this.initialHolder).depositFor(this.initialHolder, initialSupply);
await expect(tx) await expect(tx)
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.initialHolder.address, this.token.target, initialSupply) .withArgs(this.initialHolder, this.token, initialSupply)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.initialHolder.address, initialSupply); .withArgs(ethers.ZeroAddress, this.initialHolder, initialSupply);
await expect(tx).to.changeTokenBalances( await expect(tx).to.changeTokenBalances(
this.underlying, this.underlying,
[this.initialHolder, this.token], [this.initialHolder, this.token],
@ -72,7 +72,7 @@ describe('ERC20Wrapper', function () {
it('reverts when missing approval', async function () { it('reverts when missing approval', async function () {
await expect(this.token.connect(this.initialHolder).depositFor(this.initialHolder, initialSupply)) await expect(this.token.connect(this.initialHolder).depositFor(this.initialHolder, initialSupply))
.to.be.revertedWithCustomError(this.underlying, 'ERC20InsufficientAllowance') .to.be.revertedWithCustomError(this.underlying, 'ERC20InsufficientAllowance')
.withArgs(this.token.target, 0, initialSupply); .withArgs(this.token, 0, initialSupply);
}); });
it('reverts when inssuficient balance', async function () { it('reverts when inssuficient balance', async function () {
@ -80,7 +80,7 @@ describe('ERC20Wrapper', function () {
await expect(this.token.connect(this.initialHolder).depositFor(this.initialHolder, ethers.MaxUint256)) await expect(this.token.connect(this.initialHolder).depositFor(this.initialHolder, ethers.MaxUint256))
.to.be.revertedWithCustomError(this.underlying, 'ERC20InsufficientBalance') .to.be.revertedWithCustomError(this.underlying, 'ERC20InsufficientBalance')
.withArgs(this.initialHolder.address, initialSupply, ethers.MaxUint256); .withArgs(this.initialHolder, initialSupply, ethers.MaxUint256);
}); });
it('deposits to other account', async function () { it('deposits to other account', async function () {
@ -89,9 +89,9 @@ describe('ERC20Wrapper', function () {
const tx = await this.token.connect(this.initialHolder).depositFor(this.recipient, initialSupply); const tx = await this.token.connect(this.initialHolder).depositFor(this.recipient, initialSupply);
await expect(tx) await expect(tx)
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.initialHolder.address, this.token.target, initialSupply) .withArgs(this.initialHolder, this.token, initialSupply)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.recipient.address, initialSupply); .withArgs(ethers.ZeroAddress, this.recipient, initialSupply);
await expect(tx).to.changeTokenBalances( await expect(tx).to.changeTokenBalances(
this.underlying, this.underlying,
[this.initialHolder, this.token], [this.initialHolder, this.token],
@ -105,7 +105,7 @@ describe('ERC20Wrapper', function () {
await expect(this.token.connect(this.initialHolder).depositFor(this.token, ethers.MaxUint256)) await expect(this.token.connect(this.initialHolder).depositFor(this.token, ethers.MaxUint256))
.to.be.revertedWithCustomError(this.token, 'ERC20InvalidReceiver') .to.be.revertedWithCustomError(this.token, 'ERC20InvalidReceiver')
.withArgs(this.token.target); .withArgs(this.token);
}); });
}); });
@ -118,7 +118,7 @@ describe('ERC20Wrapper', function () {
it('reverts when inssuficient balance', async function () { it('reverts when inssuficient balance', async function () {
await expect(this.token.connect(this.initialHolder).withdrawTo(this.initialHolder, ethers.MaxInt256)) await expect(this.token.connect(this.initialHolder).withdrawTo(this.initialHolder, ethers.MaxInt256))
.to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance') .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance')
.withArgs(this.initialHolder.address, initialSupply, ethers.MaxInt256); .withArgs(this.initialHolder, initialSupply, ethers.MaxInt256);
}); });
it('executes when operation is valid', async function () { it('executes when operation is valid', async function () {
@ -127,9 +127,9 @@ describe('ERC20Wrapper', function () {
const tx = await this.token.connect(this.initialHolder).withdrawTo(this.initialHolder, value); const tx = await this.token.connect(this.initialHolder).withdrawTo(this.initialHolder, value);
await expect(tx) await expect(tx)
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.token.target, this.initialHolder.address, value) .withArgs(this.token, this.initialHolder, value)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.initialHolder.address, ethers.ZeroAddress, value); .withArgs(this.initialHolder, ethers.ZeroAddress, value);
await expect(tx).to.changeTokenBalances(this.underlying, [this.token, this.initialHolder], [-value, value]); await expect(tx).to.changeTokenBalances(this.underlying, [this.token, this.initialHolder], [-value, value]);
await expect(tx).to.changeTokenBalance(this.token, this.initialHolder, -value); await expect(tx).to.changeTokenBalance(this.token, this.initialHolder, -value);
}); });
@ -138,9 +138,9 @@ describe('ERC20Wrapper', function () {
const tx = await this.token.connect(this.initialHolder).withdrawTo(this.initialHolder, initialSupply); const tx = await this.token.connect(this.initialHolder).withdrawTo(this.initialHolder, initialSupply);
await expect(tx) await expect(tx)
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.token.target, this.initialHolder.address, initialSupply) .withArgs(this.token, this.initialHolder, initialSupply)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.initialHolder.address, ethers.ZeroAddress, initialSupply); .withArgs(this.initialHolder, ethers.ZeroAddress, initialSupply);
await expect(tx).to.changeTokenBalances( await expect(tx).to.changeTokenBalances(
this.underlying, this.underlying,
[this.token, this.initialHolder], [this.token, this.initialHolder],
@ -153,9 +153,9 @@ describe('ERC20Wrapper', function () {
const tx = await this.token.connect(this.initialHolder).withdrawTo(this.recipient, initialSupply); const tx = await this.token.connect(this.initialHolder).withdrawTo(this.recipient, initialSupply);
await expect(tx) await expect(tx)
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.token.target, this.recipient.address, initialSupply) .withArgs(this.token, this.recipient, initialSupply)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.initialHolder.address, ethers.ZeroAddress, initialSupply); .withArgs(this.initialHolder, ethers.ZeroAddress, initialSupply);
await expect(tx).to.changeTokenBalances( await expect(tx).to.changeTokenBalances(
this.underlying, this.underlying,
[this.token, this.initialHolder, this.recipient], [this.token, this.initialHolder, this.recipient],
@ -167,7 +167,7 @@ describe('ERC20Wrapper', function () {
it('reverts withdrawing to the wrapper contract', async function () { it('reverts withdrawing to the wrapper contract', async function () {
await expect(this.token.connect(this.initialHolder).withdrawTo(this.token, initialSupply)) await expect(this.token.connect(this.initialHolder).withdrawTo(this.token, initialSupply))
.to.be.revertedWithCustomError(this.token, 'ERC20InvalidReceiver') .to.be.revertedWithCustomError(this.token, 'ERC20InvalidReceiver')
.withArgs(this.token.target); .withArgs(this.token);
}); });
}); });
@ -177,7 +177,7 @@ describe('ERC20Wrapper', function () {
await this.token.connect(this.initialHolder).depositFor(this.initialHolder, initialSupply); await this.token.connect(this.initialHolder).depositFor(this.initialHolder, initialSupply);
const tx = await this.token.$_recover(this.recipient); const tx = await this.token.$_recover(this.recipient);
await expect(tx).to.emit(this.token, 'Transfer').withArgs(ethers.ZeroAddress, this.recipient.address, 0n); await expect(tx).to.emit(this.token, 'Transfer').withArgs(ethers.ZeroAddress, this.recipient, 0n);
await expect(tx).to.changeTokenBalance(this.token, this.recipient, 0); await expect(tx).to.changeTokenBalance(this.token, this.recipient, 0);
}); });
@ -185,9 +185,7 @@ describe('ERC20Wrapper', function () {
await this.underlying.connect(this.initialHolder).transfer(this.token, initialSupply); await this.underlying.connect(this.initialHolder).transfer(this.token, initialSupply);
const tx = await this.token.$_recover(this.recipient); const tx = await this.token.$_recover(this.recipient);
await expect(tx) await expect(tx).to.emit(this.token, 'Transfer').withArgs(ethers.ZeroAddress, this.recipient, initialSupply);
.to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.recipient.address, initialSupply);
await expect(tx).to.changeTokenBalance(this.token, this.recipient, initialSupply); await expect(tx).to.changeTokenBalance(this.token, this.recipient, initialSupply);
}); });
}); });

@ -3,9 +3,7 @@ const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic');
const { const { Enum } = require('../../../helpers/enums');
bigint: { Enum },
} = require('../../../helpers/enums');
const name = 'My Token'; const name = 'My Token';
const symbol = 'MTKN'; const symbol = 'MTKN';
@ -90,10 +88,10 @@ describe('ERC4626', function () {
await expect(this.vault.connect(this.holder).deposit(value, this.holder)) await expect(this.vault.connect(this.holder).deposit(value, this.holder))
// Deposit normally, reentering before the internal `_update` // Deposit normally, reentering before the internal `_update`
.to.emit(this.vault, 'Deposit') .to.emit(this.vault, 'Deposit')
.withArgs(this.holder.address, this.holder.address, value, sharesForDeposit) .withArgs(this.holder, this.holder, value, sharesForDeposit)
// Reentrant deposit event → uses the same price // Reentrant deposit event → uses the same price
.to.emit(this.vault, 'Deposit') .to.emit(this.vault, 'Deposit')
.withArgs(this.token.target, this.holder.address, reenterValue, sharesForReenter); .withArgs(this.token, this.holder, reenterValue, sharesForReenter);
// Assert prices is kept // Assert prices is kept
expect(await this.vault.previewDeposit(value)).to.equal(sharesForDeposit); expect(await this.vault.previewDeposit(value)).to.equal(sharesForDeposit);
@ -123,10 +121,10 @@ describe('ERC4626', function () {
await expect(this.vault.connect(this.holder).withdraw(value, this.holder, this.holder)) await expect(this.vault.connect(this.holder).withdraw(value, this.holder, this.holder))
// Main withdraw event // Main withdraw event
.to.emit(this.vault, 'Withdraw') .to.emit(this.vault, 'Withdraw')
.withArgs(this.holder.address, this.holder.address, this.holder.address, value, sharesForWithdraw) .withArgs(this.holder, this.holder, this.holder, value, sharesForWithdraw)
// Reentrant withdraw event → uses the same price // Reentrant withdraw event → uses the same price
.to.emit(this.vault, 'Withdraw') .to.emit(this.vault, 'Withdraw')
.withArgs(this.token.target, this.holder.address, this.token.target, reenterValue, sharesForReenter); .withArgs(this.token, this.holder, this.token, reenterValue, sharesForReenter);
// Assert price is kept // Assert price is kept
expect(await this.vault.previewWithdraw(value)).to.equal(sharesForWithdraw); expect(await this.vault.previewWithdraw(value)).to.equal(sharesForWithdraw);
@ -150,7 +148,7 @@ describe('ERC4626', function () {
await expect(this.vault.connect(this.holder).deposit(value, this.holder)) await expect(this.vault.connect(this.holder).deposit(value, this.holder))
// Price is as previewed // Price is as previewed
.to.emit(this.vault, 'Deposit') .to.emit(this.vault, 'Deposit')
.withArgs(this.holder.address, this.holder.address, value, sharesBefore); .withArgs(this.holder, this.holder, value, sharesBefore);
// Price was modified during reentrancy // Price was modified during reentrancy
expect(await this.vault.previewDeposit(value)).to.lt(sharesBefore); expect(await this.vault.previewDeposit(value)).to.lt(sharesBefore);
@ -177,7 +175,7 @@ describe('ERC4626', function () {
await expect(this.vault.connect(this.holder).withdraw(value, this.holder, this.holder)) await expect(this.vault.connect(this.holder).withdraw(value, this.holder, this.holder))
// Price is as previewed // Price is as previewed
.to.emit(this.vault, 'Withdraw') .to.emit(this.vault, 'Withdraw')
.withArgs(this.holder.address, this.holder.address, this.holder.address, value, sharesBefore); .withArgs(this.holder, this.holder, this.holder, value, sharesBefore);
// Price was modified during reentrancy // Price was modified during reentrancy
expect(await this.vault.previewWithdraw(value)).to.gt(sharesBefore); expect(await this.vault.previewWithdraw(value)).to.gt(sharesBefore);
@ -196,7 +194,7 @@ describe('ERC4626', function () {
const maxDeposit = await this.vault.maxDeposit(this.holder); const maxDeposit = await this.vault.maxDeposit(this.holder);
await expect(this.vault.connect(this.holder).deposit(maxDeposit + 1n, this.recipient)) await expect(this.vault.connect(this.holder).deposit(maxDeposit + 1n, this.recipient))
.to.be.revertedWithCustomError(this.vault, 'ERC4626ExceededMaxDeposit') .to.be.revertedWithCustomError(this.vault, 'ERC4626ExceededMaxDeposit')
.withArgs(this.recipient.address, maxDeposit + 1n, maxDeposit); .withArgs(this.recipient, maxDeposit + 1n, maxDeposit);
}); });
it('reverts on mint() above max mint', async function () { it('reverts on mint() above max mint', async function () {
@ -204,7 +202,7 @@ describe('ERC4626', function () {
await expect(this.vault.connect(this.holder).mint(maxMint + 1n, this.recipient)) await expect(this.vault.connect(this.holder).mint(maxMint + 1n, this.recipient))
.to.be.revertedWithCustomError(this.vault, 'ERC4626ExceededMaxMint') .to.be.revertedWithCustomError(this.vault, 'ERC4626ExceededMaxMint')
.withArgs(this.recipient.address, maxMint + 1n, maxMint); .withArgs(this.recipient, maxMint + 1n, maxMint);
}); });
it('reverts on withdraw() above max withdraw', async function () { it('reverts on withdraw() above max withdraw', async function () {
@ -212,7 +210,7 @@ describe('ERC4626', function () {
await expect(this.vault.connect(this.holder).withdraw(maxWithdraw + 1n, this.recipient, this.holder)) await expect(this.vault.connect(this.holder).withdraw(maxWithdraw + 1n, this.recipient, this.holder))
.to.be.revertedWithCustomError(this.vault, 'ERC4626ExceededMaxWithdraw') .to.be.revertedWithCustomError(this.vault, 'ERC4626ExceededMaxWithdraw')
.withArgs(this.holder.address, maxWithdraw + 1n, maxWithdraw); .withArgs(this.holder, maxWithdraw + 1n, maxWithdraw);
}); });
it('reverts on redeem() above max redeem', async function () { it('reverts on redeem() above max redeem', async function () {
@ -220,7 +218,7 @@ describe('ERC4626', function () {
await expect(this.vault.connect(this.holder).redeem(maxRedeem + 1n, this.recipient, this.holder)) await expect(this.vault.connect(this.holder).redeem(maxRedeem + 1n, this.recipient, this.holder))
.to.be.revertedWithCustomError(this.vault, 'ERC4626ExceededMaxRedeem') .to.be.revertedWithCustomError(this.vault, 'ERC4626ExceededMaxRedeem')
.withArgs(this.holder.address, maxRedeem + 1n, maxRedeem); .withArgs(this.holder, maxRedeem + 1n, maxRedeem);
}); });
}); });
@ -247,7 +245,7 @@ describe('ERC4626', function () {
expect(await this.vault.name()).to.equal(name + ' Vault'); expect(await this.vault.name()).to.equal(name + ' Vault');
expect(await this.vault.symbol()).to.equal(symbol + 'V'); expect(await this.vault.symbol()).to.equal(symbol + 'V');
expect(await this.vault.decimals()).to.equal(decimals + offset); expect(await this.vault.decimals()).to.equal(decimals + offset);
expect(await this.vault.asset()).to.equal(this.token.target); expect(await this.vault.asset()).to.equal(this.token);
}); });
describe('empty vault: no assets & no shares', function () { describe('empty vault: no assets & no shares', function () {
@ -269,11 +267,11 @@ describe('ERC4626', function () {
await expect(tx).to.changeTokenBalance(this.vault, this.recipient, parseShare(1n)); await expect(tx).to.changeTokenBalance(this.vault, this.recipient, parseShare(1n));
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.vault.target, parseToken(1n)) .withArgs(this.holder, this.vault, parseToken(1n))
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(ethers.ZeroAddress, this.recipient.address, parseShare(1n)) .withArgs(ethers.ZeroAddress, this.recipient, parseShare(1n))
.to.emit(this.vault, 'Deposit') .to.emit(this.vault, 'Deposit')
.withArgs(this.holder.address, this.recipient.address, parseToken(1n), parseShare(1n)); .withArgs(this.holder, this.recipient, parseToken(1n), parseShare(1n));
}); });
it('mint', async function () { it('mint', async function () {
@ -290,11 +288,11 @@ describe('ERC4626', function () {
await expect(tx).to.changeTokenBalance(this.vault, this.recipient, parseShare(1n)); await expect(tx).to.changeTokenBalance(this.vault, this.recipient, parseShare(1n));
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.vault.target, parseToken(1n)) .withArgs(this.holder, this.vault, parseToken(1n))
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(ethers.ZeroAddress, this.recipient.address, parseShare(1n)) .withArgs(ethers.ZeroAddress, this.recipient, parseShare(1n))
.to.emit(this.vault, 'Deposit') .to.emit(this.vault, 'Deposit')
.withArgs(this.holder.address, this.recipient.address, parseToken(1n), parseShare(1n)); .withArgs(this.holder, this.recipient, parseToken(1n), parseShare(1n));
}); });
it('withdraw', async function () { it('withdraw', async function () {
@ -307,11 +305,11 @@ describe('ERC4626', function () {
await expect(tx).to.changeTokenBalance(this.vault, this.holder, 0n); await expect(tx).to.changeTokenBalance(this.vault, this.holder, 0n);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.vault.target, this.recipient.address, 0n) .withArgs(this.vault, this.recipient, 0n)
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(this.holder.address, ethers.ZeroAddress, 0n) .withArgs(this.holder, ethers.ZeroAddress, 0n)
.to.emit(this.vault, 'Withdraw') .to.emit(this.vault, 'Withdraw')
.withArgs(this.holder.address, this.recipient.address, this.holder.address, 0n, 0n); .withArgs(this.holder, this.recipient, this.holder, 0n, 0n);
}); });
it('redeem', async function () { it('redeem', async function () {
@ -324,11 +322,11 @@ describe('ERC4626', function () {
await expect(tx).to.changeTokenBalance(this.vault, this.holder, 0n); await expect(tx).to.changeTokenBalance(this.vault, this.holder, 0n);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.vault.target, this.recipient.address, 0n) .withArgs(this.vault, this.recipient, 0n)
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(this.holder.address, ethers.ZeroAddress, 0n) .withArgs(this.holder, ethers.ZeroAddress, 0n)
.to.emit(this.vault, 'Withdraw') .to.emit(this.vault, 'Withdraw')
.withArgs(this.holder.address, this.recipient.address, this.holder.address, 0n, 0n); .withArgs(this.holder, this.recipient, this.holder, 0n, 0n);
}); });
}); });
@ -374,11 +372,11 @@ describe('ERC4626', function () {
await expect(tx).to.changeTokenBalance(this.vault, this.recipient, expectedShares); await expect(tx).to.changeTokenBalance(this.vault, this.recipient, expectedShares);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.vault.target, depositAssets) .withArgs(this.holder, this.vault, depositAssets)
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(ethers.ZeroAddress, this.recipient.address, expectedShares) .withArgs(ethers.ZeroAddress, this.recipient, expectedShares)
.to.emit(this.vault, 'Deposit') .to.emit(this.vault, 'Deposit')
.withArgs(this.holder.address, this.recipient.address, depositAssets, expectedShares); .withArgs(this.holder, this.recipient, depositAssets, expectedShares);
}); });
/** /**
@ -412,11 +410,11 @@ describe('ERC4626', function () {
await expect(tx).to.changeTokenBalance(this.vault, this.recipient, mintShares); await expect(tx).to.changeTokenBalance(this.vault, this.recipient, mintShares);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.vault.target, expectedAssets) .withArgs(this.holder, this.vault, expectedAssets)
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(ethers.ZeroAddress, this.recipient.address, mintShares) .withArgs(ethers.ZeroAddress, this.recipient, mintShares)
.to.emit(this.vault, 'Deposit') .to.emit(this.vault, 'Deposit')
.withArgs(this.holder.address, this.recipient.address, expectedAssets, mintShares); .withArgs(this.holder, this.recipient, expectedAssets, mintShares);
}); });
it('withdraw', async function () { it('withdraw', async function () {
@ -429,11 +427,11 @@ describe('ERC4626', function () {
await expect(tx).to.changeTokenBalance(this.vault, this.holder, 0n); await expect(tx).to.changeTokenBalance(this.vault, this.holder, 0n);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.vault.target, this.recipient.address, 0n) .withArgs(this.vault, this.recipient, 0n)
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(this.holder.address, ethers.ZeroAddress, 0n) .withArgs(this.holder, ethers.ZeroAddress, 0n)
.to.emit(this.vault, 'Withdraw') .to.emit(this.vault, 'Withdraw')
.withArgs(this.holder.address, this.recipient.address, this.holder.address, 0n, 0n); .withArgs(this.holder, this.recipient, this.holder, 0n, 0n);
}); });
it('redeem', async function () { it('redeem', async function () {
@ -446,11 +444,11 @@ describe('ERC4626', function () {
await expect(tx).to.changeTokenBalance(this.vault, this.holder, 0n); await expect(tx).to.changeTokenBalance(this.vault, this.holder, 0n);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.vault.target, this.recipient.address, 0n) .withArgs(this.vault, this.recipient, 0n)
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(this.holder.address, ethers.ZeroAddress, 0n) .withArgs(this.holder, ethers.ZeroAddress, 0n)
.to.emit(this.vault, 'Withdraw') .to.emit(this.vault, 'Withdraw')
.withArgs(this.holder.address, this.recipient.address, this.holder.address, 0n, 0n); .withArgs(this.holder, this.recipient, this.holder, 0n, 0n);
}); });
}); });
@ -495,11 +493,11 @@ describe('ERC4626', function () {
await expect(tx).to.changeTokenBalance(this.vault, this.recipient, expectedShares); await expect(tx).to.changeTokenBalance(this.vault, this.recipient, expectedShares);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.vault.target, depositAssets) .withArgs(this.holder, this.vault, depositAssets)
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(ethers.ZeroAddress, this.recipient.address, expectedShares) .withArgs(ethers.ZeroAddress, this.recipient, expectedShares)
.to.emit(this.vault, 'Deposit') .to.emit(this.vault, 'Deposit')
.withArgs(this.holder.address, this.recipient.address, depositAssets, expectedShares); .withArgs(this.holder, this.recipient, depositAssets, expectedShares);
}); });
/** /**
@ -531,11 +529,11 @@ describe('ERC4626', function () {
await expect(tx).to.changeTokenBalance(this.vault, this.recipient, mintShares); await expect(tx).to.changeTokenBalance(this.vault, this.recipient, mintShares);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.vault.target, expectedAssets) .withArgs(this.holder, this.vault, expectedAssets)
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(ethers.ZeroAddress, this.recipient.address, mintShares) .withArgs(ethers.ZeroAddress, this.recipient, mintShares)
.to.emit(this.vault, 'Deposit') .to.emit(this.vault, 'Deposit')
.withArgs(this.holder.address, this.recipient.address, expectedAssets, mintShares); .withArgs(this.holder, this.recipient, expectedAssets, mintShares);
}); });
it('withdraw', async function () { it('withdraw', async function () {
@ -558,11 +556,11 @@ describe('ERC4626', function () {
await expect(tx).to.changeTokenBalance(this.vault, this.holder, -expectedShares); await expect(tx).to.changeTokenBalance(this.vault, this.holder, -expectedShares);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.vault.target, this.recipient.address, withdrawAssets) .withArgs(this.vault, this.recipient, withdrawAssets)
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(this.holder.address, ethers.ZeroAddress, expectedShares) .withArgs(this.holder, ethers.ZeroAddress, expectedShares)
.to.emit(this.vault, 'Withdraw') .to.emit(this.vault, 'Withdraw')
.withArgs(this.holder.address, this.recipient.address, this.holder.address, withdrawAssets, expectedShares); .withArgs(this.holder, this.recipient, this.holder, withdrawAssets, expectedShares);
}); });
it('withdraw with approval', async function () { it('withdraw with approval', async function () {
@ -570,7 +568,7 @@ describe('ERC4626', function () {
await expect(this.vault.connect(this.other).withdraw(parseToken(1n), this.recipient, this.holder)) await expect(this.vault.connect(this.other).withdraw(parseToken(1n), this.recipient, this.holder))
.to.be.revertedWithCustomError(this.vault, 'ERC20InsufficientAllowance') .to.be.revertedWithCustomError(this.vault, 'ERC20InsufficientAllowance')
.withArgs(this.other.address, 0n, assets); .withArgs(this.other, 0n, assets);
await expect(this.vault.connect(this.spender).withdraw(parseToken(1n), this.recipient, this.holder)).to.not.be await expect(this.vault.connect(this.spender).withdraw(parseToken(1n), this.recipient, this.holder)).to.not.be
.reverted; .reverted;
@ -596,17 +594,17 @@ describe('ERC4626', function () {
await expect(tx).to.changeTokenBalance(this.vault, this.holder, -redeemShares); await expect(tx).to.changeTokenBalance(this.vault, this.holder, -redeemShares);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.vault.target, this.recipient.address, expectedAssets) .withArgs(this.vault, this.recipient, expectedAssets)
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(this.holder.address, ethers.ZeroAddress, redeemShares) .withArgs(this.holder, ethers.ZeroAddress, redeemShares)
.to.emit(this.vault, 'Withdraw') .to.emit(this.vault, 'Withdraw')
.withArgs(this.holder.address, this.recipient.address, this.holder.address, expectedAssets, redeemShares); .withArgs(this.holder, this.recipient, this.holder, expectedAssets, redeemShares);
}); });
it('redeem with approval', async function () { it('redeem with approval', async function () {
await expect(this.vault.connect(this.other).redeem(parseShare(100n), this.recipient, this.holder)) await expect(this.vault.connect(this.other).redeem(parseShare(100n), this.recipient, this.holder))
.to.be.revertedWithCustomError(this.vault, 'ERC20InsufficientAllowance') .to.be.revertedWithCustomError(this.vault, 'ERC20InsufficientAllowance')
.withArgs(this.other.address, 0n, parseShare(100n)); .withArgs(this.other, 0n, parseShare(100n));
await expect(this.vault.connect(this.spender).redeem(parseShare(100n), this.recipient, this.holder)).to.not.be await expect(this.vault.connect(this.spender).redeem(parseShare(100n), this.recipient, this.holder)).to.not.be
.reverted; .reverted;
@ -660,16 +658,16 @@ describe('ERC4626', function () {
await expect(this.tx) await expect(this.tx)
// get total // get total
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.vault.target, valueWithFees) .withArgs(this.holder, this.vault, valueWithFees)
// redirect fees // redirect fees
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.vault.target, this.other.address, fees) .withArgs(this.vault, this.other, fees)
// mint shares // mint shares
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(ethers.ZeroAddress, this.recipient.address, valueWithoutFees) .withArgs(ethers.ZeroAddress, this.recipient, valueWithoutFees)
// deposit event // deposit event
.to.emit(this.vault, 'Deposit') .to.emit(this.vault, 'Deposit')
.withArgs(this.holder.address, this.recipient.address, valueWithFees, valueWithoutFees); .withArgs(this.holder, this.recipient, valueWithFees, valueWithoutFees);
}); });
}); });
@ -712,16 +710,16 @@ describe('ERC4626', function () {
await expect(this.tx) await expect(this.tx)
// withdraw principal // withdraw principal
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.vault.target, this.recipient.address, valueWithoutFees) .withArgs(this.vault, this.recipient, valueWithoutFees)
// redirect fees // redirect fees
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.vault.target, this.other.address, fees) .withArgs(this.vault, this.other, fees)
// mint shares // mint shares
.to.emit(this.vault, 'Transfer') .to.emit(this.vault, 'Transfer')
.withArgs(this.holder.address, ethers.ZeroAddress, valueWithFees) .withArgs(this.holder, ethers.ZeroAddress, valueWithFees)
// withdraw event // withdraw event
.to.emit(this.vault, 'Withdraw') .to.emit(this.vault, 'Withdraw')
.withArgs(this.holder.address, this.recipient.address, this.holder.address, valueWithoutFees, valueWithFees); .withArgs(this.holder, this.recipient, this.holder, valueWithoutFees, valueWithFees);
}); });
}); });
}); });
@ -742,9 +740,9 @@ describe('ERC4626', function () {
// 1. Alice mints 2000 shares (costs 2000 tokens) // 1. Alice mints 2000 shares (costs 2000 tokens)
await expect(vault.connect(alice).mint(2000n, alice)) await expect(vault.connect(alice).mint(2000n, alice))
.to.emit(token, 'Transfer') .to.emit(token, 'Transfer')
.withArgs(alice.address, vault.target, 2000n) .withArgs(alice, vault, 2000n)
.to.emit(vault, 'Transfer') .to.emit(vault, 'Transfer')
.withArgs(ethers.ZeroAddress, alice.address, 2000n); .withArgs(ethers.ZeroAddress, alice, 2000n);
expect(await vault.previewDeposit(2000n)).to.equal(2000n); expect(await vault.previewDeposit(2000n)).to.equal(2000n);
expect(await vault.balanceOf(alice)).to.equal(2000n); expect(await vault.balanceOf(alice)).to.equal(2000n);
@ -758,9 +756,9 @@ describe('ERC4626', function () {
// 2. Bruce deposits 4000 tokens (mints 4000 shares) // 2. Bruce deposits 4000 tokens (mints 4000 shares)
await expect(vault.connect(bruce).mint(4000n, bruce)) await expect(vault.connect(bruce).mint(4000n, bruce))
.to.emit(token, 'Transfer') .to.emit(token, 'Transfer')
.withArgs(bruce.address, vault.target, 4000n) .withArgs(bruce, vault, 4000n)
.to.emit(vault, 'Transfer') .to.emit(vault, 'Transfer')
.withArgs(ethers.ZeroAddress, bruce.address, 4000n); .withArgs(ethers.ZeroAddress, bruce, 4000n);
expect(await vault.previewDeposit(4000n)).to.equal(4000n); expect(await vault.previewDeposit(4000n)).to.equal(4000n);
expect(await vault.balanceOf(alice)).to.equal(2000n); expect(await vault.balanceOf(alice)).to.equal(2000n);
@ -785,9 +783,9 @@ describe('ERC4626', function () {
// 4. Alice deposits 2000 tokens (mints 1333 shares) // 4. Alice deposits 2000 tokens (mints 1333 shares)
await expect(vault.connect(alice).deposit(2000n, alice)) await expect(vault.connect(alice).deposit(2000n, alice))
.to.emit(token, 'Transfer') .to.emit(token, 'Transfer')
.withArgs(alice.address, vault.target, 2000n) .withArgs(alice, vault, 2000n)
.to.emit(vault, 'Transfer') .to.emit(vault, 'Transfer')
.withArgs(ethers.ZeroAddress, alice.address, 1333n); .withArgs(ethers.ZeroAddress, alice, 1333n);
expect(await vault.balanceOf(alice)).to.equal(3333n); expect(await vault.balanceOf(alice)).to.equal(3333n);
expect(await vault.balanceOf(bruce)).to.equal(4000n); expect(await vault.balanceOf(bruce)).to.equal(4000n);
@ -802,9 +800,9 @@ describe('ERC4626', function () {
// NOTE: Alices's vault assets got rounded towards infinity // NOTE: Alices's vault assets got rounded towards infinity
await expect(vault.connect(bruce).mint(2000n, bruce)) await expect(vault.connect(bruce).mint(2000n, bruce))
.to.emit(token, 'Transfer') .to.emit(token, 'Transfer')
.withArgs(bruce.address, vault.target, 3000n) .withArgs(bruce, vault, 3000n)
.to.emit(vault, 'Transfer') .to.emit(vault, 'Transfer')
.withArgs(ethers.ZeroAddress, bruce.address, 2000n); .withArgs(ethers.ZeroAddress, bruce, 2000n);
expect(await vault.balanceOf(alice)).to.equal(3333n); expect(await vault.balanceOf(alice)).to.equal(3333n);
expect(await vault.balanceOf(bruce)).to.equal(6000n); expect(await vault.balanceOf(bruce)).to.equal(6000n);
@ -829,9 +827,9 @@ describe('ERC4626', function () {
// 7. Alice redeem 1333 shares (2428 assets) // 7. Alice redeem 1333 shares (2428 assets)
await expect(vault.connect(alice).redeem(1333n, alice, alice)) await expect(vault.connect(alice).redeem(1333n, alice, alice))
.to.emit(vault, 'Transfer') .to.emit(vault, 'Transfer')
.withArgs(alice.address, ethers.ZeroAddress, 1333n) .withArgs(alice, ethers.ZeroAddress, 1333n)
.to.emit(token, 'Transfer') .to.emit(token, 'Transfer')
.withArgs(vault.target, alice.address, 2427n); // used to be 2428 .withArgs(vault, alice, 2427n); // used to be 2428
expect(await vault.balanceOf(alice)).to.equal(2000n); expect(await vault.balanceOf(alice)).to.equal(2000n);
expect(await vault.balanceOf(bruce)).to.equal(6000n); expect(await vault.balanceOf(bruce)).to.equal(6000n);
@ -844,9 +842,9 @@ describe('ERC4626', function () {
// 8. Bruce withdraws 2929 assets (1608 shares) // 8. Bruce withdraws 2929 assets (1608 shares)
await expect(vault.connect(bruce).withdraw(2929n, bruce, bruce)) await expect(vault.connect(bruce).withdraw(2929n, bruce, bruce))
.to.emit(vault, 'Transfer') .to.emit(vault, 'Transfer')
.withArgs(bruce.address, ethers.ZeroAddress, 1608n) .withArgs(bruce, ethers.ZeroAddress, 1608n)
.to.emit(token, 'Transfer') .to.emit(token, 'Transfer')
.withArgs(vault.target, bruce.address, 2929n); .withArgs(vault, bruce, 2929n);
expect(await vault.balanceOf(alice)).to.equal(2000n); expect(await vault.balanceOf(alice)).to.equal(2000n);
expect(await vault.balanceOf(bruce)).to.equal(4392n); expect(await vault.balanceOf(bruce)).to.equal(4392n);
@ -860,9 +858,9 @@ describe('ERC4626', function () {
// NOTE: Bruce's assets have been rounded back towards infinity // NOTE: Bruce's assets have been rounded back towards infinity
await expect(vault.connect(alice).withdraw(3643n, alice, alice)) await expect(vault.connect(alice).withdraw(3643n, alice, alice))
.to.emit(vault, 'Transfer') .to.emit(vault, 'Transfer')
.withArgs(alice.address, ethers.ZeroAddress, 2000n) .withArgs(alice, ethers.ZeroAddress, 2000n)
.to.emit(token, 'Transfer') .to.emit(token, 'Transfer')
.withArgs(vault.target, alice.address, 3643n); .withArgs(vault, alice, 3643n);
expect(await vault.balanceOf(alice)).to.equal(0n); expect(await vault.balanceOf(alice)).to.equal(0n);
expect(await vault.balanceOf(bruce)).to.equal(4392n); expect(await vault.balanceOf(bruce)).to.equal(4392n);
@ -875,9 +873,9 @@ describe('ERC4626', function () {
// 10. Bruce redeem 4392 shares (8001 tokens) // 10. Bruce redeem 4392 shares (8001 tokens)
await expect(vault.connect(bruce).redeem(4392n, bruce, bruce)) await expect(vault.connect(bruce).redeem(4392n, bruce, bruce))
.to.emit(vault, 'Transfer') .to.emit(vault, 'Transfer')
.withArgs(bruce.address, ethers.ZeroAddress, 4392n) .withArgs(bruce, ethers.ZeroAddress, 4392n)
.to.emit(token, 'Transfer') .to.emit(token, 'Transfer')
.withArgs(vault.target, bruce.address, 8000n); // used to be 8001 .withArgs(vault, bruce, 8000n); // used to be 8001
expect(await vault.balanceOf(alice)).to.equal(0n); expect(await vault.balanceOf(alice)).to.equal(0n);
expect(await vault.balanceOf(bruce)).to.equal(0n); expect(await vault.balanceOf(bruce)).to.equal(0n);

@ -40,13 +40,13 @@ describe('SafeERC20', function () {
it('reverts on transfer', async function () { it('reverts on transfer', async function () {
await expect(this.mock.$safeTransfer(this.token, this.receiver, 0n)) await expect(this.mock.$safeTransfer(this.token, this.receiver, 0n))
.to.be.revertedWithCustomError(this.mock, 'AddressEmptyCode') .to.be.revertedWithCustomError(this.mock, 'AddressEmptyCode')
.withArgs(this.token.address); .withArgs(this.token);
}); });
it('reverts on transferFrom', async function () { it('reverts on transferFrom', async function () {
await expect(this.mock.$safeTransferFrom(this.token, this.mock, this.receiver, 0n)) await expect(this.mock.$safeTransferFrom(this.token, this.mock, this.receiver, 0n))
.to.be.revertedWithCustomError(this.mock, 'AddressEmptyCode') .to.be.revertedWithCustomError(this.mock, 'AddressEmptyCode')
.withArgs(this.token.address); .withArgs(this.token);
}); });
it('reverts on increaseAllowance', async function () { it('reverts on increaseAllowance', async function () {
@ -62,7 +62,7 @@ describe('SafeERC20', function () {
it('reverts on forceApprove', async function () { it('reverts on forceApprove', async function () {
await expect(this.mock.$forceApprove(this.token, this.spender, 0n)) await expect(this.mock.$forceApprove(this.token, this.spender, 0n))
.to.be.revertedWithCustomError(this.mock, 'AddressEmptyCode') .to.be.revertedWithCustomError(this.mock, 'AddressEmptyCode')
.withArgs(this.token.address); .withArgs(this.token);
}); });
}); });
@ -74,31 +74,31 @@ describe('SafeERC20', function () {
it('reverts on transfer', async function () { it('reverts on transfer', async function () {
await expect(this.mock.$safeTransfer(this.token, this.receiver, 0n)) await expect(this.mock.$safeTransfer(this.token, this.receiver, 0n))
.to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation') .to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation')
.withArgs(this.token.target); .withArgs(this.token);
}); });
it('reverts on transferFrom', async function () { it('reverts on transferFrom', async function () {
await expect(this.mock.$safeTransferFrom(this.token, this.mock, this.receiver, 0n)) await expect(this.mock.$safeTransferFrom(this.token, this.mock, this.receiver, 0n))
.to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation') .to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation')
.withArgs(this.token.target); .withArgs(this.token);
}); });
it('reverts on increaseAllowance', async function () { it('reverts on increaseAllowance', async function () {
await expect(this.mock.$safeIncreaseAllowance(this.token, this.spender, 0n)) await expect(this.mock.$safeIncreaseAllowance(this.token, this.spender, 0n))
.to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation') .to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation')
.withArgs(this.token.target); .withArgs(this.token);
}); });
it('reverts on decreaseAllowance', async function () { it('reverts on decreaseAllowance', async function () {
await expect(this.mock.$safeDecreaseAllowance(this.token, this.spender, 0n)) await expect(this.mock.$safeDecreaseAllowance(this.token, this.spender, 0n))
.to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation') .to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation')
.withArgs(this.token.target); .withArgs(this.token);
}); });
it('reverts on forceApprove', async function () { it('reverts on forceApprove', async function () {
await expect(this.mock.$forceApprove(this.token, this.spender, 0n)) await expect(this.mock.$forceApprove(this.token, this.spender, 0n))
.to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation') .to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation')
.withArgs(this.token.target); .withArgs(this.token);
}); });
}); });
@ -157,13 +157,13 @@ function shouldOnlyRevertOnErrors() {
it("doesn't revert on transfer", async function () { it("doesn't revert on transfer", async function () {
await expect(this.mock.$safeTransfer(this.token, this.receiver, 10n)) await expect(this.mock.$safeTransfer(this.token, this.receiver, 10n))
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.mock.target, this.receiver.address, 10n); .withArgs(this.mock, this.receiver, 10n);
}); });
it("doesn't revert on transferFrom", async function () { it("doesn't revert on transferFrom", async function () {
await expect(this.mock.$safeTransferFrom(this.token, this.owner, this.receiver, 10n)) await expect(this.mock.$safeTransferFrom(this.token, this.owner, this.receiver, 10n))
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.owner.address, this.receiver.address, 10n); .withArgs(this.owner, this.receiver, 10n);
}); });
}); });
@ -191,7 +191,7 @@ function shouldOnlyRevertOnErrors() {
it('reverts when decreasing the allowance', async function () { it('reverts when decreasing the allowance', async function () {
await expect(this.mock.$safeDecreaseAllowance(this.token, this.spender, 10n)) await expect(this.mock.$safeDecreaseAllowance(this.token, this.spender, 10n))
.to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedDecreaseAllowance') .to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedDecreaseAllowance')
.withArgs(this.spender.address, 0n, 10n); .withArgs(this.spender, 0n, 10n);
}); });
}); });
@ -223,7 +223,7 @@ function shouldOnlyRevertOnErrors() {
it('reverts when decreasing the allowance to a negative value', async function () { it('reverts when decreasing the allowance to a negative value', async function () {
await expect(this.mock.$safeDecreaseAllowance(this.token, this.spender, 200n)) await expect(this.mock.$safeDecreaseAllowance(this.token, this.spender, 200n))
.to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedDecreaseAllowance') .to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedDecreaseAllowance')
.withArgs(this.spender.address, 100n, 200n); .withArgs(this.spender, 100n, 200n);
}); });
}); });
}); });

@ -4,9 +4,7 @@ const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic');
const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs');
const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior'); const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior');
const { const { RevertType } = require('../../helpers/enums');
bigint: { RevertType },
} = require('../../helpers/enums');
const firstTokenId = 5042n; const firstTokenId = 5042n;
const secondTokenId = 79217n; const secondTokenId = 79217n;
@ -58,7 +56,7 @@ function shouldBehaveLikeERC721() {
const tokenId = firstTokenId; const tokenId = firstTokenId;
it('returns the owner of the given token ID', async function () { it('returns the owner of the given token ID', async function () {
expect(await this.token.ownerOf(tokenId)).to.equal(this.owner.address); expect(await this.token.ownerOf(tokenId)).to.equal(this.owner);
}); });
}); });
@ -85,13 +83,11 @@ function shouldBehaveLikeERC721() {
const transferWasSuccessful = () => { const transferWasSuccessful = () => {
it('transfers the ownership of the given token ID to the given address', async function () { it('transfers the ownership of the given token ID to the given address', async function () {
await this.tx(); await this.tx();
expect(await this.token.ownerOf(tokenId)).to.equal(this.to.address ?? this.to.target); expect(await this.token.ownerOf(tokenId)).to.equal(this.to);
}); });
it('emits a Transfer event', async function () { it('emits a Transfer event', async function () {
await expect(this.tx()) await expect(this.tx()).to.emit(this.token, 'Transfer').withArgs(this.owner, this.to, tokenId);
.to.emit(this.token, 'Transfer')
.withArgs(this.owner.address, this.to.address ?? this.to.target, tokenId);
}); });
it('clears the approval for the token ID with no event', async function () { it('clears the approval for the token ID with no event', async function () {
@ -157,7 +153,7 @@ function shouldBehaveLikeERC721() {
it('keeps ownership of the token', async function () { it('keeps ownership of the token', async function () {
await this.tx(); await this.tx();
expect(await this.token.ownerOf(tokenId)).to.equal(this.owner.address); expect(await this.token.ownerOf(tokenId)).to.equal(this.owner);
}); });
it('clears the approval for the token ID', async function () { it('clears the approval for the token ID', async function () {
@ -166,9 +162,7 @@ function shouldBehaveLikeERC721() {
}); });
it('emits only a transfer event', async function () { it('emits only a transfer event', async function () {
await expect(this.tx()) await expect(this.tx()).to.emit(this.token, 'Transfer').withArgs(this.owner, this.owner, tokenId);
.to.emit(this.token, 'Transfer')
.withArgs(this.owner.address, this.owner.address, tokenId);
}); });
it('keeps the owner balance', async function () { it('keeps the owner balance', async function () {
@ -192,7 +186,7 @@ function shouldBehaveLikeERC721() {
this.token.connect(this.owner)[fragment](this.other, this.other, tokenId, ...(opts.extra ?? [])), this.token.connect(this.owner)[fragment](this.other, this.other, tokenId, ...(opts.extra ?? [])),
) )
.to.be.revertedWithCustomError(this.token, 'ERC721IncorrectOwner') .to.be.revertedWithCustomError(this.token, 'ERC721IncorrectOwner')
.withArgs(this.other.address, tokenId, this.owner.address); .withArgs(this.other, tokenId, this.owner);
}); });
}); });
@ -207,7 +201,7 @@ function shouldBehaveLikeERC721() {
this.token.connect(this.other)[fragment](this.owner, this.other, tokenId, ...(opts.extra ?? [])), this.token.connect(this.other)[fragment](this.owner, this.other, tokenId, ...(opts.extra ?? [])),
) )
.to.be.revertedWithCustomError(this.token, 'ERC721InsufficientApproval') .to.be.revertedWithCustomError(this.token, 'ERC721InsufficientApproval')
.withArgs(this.other.address, tokenId); .withArgs(this.other, tokenId);
}); });
} }
}); });
@ -255,7 +249,7 @@ function shouldBehaveLikeERC721() {
it('calls onERC721Received', async function () { it('calls onERC721Received', async function () {
await expect(this.token.connect(this.owner)[fragment](this.owner, this.to, tokenId, ...(opts.extra ?? []))) await expect(this.token.connect(this.owner)[fragment](this.owner, this.to, tokenId, ...(opts.extra ?? [])))
.to.emit(this.to, 'Received') .to.emit(this.to, 'Received')
.withArgs(this.owner.address, this.owner.address, tokenId, data, anyValue); .withArgs(this.owner, this.owner, tokenId, data, anyValue);
}); });
it('calls onERC721Received from approved', async function () { it('calls onERC721Received from approved', async function () {
@ -263,7 +257,7 @@ function shouldBehaveLikeERC721() {
this.token.connect(this.approved)[fragment](this.owner, this.to, tokenId, ...(opts.extra ?? [])), this.token.connect(this.approved)[fragment](this.owner, this.to, tokenId, ...(opts.extra ?? [])),
) )
.to.emit(this.to, 'Received') .to.emit(this.to, 'Received')
.withArgs(this.approved.address, this.owner.address, tokenId, data, anyValue); .withArgs(this.approved, this.owner, tokenId, data, anyValue);
}); });
describe('with an invalid token id', function () { describe('with an invalid token id', function () {
@ -311,7 +305,7 @@ function shouldBehaveLikeERC721() {
await expect(this.token.connect(this.owner)[fnName](this.owner, invalidReceiver, tokenId)) await expect(this.token.connect(this.owner)[fnName](this.owner, invalidReceiver, tokenId))
.to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver') .to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver')
.withArgs(invalidReceiver.target); .withArgs(invalidReceiver);
}); });
}); });
@ -337,7 +331,7 @@ function shouldBehaveLikeERC721() {
await expect(this.token.connect(this.owner)[fnName](this.owner, revertingReceiver, tokenId)) await expect(this.token.connect(this.owner)[fnName](this.owner, revertingReceiver, tokenId))
.to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver') .to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver')
.withArgs(revertingReceiver.target); .withArgs(revertingReceiver);
}); });
}); });
@ -373,7 +367,7 @@ function shouldBehaveLikeERC721() {
await expect(this.token.connect(this.owner)[fnName](this.owner, nonReceiver, tokenId)) await expect(this.token.connect(this.owner)[fnName](this.owner, nonReceiver, tokenId))
.to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver') .to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver')
.withArgs(nonReceiver.target); .withArgs(nonReceiver);
}); });
}); });
}); });
@ -408,7 +402,7 @@ function shouldBehaveLikeERC721() {
await expect(this.token.$_safeMint(invalidReceiver, tokenId)) await expect(this.token.$_safeMint(invalidReceiver, tokenId))
.to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver') .to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver')
.withArgs(invalidReceiver.target); .withArgs(invalidReceiver);
}); });
}); });
@ -434,7 +428,7 @@ function shouldBehaveLikeERC721() {
await expect(this.token.$_safeMint(revertingReceiver, tokenId)) await expect(this.token.$_safeMint(revertingReceiver, tokenId))
.to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver') .to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver')
.withArgs(revertingReceiver.target); .withArgs(revertingReceiver);
}); });
}); });
@ -470,7 +464,7 @@ function shouldBehaveLikeERC721() {
await expect(this.token.$_safeMint(nonReceiver, tokenId)) await expect(this.token.$_safeMint(nonReceiver, tokenId))
.to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver') .to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver')
.withArgs(nonReceiver.target); .withArgs(nonReceiver);
}); });
}); });
}); });
@ -487,7 +481,7 @@ function shouldBehaveLikeERC721() {
const itApproves = function () { const itApproves = function () {
it('sets the approval for the target address', async function () { it('sets the approval for the target address', async function () {
expect(await this.token.getApproved(tokenId)).to.equal(this.approved.address ?? this.approved); expect(await this.token.getApproved(tokenId)).to.equal(this.approved ?? this.approved);
}); });
}; };
@ -495,7 +489,7 @@ function shouldBehaveLikeERC721() {
it('emits an approval event', async function () { it('emits an approval event', async function () {
await expect(this.tx) await expect(this.tx)
.to.emit(this.token, 'Approval') .to.emit(this.token, 'Approval')
.withArgs(this.owner.address, this.approved.address ?? this.approved, tokenId); .withArgs(this.owner, this.approved ?? this.approved, tokenId);
}); });
}; };
@ -557,7 +551,7 @@ function shouldBehaveLikeERC721() {
it('reverts', async function () { it('reverts', async function () {
await expect(this.token.connect(this.other).approve(this.approved, tokenId)) await expect(this.token.connect(this.other).approve(this.approved, tokenId))
.to.be.revertedWithCustomError(this.token, 'ERC721InvalidApprover') .to.be.revertedWithCustomError(this.token, 'ERC721InvalidApprover')
.withArgs(this.other.address); .withArgs(this.other);
}); });
}); });
@ -567,7 +561,7 @@ function shouldBehaveLikeERC721() {
await expect(this.token.connect(this.approved).approve(this.other, tokenId)) await expect(this.token.connect(this.approved).approve(this.other, tokenId))
.to.be.revertedWithCustomError(this.token, 'ERC721InvalidApprover') .to.be.revertedWithCustomError(this.token, 'ERC721InvalidApprover')
.withArgs(this.approved.address); .withArgs(this.approved);
}); });
}); });
@ -603,7 +597,7 @@ function shouldBehaveLikeERC721() {
it('emits an approval event', async function () { it('emits an approval event', async function () {
await expect(this.token.connect(this.owner).setApprovalForAll(this.operator, true)) await expect(this.token.connect(this.owner).setApprovalForAll(this.operator, true))
.to.emit(this.token, 'ApprovalForAll') .to.emit(this.token, 'ApprovalForAll')
.withArgs(this.owner.address, this.operator.address, true); .withArgs(this.owner, this.operator, true);
}); });
}); });
@ -621,7 +615,7 @@ function shouldBehaveLikeERC721() {
it('emits an approval event', async function () { it('emits an approval event', async function () {
await expect(this.token.connect(this.owner).setApprovalForAll(this.operator, true)) await expect(this.token.connect(this.owner).setApprovalForAll(this.operator, true))
.to.emit(this.token, 'ApprovalForAll') .to.emit(this.token, 'ApprovalForAll')
.withArgs(this.owner.address, this.operator.address, true); .withArgs(this.owner, this.operator, true);
}); });
it('can unset the operator approval', async function () { it('can unset the operator approval', async function () {
@ -645,7 +639,7 @@ function shouldBehaveLikeERC721() {
it('emits an approval event', async function () { it('emits an approval event', async function () {
await expect(this.token.connect(this.owner).setApprovalForAll(this.operator, true)) await expect(this.token.connect(this.owner).setApprovalForAll(this.operator, true))
.to.emit(this.token, 'ApprovalForAll') .to.emit(this.token, 'ApprovalForAll')
.withArgs(this.owner.address, this.operator.address, true); .withArgs(this.owner, this.operator, true);
}); });
}); });
}); });
@ -679,7 +673,7 @@ function shouldBehaveLikeERC721() {
}); });
it('returns approved account', async function () { it('returns approved account', async function () {
expect(await this.token.getApproved(firstTokenId)).to.equal(this.approved.address); expect(await this.token.getApproved(firstTokenId)).to.equal(this.approved);
}); });
}); });
}); });
@ -699,14 +693,12 @@ function shouldBehaveLikeERC721() {
}); });
it('emits a Transfer event', async function () { it('emits a Transfer event', async function () {
await expect(this.tx) await expect(this.tx).to.emit(this.token, 'Transfer').withArgs(ethers.ZeroAddress, this.owner, firstTokenId);
.to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.owner.address, firstTokenId);
}); });
it('creates the token', async function () { it('creates the token', async function () {
expect(await this.token.balanceOf(this.owner)).to.equal(1n); expect(await this.token.balanceOf(this.owner)).to.equal(1n);
expect(await this.token.ownerOf(firstTokenId)).to.equal(this.owner.address); expect(await this.token.ownerOf(firstTokenId)).to.equal(this.owner);
}); });
it('reverts when adding a token id that already exists', async function () { it('reverts when adding a token id that already exists', async function () {
@ -736,9 +728,7 @@ function shouldBehaveLikeERC721() {
}); });
it('emits a Transfer event', async function () { it('emits a Transfer event', async function () {
await expect(this.tx) await expect(this.tx).to.emit(this.token, 'Transfer').withArgs(this.owner, ethers.ZeroAddress, firstTokenId);
.to.emit(this.token, 'Transfer')
.withArgs(this.owner.address, ethers.ZeroAddress, firstTokenId);
}); });
it('deletes the token', async function () { it('deletes the token', async function () {
@ -790,7 +780,7 @@ function shouldBehaveLikeERC721Enumerable() {
it('reverts', async function () { it('reverts', async function () {
await expect(this.token.tokenOfOwnerByIndex(this.owner, 2n)) await expect(this.token.tokenOfOwnerByIndex(this.owner, 2n))
.to.be.revertedWithCustomError(this.token, 'ERC721OutOfBoundsIndex') .to.be.revertedWithCustomError(this.token, 'ERC721OutOfBoundsIndex')
.withArgs(this.owner.address, 2n); .withArgs(this.owner, 2n);
}); });
}); });
@ -798,7 +788,7 @@ function shouldBehaveLikeERC721Enumerable() {
it('reverts', async function () { it('reverts', async function () {
await expect(this.token.tokenOfOwnerByIndex(this.other, 0n)) await expect(this.token.tokenOfOwnerByIndex(this.other, 0n))
.to.be.revertedWithCustomError(this.token, 'ERC721OutOfBoundsIndex') .to.be.revertedWithCustomError(this.token, 'ERC721OutOfBoundsIndex')
.withArgs(this.other.address, 0n); .withArgs(this.other, 0n);
}); });
}); });
@ -821,7 +811,7 @@ function shouldBehaveLikeERC721Enumerable() {
expect(await this.token.balanceOf(this.owner)).to.equal(0n); expect(await this.token.balanceOf(this.owner)).to.equal(0n);
await expect(this.token.tokenOfOwnerByIndex(this.owner, 0n)) await expect(this.token.tokenOfOwnerByIndex(this.owner, 0n))
.to.be.revertedWithCustomError(this.token, 'ERC721OutOfBoundsIndex') .to.be.revertedWithCustomError(this.token, 'ERC721OutOfBoundsIndex')
.withArgs(this.owner.address, 0n); .withArgs(this.owner, 0n);
}); });
}); });
}); });

@ -32,7 +32,7 @@ describe('ERC721Burnable', function () {
await expect(this.token.connect(this.owner).burn(tokenId)) await expect(this.token.connect(this.owner).burn(tokenId))
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.owner.address, ethers.ZeroAddress, tokenId); .withArgs(this.owner, ethers.ZeroAddress, tokenId);
await expect(this.token.ownerOf(tokenId)) await expect(this.token.ownerOf(tokenId))
.to.be.revertedWithCustomError(this.token, 'ERC721NonexistentToken') .to.be.revertedWithCustomError(this.token, 'ERC721NonexistentToken')
@ -61,7 +61,7 @@ describe('ERC721Burnable', function () {
it('reverts', async function () { it('reverts', async function () {
await expect(this.token.connect(this.another).burn(tokenId)) await expect(this.token.connect(this.another).burn(tokenId))
.to.be.revertedWithCustomError(this.token, 'ERC721InsufficientApproval') .to.be.revertedWithCustomError(this.token, 'ERC721InsufficientApproval')
.withArgs(this.another.address, tokenId); .withArgs(this.another, tokenId);
}); });
}); });

@ -51,7 +51,7 @@ describe('ERC721Consecutive', function () {
first /* fromTokenId */, first /* fromTokenId */,
first + batch.amount - 1n /* toTokenId */, first + batch.amount - 1n /* toTokenId */,
ethers.ZeroAddress /* fromAddress */, ethers.ZeroAddress /* fromAddress */,
batch.receiver.address /* toAddress */, batch.receiver /* toAddress */,
); );
} else { } else {
// ".to.not.emit" only looks at event name, and doesn't check the parameters // ".to.not.emit" only looks at event name, and doesn't check the parameters
@ -125,7 +125,7 @@ describe('ERC721Consecutive', function () {
await expect(this.token.$_mint(this.alice, tokenId)) await expect(this.token.$_mint(this.alice, tokenId))
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.alice.address, tokenId); .withArgs(ethers.ZeroAddress, this.alice, tokenId);
}); });
it('cannot mint a token that has been batched minted', async function () { it('cannot mint a token that has been batched minted', async function () {
@ -145,13 +145,13 @@ describe('ERC721Consecutive', function () {
it('core takes over ownership on transfer', async function () { it('core takes over ownership on transfer', async function () {
await this.token.connect(this.alice).transferFrom(this.alice, this.receiver, tokenId); await this.token.connect(this.alice).transferFrom(this.alice, this.receiver, tokenId);
expect(await this.token.ownerOf(tokenId)).to.equal(this.receiver.address); expect(await this.token.ownerOf(tokenId)).to.equal(this.receiver);
}); });
it('tokens can be burned and re-minted #1', async function () { it('tokens can be burned and re-minted #1', async function () {
await expect(this.token.connect(this.alice).$_burn(tokenId)) await expect(this.token.connect(this.alice).$_burn(tokenId))
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.alice.address, ethers.ZeroAddress, tokenId); .withArgs(this.alice, ethers.ZeroAddress, tokenId);
await expect(this.token.ownerOf(tokenId)) await expect(this.token.ownerOf(tokenId))
.to.be.revertedWithCustomError(this.token, 'ERC721NonexistentToken') .to.be.revertedWithCustomError(this.token, 'ERC721NonexistentToken')
@ -159,9 +159,9 @@ describe('ERC721Consecutive', function () {
await expect(this.token.$_mint(this.bruce, tokenId)) await expect(this.token.$_mint(this.bruce, tokenId))
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.bruce.address, tokenId); .withArgs(ethers.ZeroAddress, this.bruce, tokenId);
expect(await this.token.ownerOf(tokenId)).to.equal(this.bruce.address); expect(await this.token.ownerOf(tokenId)).to.equal(this.bruce);
}); });
it('tokens can be burned and re-minted #2', async function () { it('tokens can be burned and re-minted #2', async function () {
@ -174,14 +174,14 @@ describe('ERC721Consecutive', function () {
// mint // mint
await expect(this.token.$_mint(this.alice, tokenId)) await expect(this.token.$_mint(this.alice, tokenId))
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.alice.address, tokenId); .withArgs(ethers.ZeroAddress, this.alice, tokenId);
expect(await this.token.ownerOf(tokenId)).to.equal(this.alice.address); expect(await this.token.ownerOf(tokenId)).to.equal(this.alice);
// burn // burn
await expect(await this.token.$_burn(tokenId)) await expect(await this.token.$_burn(tokenId))
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.alice.address, ethers.ZeroAddress, tokenId); .withArgs(this.alice, ethers.ZeroAddress, tokenId);
await expect(this.token.ownerOf(tokenId)) await expect(this.token.ownerOf(tokenId))
.to.be.revertedWithCustomError(this.token, 'ERC721NonexistentToken') .to.be.revertedWithCustomError(this.token, 'ERC721NonexistentToken')
@ -190,9 +190,9 @@ describe('ERC721Consecutive', function () {
// re-mint // re-mint
await expect(this.token.$_mint(this.bruce, tokenId)) await expect(this.token.$_mint(this.bruce, tokenId))
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.bruce.address, tokenId); .withArgs(ethers.ZeroAddress, this.bruce, tokenId);
expect(await this.token.ownerOf(tokenId)).to.equal(this.bruce.address); expect(await this.token.ownerOf(tokenId)).to.equal(this.bruce);
}); });
}); });
}); });

@ -68,7 +68,7 @@ describe('ERC721Pausable', function () {
describe('ownerOf', function () { describe('ownerOf', function () {
it('returns the amount of tokens owned by the given address', async function () { it('returns the amount of tokens owned by the given address', async function () {
expect(await this.token.ownerOf(tokenId)).to.equal(this.owner.address); expect(await this.token.ownerOf(tokenId)).to.equal(this.owner);
}); });
}); });

@ -18,7 +18,7 @@ async function fixture() {
return { owner, token }; return { owner, token };
} }
contract('ERC721URIStorage', function () { describe('ERC721URIStorage', function () {
beforeEach(async function () { beforeEach(async function () {
Object.assign(this, await loadFixture(fixture)); Object.assign(this, await loadFixture(fixture));
}); });

@ -2,7 +2,7 @@ const { ethers } = require('hardhat');
const { expect } = require('chai'); const { expect } = require('chai');
const { loadFixture, mine } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture, mine } = require('@nomicfoundation/hardhat-network-helpers');
const { bigint: time } = require('../../../helpers/time'); const time = require('../../../helpers/time');
const { shouldBehaveLikeVotes } = require('../../../governance/utils/Votes.behavior'); const { shouldBehaveLikeVotes } = require('../../../governance/utils/Votes.behavior');
@ -58,7 +58,7 @@ describe('ERC721Votes', function () {
it('no delegation', async function () { it('no delegation', async function () {
await expect(this.votes.connect(this.holder).transferFrom(this.holder, this.recipient, tokens[0])) await expect(this.votes.connect(this.holder).transferFrom(this.holder, this.recipient, tokens[0]))
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.recipient.address, tokens[0]) .withArgs(this.holder, this.recipient, tokens[0])
.to.not.emit(this.token, 'DelegateVotesChanged'); .to.not.emit(this.token, 'DelegateVotesChanged');
this.holderVotes = 0n; this.holderVotes = 0n;
@ -71,9 +71,9 @@ describe('ERC721Votes', function () {
const tx = await this.votes.connect(this.holder).transferFrom(this.holder, this.recipient, tokens[0]); const tx = await this.votes.connect(this.holder).transferFrom(this.holder, this.recipient, tokens[0]);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.recipient.address, tokens[0]) .withArgs(this.holder, this.recipient, tokens[0])
.to.emit(this.token, 'DelegateVotesChanged') .to.emit(this.token, 'DelegateVotesChanged')
.withArgs(this.holder.address, 1n, 0n); .withArgs(this.holder, 1n, 0n);
const { logs } = await tx.wait(); const { logs } = await tx.wait();
const { index } = logs.find(event => event.fragment.name == 'DelegateVotesChanged'); const { index } = logs.find(event => event.fragment.name == 'DelegateVotesChanged');
@ -91,9 +91,9 @@ describe('ERC721Votes', function () {
const tx = await this.votes.connect(this.holder).transferFrom(this.holder, this.recipient, tokens[0]); const tx = await this.votes.connect(this.holder).transferFrom(this.holder, this.recipient, tokens[0]);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.recipient.address, tokens[0]) .withArgs(this.holder, this.recipient, tokens[0])
.to.emit(this.token, 'DelegateVotesChanged') .to.emit(this.token, 'DelegateVotesChanged')
.withArgs(this.recipient.address, 0n, 1n); .withArgs(this.recipient, 0n, 1n);
const { logs } = await tx.wait(); const { logs } = await tx.wait();
const { index } = logs.find(event => event.fragment.name == 'DelegateVotesChanged'); const { index } = logs.find(event => event.fragment.name == 'DelegateVotesChanged');
@ -112,11 +112,11 @@ describe('ERC721Votes', function () {
const tx = await this.votes.connect(this.holder).transferFrom(this.holder, this.recipient, tokens[0]); const tx = await this.votes.connect(this.holder).transferFrom(this.holder, this.recipient, tokens[0]);
await expect(tx) await expect(tx)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.holder.address, this.recipient.address, tokens[0]) .withArgs(this.holder, this.recipient, tokens[0])
.to.emit(this.token, 'DelegateVotesChanged') .to.emit(this.token, 'DelegateVotesChanged')
.withArgs(this.holder.address, 1n, 0n) .withArgs(this.holder, 1n, 0n)
.to.emit(this.token, 'DelegateVotesChanged') .to.emit(this.token, 'DelegateVotesChanged')
.withArgs(this.recipient.address, 0n, 1n); .withArgs(this.recipient, 0n, 1n);
const { logs } = await tx.wait(); const { logs } = await tx.wait();
const { index } = logs.find(event => event.fragment.name == 'DelegateVotesChanged'); const { index } = logs.find(event => event.fragment.name == 'DelegateVotesChanged');

@ -35,7 +35,7 @@ describe('ERC721Wrapper', function () {
}); });
it('has underlying', async function () { it('has underlying', async function () {
expect(await this.token.underlying()).to.equal(this.underlying.target); expect(await this.token.underlying()).to.equal(this.underlying);
}); });
describe('depositFor', function () { describe('depositFor', function () {
@ -44,9 +44,9 @@ describe('ERC721Wrapper', function () {
await expect(this.token.connect(this.owner).depositFor(this.owner, [tokenId])) await expect(this.token.connect(this.owner).depositFor(this.owner, [tokenId]))
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.owner.address, this.token.target, tokenId) .withArgs(this.owner, this.token, tokenId)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.owner.address, tokenId); .withArgs(ethers.ZeroAddress, this.owner, tokenId);
}); });
it('works with approval for all', async function () { it('works with approval for all', async function () {
@ -54,9 +54,9 @@ describe('ERC721Wrapper', function () {
await expect(this.token.connect(this.owner).depositFor(this.owner, [tokenId])) await expect(this.token.connect(this.owner).depositFor(this.owner, [tokenId]))
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.owner.address, this.token.target, tokenId) .withArgs(this.owner, this.token, tokenId)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.owner.address, tokenId); .withArgs(ethers.ZeroAddress, this.owner, tokenId);
}); });
it('works sending to another account', async function () { it('works sending to another account', async function () {
@ -64,9 +64,9 @@ describe('ERC721Wrapper', function () {
await expect(this.token.connect(this.owner).depositFor(this.other, [tokenId])) await expect(this.token.connect(this.owner).depositFor(this.other, [tokenId]))
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.owner.address, this.token.target, tokenId) .withArgs(this.owner, this.token, tokenId)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.other.address, tokenId); .withArgs(ethers.ZeroAddress, this.other, tokenId);
}); });
it('works with multiple tokens', async function () { it('works with multiple tokens', async function () {
@ -75,19 +75,19 @@ describe('ERC721Wrapper', function () {
await expect(this.token.connect(this.owner).depositFor(this.owner, [tokenId, otherTokenId])) await expect(this.token.connect(this.owner).depositFor(this.owner, [tokenId, otherTokenId]))
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.owner.address, this.token.target, tokenId) .withArgs(this.owner, this.token, tokenId)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.owner.address, tokenId) .withArgs(ethers.ZeroAddress, this.owner, tokenId)
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.owner.address, this.token.target, otherTokenId) .withArgs(this.owner, this.token, otherTokenId)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.owner.address, otherTokenId); .withArgs(ethers.ZeroAddress, this.owner, otherTokenId);
}); });
it('reverts with missing approval', async function () { it('reverts with missing approval', async function () {
await expect(this.token.connect(this.owner).depositFor(this.owner, [tokenId])) await expect(this.token.connect(this.owner).depositFor(this.owner, [tokenId]))
.to.be.revertedWithCustomError(this.token, 'ERC721InsufficientApproval') .to.be.revertedWithCustomError(this.token, 'ERC721InsufficientApproval')
.withArgs(this.token.target, tokenId); .withArgs(this.token, tokenId);
}); });
}); });
@ -100,9 +100,9 @@ describe('ERC721Wrapper', function () {
it('works for an owner', async function () { it('works for an owner', async function () {
await expect(this.token.connect(this.owner).withdrawTo(this.owner, [tokenId])) await expect(this.token.connect(this.owner).withdrawTo(this.owner, [tokenId]))
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.token.target, this.owner.address, tokenId) .withArgs(this.token, this.owner, tokenId)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.owner.address, ethers.ZeroAddress, tokenId); .withArgs(this.owner, ethers.ZeroAddress, tokenId);
}); });
it('works for an approved', async function () { it('works for an approved', async function () {
@ -110,9 +110,9 @@ describe('ERC721Wrapper', function () {
await expect(this.token.connect(this.approved).withdrawTo(this.owner, [tokenId])) await expect(this.token.connect(this.approved).withdrawTo(this.owner, [tokenId]))
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.token.target, this.owner.address, tokenId) .withArgs(this.token, this.owner, tokenId)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.owner.address, ethers.ZeroAddress, tokenId); .withArgs(this.owner, ethers.ZeroAddress, tokenId);
}); });
it('works for an approved for all', async function () { it('works for an approved for all', async function () {
@ -120,15 +120,15 @@ describe('ERC721Wrapper', function () {
await expect(this.token.connect(this.approved).withdrawTo(this.owner, [tokenId])) await expect(this.token.connect(this.approved).withdrawTo(this.owner, [tokenId]))
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.token.target, this.owner.address, tokenId) .withArgs(this.token, this.owner, tokenId)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.owner.address, ethers.ZeroAddress, tokenId); .withArgs(this.owner, ethers.ZeroAddress, tokenId);
}); });
it("doesn't work for a non-owner nor approved", async function () { it("doesn't work for a non-owner nor approved", async function () {
await expect(this.token.connect(this.other).withdrawTo(this.owner, [tokenId])) await expect(this.token.connect(this.other).withdrawTo(this.owner, [tokenId]))
.to.be.revertedWithCustomError(this.token, 'ERC721InsufficientApproval') .to.be.revertedWithCustomError(this.token, 'ERC721InsufficientApproval')
.withArgs(this.other.address, tokenId); .withArgs(this.other, tokenId);
}); });
it('works with multiple tokens', async function () { it('works with multiple tokens', async function () {
@ -137,21 +137,21 @@ describe('ERC721Wrapper', function () {
await expect(this.token.connect(this.owner).withdrawTo(this.owner, [tokenId, otherTokenId])) await expect(this.token.connect(this.owner).withdrawTo(this.owner, [tokenId, otherTokenId]))
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.token.target, this.owner.address, tokenId) .withArgs(this.token, this.owner, tokenId)
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.token.target, this.owner.address, tokenId) .withArgs(this.token, this.owner, tokenId)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.owner.address, ethers.ZeroAddress, tokenId) .withArgs(this.owner, ethers.ZeroAddress, tokenId)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.owner.address, ethers.ZeroAddress, tokenId); .withArgs(this.owner, ethers.ZeroAddress, tokenId);
}); });
it('works to another account', async function () { it('works to another account', async function () {
await expect(this.token.connect(this.owner).withdrawTo(this.other, [tokenId])) await expect(this.token.connect(this.owner).withdrawTo(this.other, [tokenId]))
.to.emit(this.underlying, 'Transfer') .to.emit(this.underlying, 'Transfer')
.withArgs(this.token.target, this.other.address, tokenId) .withArgs(this.token, this.other, tokenId)
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(this.owner.address, ethers.ZeroAddress, tokenId); .withArgs(this.owner, ethers.ZeroAddress, tokenId);
}); });
}); });
@ -166,13 +166,13 @@ describe('ERC721Wrapper', function () {
), ),
) )
.to.be.revertedWithCustomError(this.token, 'ERC721UnsupportedToken') .to.be.revertedWithCustomError(this.token, 'ERC721UnsupportedToken')
.withArgs(this.other.address); .withArgs(this.other);
}); });
it('mints a token to from', async function () { it('mints a token to from', async function () {
await expect(this.underlying.connect(this.owner).safeTransferFrom(this.owner, this.token, tokenId)) await expect(this.underlying.connect(this.owner).safeTransferFrom(this.owner, this.token, tokenId))
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.owner.address, tokenId); .withArgs(ethers.ZeroAddress, this.owner, tokenId);
}); });
}); });
@ -183,7 +183,7 @@ describe('ERC721Wrapper', function () {
await expect(this.token.$_recover(this.other, tokenId)) await expect(this.token.$_recover(this.other, tokenId))
.to.emit(this.token, 'Transfer') .to.emit(this.token, 'Transfer')
.withArgs(ethers.ZeroAddress, this.other.address, tokenId); .withArgs(ethers.ZeroAddress, this.other, tokenId);
}); });
it('reverts if there is nothing to recover', async function () { it('reverts if there is nothing to recover', async function () {
@ -191,7 +191,7 @@ describe('ERC721Wrapper', function () {
await expect(this.token.$_recover(holder, tokenId)) await expect(this.token.$_recover(holder, tokenId))
.to.be.revertedWithCustomError(this.token, 'ERC721IncorrectOwner') .to.be.revertedWithCustomError(this.token, 'ERC721IncorrectOwner')
.withArgs(this.token.target, tokenId, holder); .withArgs(this.token, tokenId, holder);
}); });
}); });

@ -15,6 +15,6 @@ describe('ERC721Holder', function () {
const receiver = await ethers.deployContract('$ERC721Holder'); const receiver = await ethers.deployContract('$ERC721Holder');
await token.connect(owner).safeTransferFrom(owner, receiver, tokenId); await token.connect(owner).safeTransferFrom(owner, receiver, tokenId);
expect(await token.ownerOf(tokenId)).to.equal(receiver.target); expect(await token.ownerOf(tokenId)).to.equal(receiver);
}); });
}); });

@ -29,7 +29,7 @@ describe('Address', function () {
it('reverts when sending non-zero amounts', async function () { it('reverts when sending non-zero amounts', async function () {
await expect(this.mock.$sendValue(this.other, 1)) await expect(this.mock.$sendValue(this.other, 1))
.to.be.revertedWithCustomError(this.mock, 'AddressInsufficientBalance') .to.be.revertedWithCustomError(this.mock, 'AddressInsufficientBalance')
.withArgs(this.mock.target); .withArgs(this.mock);
}); });
}); });
@ -42,7 +42,7 @@ describe('Address', function () {
describe('with EOA recipient', function () { describe('with EOA recipient', function () {
it('sends 0 wei', async function () { it('sends 0 wei', async function () {
await expect(this.mock.$sendValue(this.recipient, 0)).to.changeEtherBalance(this.recipient.address, 0); await expect(this.mock.$sendValue(this.recipient, 0)).to.changeEtherBalance(this.recipient, 0);
}); });
it('sends non-zero amounts', async function () { it('sends non-zero amounts', async function () {
@ -60,7 +60,7 @@ describe('Address', function () {
it('reverts when sending more than the balance', async function () { it('reverts when sending more than the balance', async function () {
await expect(this.mock.$sendValue(this.recipient, funds + 1n)) await expect(this.mock.$sendValue(this.recipient, funds + 1n))
.to.be.revertedWithCustomError(this.mock, 'AddressInsufficientBalance') .to.be.revertedWithCustomError(this.mock, 'AddressInsufficientBalance')
.withArgs(this.mock.target); .withArgs(this.mock);
}); });
}); });
@ -145,7 +145,7 @@ describe('Address', function () {
await expect(this.mock.$functionCall(this.recipient, call)) await expect(this.mock.$functionCall(this.recipient, call))
.to.be.revertedWithCustomError(this.mock, 'AddressEmptyCode') .to.be.revertedWithCustomError(this.mock, 'AddressEmptyCode')
.withArgs(this.recipient.address); .withArgs(this.recipient);
}); });
}); });
}); });
@ -170,7 +170,7 @@ describe('Address', function () {
await expect(this.mock.$functionCallWithValue(this.target, call, value)) await expect(this.mock.$functionCallWithValue(this.target, call, value))
.to.be.revertedWithCustomError(this.mock, 'AddressInsufficientBalance') .to.be.revertedWithCustomError(this.mock, 'AddressInsufficientBalance')
.withArgs(this.mock.target); .withArgs(this.mock);
}); });
it('calls the requested function with existing value', async function () { it('calls the requested function with existing value', async function () {
@ -240,7 +240,7 @@ describe('Address', function () {
await expect(this.mock.$functionStaticCall(this.recipient, call)) await expect(this.mock.$functionStaticCall(this.recipient, call))
.to.be.revertedWithCustomError(this.mock, 'AddressEmptyCode') .to.be.revertedWithCustomError(this.mock, 'AddressEmptyCode')
.withArgs(this.recipient.address); .withArgs(this.recipient);
}); });
}); });
@ -273,7 +273,7 @@ describe('Address', function () {
await expect(this.mock.$functionDelegateCall(this.recipient, call)) await expect(this.mock.$functionDelegateCall(this.recipient, call))
.to.be.revertedWithCustomError(this.mock, 'AddressEmptyCode') .to.be.revertedWithCustomError(this.mock, 'AddressEmptyCode')
.withArgs(this.recipient.address); .withArgs(this.recipient);
}); });
}); });

@ -12,15 +12,13 @@ function shouldBehaveLikeRegularContext() {
describe('msgSender', function () { describe('msgSender', function () {
it('returns the transaction sender when called from an EOA', async function () { it('returns the transaction sender when called from an EOA', async function () {
await expect(this.context.connect(this.sender).msgSender()) await expect(this.context.connect(this.sender).msgSender()).to.emit(this.context, 'Sender').withArgs(this.sender);
.to.emit(this.context, 'Sender')
.withArgs(this.sender.address);
}); });
it('returns the transaction sender when called from another contract', async function () { it('returns the transaction sender when called from another contract', async function () {
await expect(this.contextHelper.connect(this.sender).callSender(this.context)) await expect(this.contextHelper.connect(this.sender).callSender(this.context))
.to.emit(this.context, 'Sender') .to.emit(this.context, 'Sender')
.withArgs(this.contextHelper.target); .withArgs(this.contextHelper);
}); });
}); });

@ -68,7 +68,7 @@ describe('Create2', function () {
.to.emit(this.factory, 'return$deploy') .to.emit(this.factory, 'return$deploy')
.withArgs(offChainComputed); .withArgs(offChainComputed);
expect(this.constructorLessBytecode).to.include((await web3.eth.getCode(offChainComputed)).slice(2)); expect(this.constructorLessBytecode).to.include((await ethers.provider.getCode(offChainComputed)).slice(2));
}); });
it('deploys a contract with constructor arguments', async function () { it('deploys a contract with constructor arguments', async function () {
@ -84,7 +84,7 @@ describe('Create2', function () {
const instance = await ethers.getContractAt('VestingWallet', offChainComputed); const instance = await ethers.getContractAt('VestingWallet', offChainComputed);
expect(await instance.owner()).to.equal(this.other.address); expect(await instance.owner()).to.equal(this.other);
}); });
it('deploys a contract with funds deposited in the factory', async function () { it('deploys a contract with funds deposited in the factory', async function () {

@ -29,9 +29,9 @@ describe('Multicall', function () {
]), ]),
) )
.to.emit(this.mock, 'Transfer') .to.emit(this.mock, 'Transfer')
.withArgs(this.holder.address, this.alice.address, this.amount / 2n) .withArgs(this.holder, this.alice, this.amount / 2n)
.to.emit(this.mock, 'Transfer') .to.emit(this.mock, 'Transfer')
.withArgs(this.holder.address, this.bruce.address, this.amount / 3n); .withArgs(this.holder, this.bruce, this.amount / 3n);
expect(await this.mock.balanceOf(this.alice)).to.equal(this.amount / 2n); expect(await this.mock.balanceOf(this.alice)).to.equal(this.amount / 2n);
expect(await this.mock.balanceOf(this.bruce)).to.equal(this.amount / 3n); expect(await this.mock.balanceOf(this.bruce)).to.equal(this.amount / 3n);
@ -54,7 +54,7 @@ describe('Multicall', function () {
]), ]),
) )
.to.be.revertedWithCustomError(this.mock, 'ERC20InsufficientBalance') .to.be.revertedWithCustomError(this.mock, 'ERC20InsufficientBalance')
.withArgs(this.holder.address, 0, this.amount); .withArgs(this.holder, 0, this.amount);
expect(await this.mock.balanceOf(this.alice)).to.equal(0n); expect(await this.mock.balanceOf(this.alice)).to.equal(0n);
}); });
@ -67,6 +67,6 @@ describe('Multicall', function () {
]), ]),
) )
.to.be.revertedWithCustomError(this.mock, 'ERC20InsufficientBalance') .to.be.revertedWithCustomError(this.mock, 'ERC20InsufficientBalance')
.withArgs(this.holder.address, 0, this.amount); .withArgs(this.holder, 0, this.amount);
}); });
}); });

@ -69,7 +69,7 @@ describe('Nonces', function () {
await expect(this.mock.$_useCheckedNonce(this.sender, currentNonce + 1n)) await expect(this.mock.$_useCheckedNonce(this.sender, currentNonce + 1n))
.to.be.revertedWithCustomError(this.mock, 'InvalidAccountNonce') .to.be.revertedWithCustomError(this.mock, 'InvalidAccountNonce')
.withArgs(this.sender.address, currentNonce); .withArgs(this.sender, currentNonce);
}); });
}); });
}); });

@ -39,7 +39,7 @@ describe('Pausable', function () {
}); });
it('emits a Paused event', async function () { it('emits a Paused event', async function () {
await expect(this.tx).to.emit(this.mock, 'Paused').withArgs(this.pauser.address); await expect(this.tx).to.emit(this.mock, 'Paused').withArgs(this.pauser);
}); });
it('cannot perform normal process in pause', async function () { it('cannot perform normal process in pause', async function () {
@ -67,7 +67,7 @@ describe('Pausable', function () {
}); });
it('emits an Unpaused event', async function () { it('emits an Unpaused event', async function () {
await expect(this.tx).to.emit(this.mock, 'Unpaused').withArgs(this.pauser.address); await expect(this.tx).to.emit(this.mock, 'Unpaused').withArgs(this.pauser);
}); });
it('should resume allowing normal process', async function () { it('should resume allowing normal process', async function () {

@ -6,10 +6,6 @@ const TEST_MESSAGE = ethers.id('OpenZeppelin');
const WRONG_MESSAGE = ethers.id('Nope'); const WRONG_MESSAGE = ethers.id('Nope');
const NON_HASH_MESSAGE = '0xabcd'; const NON_HASH_MESSAGE = '0xabcd';
function toSignature(signature) {
return ethers.Signature.from(signature);
}
async function fixture() { async function fixture() {
const [signer] = await ethers.getSigners(); const [signer] = await ethers.getSigners();
const mock = await ethers.deployContract('$ECDSA'); const mock = await ethers.deployContract('$ECDSA');
@ -48,7 +44,7 @@ describe('ECDSA', function () {
const signature = await this.signer.signMessage(TEST_MESSAGE); const signature = await this.signer.signMessage(TEST_MESSAGE);
// Recover the signer address from the generated message and signature. // Recover the signer address from the generated message and signature.
expect(await this.mock.$recover(ethers.hashMessage(TEST_MESSAGE), signature)).to.equal(this.signer.address); expect(await this.mock.$recover(ethers.hashMessage(TEST_MESSAGE), signature)).to.equal(this.signer);
}); });
it('returns signer address with correct signature for arbitrary length message', async function () { it('returns signer address with correct signature for arbitrary length message', async function () {
@ -56,12 +52,12 @@ describe('ECDSA', function () {
const signature = await this.signer.signMessage(NON_HASH_MESSAGE); const signature = await this.signer.signMessage(NON_HASH_MESSAGE);
// Recover the signer address from the generated message and signature. // Recover the signer address from the generated message and signature.
expect(await this.mock.$recover(ethers.hashMessage(NON_HASH_MESSAGE), signature)).to.equal(this.signer.address); expect(await this.mock.$recover(ethers.hashMessage(NON_HASH_MESSAGE), signature)).to.equal(this.signer);
}); });
it('returns a different address', async function () { it('returns a different address', async function () {
const signature = await this.signer.signMessage(TEST_MESSAGE); const signature = await this.signer.signMessage(TEST_MESSAGE);
expect(await this.mock.$recover(WRONG_MESSAGE, signature)).to.not.be.equal(this.signer.address); expect(await this.mock.$recover(WRONG_MESSAGE, signature)).to.not.be.equal(this.signer);
}); });
it('reverts with invalid signature', async function () { it('reverts with invalid signature', async function () {
@ -76,7 +72,6 @@ describe('ECDSA', function () {
}); });
describe('with v=27 signature', function () { describe('with v=27 signature', function () {
// Signature generated outside ganache with method web3.eth.sign(signer, message)
const signer = '0x2cc1166f6212628A0deEf2B33BEFB2187D35b86c'; const signer = '0x2cc1166f6212628A0deEf2B33BEFB2187D35b86c';
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
const signatureWithoutV = const signatureWithoutV =
@ -87,7 +82,7 @@ describe('ECDSA', function () {
const signature = ethers.concat([signatureWithoutV, v]); const signature = ethers.concat([signatureWithoutV, v]);
expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.equal(signer); expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.equal(signer);
const { r, s, yParityAndS: vs } = toSignature(signature); const { r, s, yParityAndS: vs } = ethers.Signature.from(signature);
expect(await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s)).to.equal( expect(await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s)).to.equal(
signer, signer,
); );
@ -100,7 +95,7 @@ describe('ECDSA', function () {
const signature = ethers.concat([signatureWithoutV, v]); const signature = ethers.concat([signatureWithoutV, v]);
expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.not.equal(signer); expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.not.equal(signer);
const { r, s, yParityAndS: vs } = toSignature(signature); const { r, s, yParityAndS: vs } = ethers.Signature.from(signature);
expect( expect(
await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s), await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s),
).to.not.equal(signer); ).to.not.equal(signer);
@ -118,7 +113,7 @@ describe('ECDSA', function () {
'ECDSAInvalidSignature', 'ECDSAInvalidSignature',
); );
const { r, s } = toSignature(signature); const { r, s } = ethers.Signature.from(signature);
await expect( await expect(
this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s), this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s),
).to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignature'); ).to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignature');
@ -128,7 +123,9 @@ describe('ECDSA', function () {
it('rejects short EIP2098 format', async function () { it('rejects short EIP2098 format', async function () {
const v = '0x1b'; // 27 = 1b. const v = '0x1b'; // 27 = 1b.
const signature = ethers.concat([signatureWithoutV, v]); const signature = ethers.concat([signatureWithoutV, v]);
await expect(this.mock.$recover(TEST_MESSAGE, toSignature(signature).compactSerialized))
const { compactSerialized } = ethers.Signature.from(signature);
await expect(this.mock.$recover(TEST_MESSAGE, compactSerialized))
.to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureLength') .to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureLength')
.withArgs(64); .withArgs(64);
}); });
@ -145,7 +142,7 @@ describe('ECDSA', function () {
const signature = ethers.concat([signatureWithoutV, v]); const signature = ethers.concat([signatureWithoutV, v]);
expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.equal(signer); expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.equal(signer);
const { r, s, yParityAndS: vs } = toSignature(signature); const { r, s, yParityAndS: vs } = ethers.Signature.from(signature);
expect(await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s)).to.equal( expect(await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s)).to.equal(
signer, signer,
); );
@ -158,7 +155,7 @@ describe('ECDSA', function () {
const signature = ethers.concat([signatureWithoutV, v]); const signature = ethers.concat([signatureWithoutV, v]);
expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.not.equal(signer); expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.not.equal(signer);
const { r, s, yParityAndS: vs } = toSignature(signature); const { r, s, yParityAndS: vs } = ethers.Signature.from(signature);
expect( expect(
await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s), await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s),
).to.not.equal(signer); ).to.not.equal(signer);
@ -176,7 +173,7 @@ describe('ECDSA', function () {
'ECDSAInvalidSignature', 'ECDSAInvalidSignature',
); );
const { r, s } = toSignature(signature); const { r, s } = ethers.Signature.from(signature);
await expect( await expect(
this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s), this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s),
).to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignature'); ).to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignature');
@ -184,9 +181,11 @@ describe('ECDSA', function () {
}); });
it('rejects short EIP2098 format', async function () { it('rejects short EIP2098 format', async function () {
const v = '0x1c'; // 27 = 1b. const v = '0x1b'; // 28 = 1b.
const signature = ethers.concat([signatureWithoutV, v]); const signature = ethers.concat([signatureWithoutV, v]);
await expect(this.mock.$recover(TEST_MESSAGE, toSignature(signature).compactSerialized))
const { compactSerialized } = ethers.Signature.from(signature);
await expect(this.mock.$recover(TEST_MESSAGE, compactSerialized))
.to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureLength') .to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureLength')
.withArgs(64); .withArgs(64);
}); });
@ -208,7 +207,7 @@ describe('ECDSA', function () {
await expect(this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s)) await expect(this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s))
.to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureS') .to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureS')
.withArgs(s); .withArgs(s);
expect(() => toSignature(highSSignature)).to.throw('non-canonical s'); expect(() => ethers.Signature.from(highSSignature)).to.throw('non-canonical s');
}); });
}); });
}); });

@ -4,7 +4,6 @@ const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { getDomain, domainSeparator, hashTypedData } = require('../../helpers/eip712'); const { getDomain, domainSeparator, hashTypedData } = require('../../helpers/eip712');
const { formatType } = require('../../helpers/eip712-types'); const { formatType } = require('../../helpers/eip712-types');
const { getChainId } = require('../../helpers/chainid');
const LENGTHS = { const LENGTHS = {
short: ['A Name', '1'], short: ['A Name', '1'],
@ -21,7 +20,7 @@ const fixture = async () => {
lengths[shortOrLong].domain = { lengths[shortOrLong].domain = {
name, name,
version, version,
chainId: await getChainId(), chainId: await ethers.provider.getNetwork().then(({ chainId }) => chainId),
verifyingContract: lengths[shortOrLong].eip712.target, verifyingContract: lengths[shortOrLong].eip712.target,
}; };
} }

@ -9,7 +9,7 @@ async function fixture() {
}; };
} }
contract('ERC165', function () { describe('ERC165', function () {
beforeEach(async function () { beforeEach(async function () {
Object.assign(this, await loadFixture(fixture)); Object.assign(this, await loadFixture(fixture));
}); });

@ -1,5 +1,5 @@
const { expect } = require('chai'); const { expect } = require('chai');
const { selector, interfaceId } = require('../../helpers/methods'); const { interfaceId } = require('../../helpers/methods');
const { mapValues } = require('../../helpers/iterate'); const { mapValues } = require('../../helpers/iterate');
const INVALID_ID = '0xffffffff'; const INVALID_ID = '0xffffffff';
@ -123,15 +123,6 @@ function shouldSupportInterfaces(interfaces = []) {
// Check the presence of each function in the contract's interface // Check the presence of each function in the contract's interface
for (const fnSig of SIGNATURES[k]) { for (const fnSig of SIGNATURES[k]) {
// TODO: Remove Truffle case when ethersjs migration is done
if (this.contractUnderTest.abi) {
const fnSelector = selector(fnSig);
return expect(this.contractUnderTest.abi.filter(fn => fn.signature === fnSelector).length).to.equal(
1,
`did not find ${fnSig}`,
);
}
expect(this.contractUnderTest.interface.hasFunction(fnSig), `did not find ${fnSig}`).to.be.true; expect(this.contractUnderTest.interface.hasFunction(fnSig), `did not find ${fnSig}`).to.be.true;
} }
} }

@ -3,10 +3,8 @@ const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic');
const { Rounding } = require('../../helpers/enums');
const { min, max } = require('../../helpers/math'); const { min, max } = require('../../helpers/math');
const {
bigint: { Rounding },
} = require('../../helpers/enums.js');
const RoundingDown = [Rounding.Floor, Rounding.Trunc]; const RoundingDown = [Rounding.Floor, Rounding.Trunc];
const RoundingUp = [Rounding.Ceil, Rounding.Expand]; const RoundingUp = [Rounding.Ceil, Rounding.Expand];

@ -9,7 +9,7 @@ async function fixture() {
return { mock }; return { mock };
} }
contract('SafeCast', function () { describe('SafeCast', function () {
beforeEach(async function () { beforeEach(async function () {
Object.assign(this, await loadFixture(fixture)); Object.assign(this, await loadFixture(fixture));
}); });

@ -14,7 +14,7 @@ async function fixture() {
return { mock }; return { mock };
} }
contract('SignedMath', function () { describe('SignedMath', function () {
beforeEach(async function () { beforeEach(async function () {
Object.assign(this, await loadFixture(fixture)); Object.assign(this, await loadFixture(fixture));
}); });

@ -2,7 +2,7 @@ const { ethers } = require('hardhat');
const { expect } = require('chai'); const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { VALUE_SIZES } = require('../../../scripts/generate/templates/Checkpoints.opts.js'); const { VALUE_SIZES } = require('../../../scripts/generate/templates/Checkpoints.opts');
const last = array => (array.length ? array[array.length - 1] : undefined); const last = array => (array.length ? array[array.length - 1] : undefined);

@ -4,9 +4,7 @@ const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { product } = require('../../helpers/iterate'); const { product } = require('../../helpers/iterate');
const { max } = require('../../helpers/math'); const { max } = require('../../helpers/math');
const { const time = require('../../helpers/time');
bigint: { clock },
} = require('../../helpers/time');
const MAX_UINT32 = 1n << (32n - 1n); const MAX_UINT32 = 1n << (32n - 1n);
const MAX_UINT48 = 1n << (48n - 1n); const MAX_UINT48 = 1n << (48n - 1n);
@ -43,18 +41,18 @@ async function fixture() {
return { mock }; return { mock };
} }
contract('Time', function () { describe('Time', function () {
beforeEach(async function () { beforeEach(async function () {
Object.assign(this, await loadFixture(fixture)); Object.assign(this, await loadFixture(fixture));
}); });
describe('clocks', function () { describe('clocks', function () {
it('timestamp', async function () { it('timestamp', async function () {
expect(await this.mock.$timestamp()).to.equal(await clock.timestamp()); expect(await this.mock.$timestamp()).to.equal(await time.clock.timestamp());
}); });
it('block number', async function () { it('block number', async function () {
expect(await this.mock.$blockNumber()).to.equal(await clock.blocknumber()); expect(await this.mock.$blockNumber()).to.equal(await time.clock.blocknumber());
}); });
}); });
@ -92,7 +90,7 @@ contract('Time', function () {
}); });
it('get & getFull', async function () { it('get & getFull', async function () {
const timepoint = await clock.timestamp().then(BigInt); const timepoint = await time.clock.timestamp();
const valueBefore = 24194n; const valueBefore = 24194n;
const valueAfter = 4214143n; const valueAfter = 4214143n;
@ -110,7 +108,7 @@ contract('Time', function () {
}); });
it('withUpdate', async function () { it('withUpdate', async function () {
const timepoint = await clock.timestamp().then(BigInt); const timepoint = await time.clock.timestamp();
const valueBefore = 24194n; const valueBefore = 24194n;
const valueAfter = 4214143n; const valueAfter = 4214143n;
const newvalueAfter = 94716n; const newvalueAfter = 94716n;

Loading…
Cancel
Save