Merge branch 'OpenZeppelin:master' into master

pull/5464/head
Marson Kotovi4 1 week ago committed by GitHub
commit b6f469540a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      .changeset/eighty-hounds-promise.md
  2. 5
      .changeset/famous-timers-compare.md
  3. 5
      .changeset/four-chairs-help.md
  4. 5
      .changeset/good-cameras-serve.md
  5. 5
      .changeset/great-lions-hear.md
  6. 5
      .changeset/green-drinks-report.md
  7. 5
      .changeset/healthy-books-shout.md
  8. 5
      .changeset/hot-shrimps-wait.md
  9. 5
      .changeset/lovely-dodos-lay.md
  10. 5
      .changeset/pink-wasps-hammer.md
  11. 5
      .changeset/pretty-lobsters-tan.md
  12. 5
      .changeset/proud-planes-arrive.md
  13. 5
      .changeset/seven-donkeys-tap.md
  14. 5
      .changeset/small-seahorses-bathe.md
  15. 5
      .changeset/tricky-bats-pretend.md
  16. 5
      .changeset/weak-roses-bathe.md
  17. 3
      .github/actions/gas-compare/action.yml
  18. 3
      .github/actions/storage-layout/action.yml
  19. 1
      .gitignore
  20. 52
      CHANGELOG.md
  21. 2
      LICENSE
  22. 2
      README.md
  23. 2
      RELEASING.md
  24. 2
      SECURITY.md
  25. 2
      audits/2017-03.md
  26. 4
      certora/README.md
  27. 2
      certora/run.js
  28. 4
      certora/specs/AccessControlDefaultAdminRules.spec
  29. 6
      certora/specs/ERC20FlashMint.spec
  30. 2
      contracts/access/AccessControl.sol
  31. 14
      contracts/account/utils/draft-ERC4337Utils.sol
  32. 1
      contracts/account/utils/draft-ERC7579Utils.sol
  33. 4
      contracts/finance/VestingWallet.sol
  34. 20
      contracts/governance/Governor.sol
  35. 10
      contracts/governance/IGovernor.sol
  36. 12
      contracts/governance/README.adoc
  37. 4
      contracts/governance/extensions/GovernorCountingFractional.sol
  38. 35
      contracts/governance/extensions/GovernorCountingOverridable.sol
  39. 2
      contracts/governance/extensions/GovernorPreventLateQuorum.sol
  40. 57
      contracts/governance/extensions/GovernorProposalGuardian.sol
  41. 2
      contracts/governance/extensions/GovernorStorage.sol
  42. 2
      contracts/governance/extensions/GovernorTimelockAccess.sol
  43. 2
      contracts/governance/utils/Votes.sol
  44. 1
      contracts/governance/utils/VotesExtended.sol
  45. 2
      contracts/interfaces/IERC1271.sol
  46. 4
      contracts/interfaces/README.adoc
  47. 1
      contracts/interfaces/draft-IERC4337.sol
  48. 5
      contracts/interfaces/draft-IERC7579.sol
  49. 5
      contracts/metatx/ERC2771Forwarder.sol
  50. 27
      contracts/mocks/governance/GovernorProposalGuardianMock.sol
  51. 2
      contracts/package.json
  52. 2
      contracts/proxy/Clones.sol
  53. 2
      contracts/proxy/ERC1967/ERC1967Proxy.sol
  54. 2
      contracts/proxy/ERC1967/ERC1967Utils.sol
  55. 2
      contracts/proxy/beacon/BeaconProxy.sol
  56. 2
      contracts/proxy/transparent/ProxyAdmin.sol
  57. 2
      contracts/proxy/transparent/TransparentUpgradeableProxy.sol
  58. 3
      contracts/proxy/utils/UUPSUpgradeable.sol
  59. 4
      contracts/token/ERC1155/IERC1155.sol
  60. 4
      contracts/token/ERC1155/utils/ERC1155Utils.sol
  61. 2
      contracts/token/ERC20/ERC20.sol
  62. 2
      contracts/token/ERC20/extensions/ERC1363.sol
  63. 2
      contracts/token/ERC20/extensions/ERC4626.sol
  64. 2
      contracts/token/ERC20/utils/SafeERC20.sol
  65. 2
      contracts/token/ERC721/README.adoc
  66. 2
      contracts/token/ERC721/extensions/ERC721Consecutive.sol
  67. 2
      contracts/token/ERC721/extensions/ERC721URIStorage.sol
  68. 2
      contracts/token/ERC721/utils/ERC721Utils.sol
  69. 2
      contracts/token/common/ERC2981.sol
  70. 2
      contracts/utils/Address.sol
  71. 6
      contracts/utils/Arrays.sol
  72. 1
      contracts/utils/Bytes.sol
  73. 1
      contracts/utils/CAIP10.sol
  74. 1
      contracts/utils/CAIP2.sol
  75. 24
      contracts/utils/Calldata.sol
  76. 4
      contracts/utils/Multicall.sol
  77. 1
      contracts/utils/NoncesKeyed.sol
  78. 2
      contracts/utils/Packing.sol
  79. 7
      contracts/utils/Pausable.sol
  80. 11
      contracts/utils/README.adoc
  81. 2
      contracts/utils/ReentrancyGuardTransient.sol
  82. 4
      contracts/utils/ShortStrings.sol
  83. 20
      contracts/utils/Strings.sol
  84. 2
      contracts/utils/cryptography/EIP712.sol
  85. 4
      contracts/utils/cryptography/MessageHashUtils.sol
  86. 4
      contracts/utils/structs/MerkleTree.sol
  87. 11
      docs/modules/ROOT/pages/crowdsales.adoc
  88. 19
      docs/modules/ROOT/pages/drafts.adoc
  89. 2
      docs/modules/ROOT/pages/erc20-supply.adoc
  90. 4
      docs/modules/ROOT/pages/governance.adoc
  91. 4
      docs/templates/contract.hbs
  92. 16
      docs/templates/properties.js
  93. 2
      fv-requirements.txt
  94. 2
      package.json
  95. 2
      scripts/generate/templates/Arrays.js
  96. 86
      test/account/utils/draft-ERC4337Utils.test.js
  97. 59
      test/account/utils/draft-ERC7579Utils.test.js
  98. 36
      test/governance/Governor.test.js
  99. 2
      test/governance/extensions/GovernorCountingOverridable.test.js
  100. 132
      test/governance/extensions/GovernorProposalGuardian.test.js
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,5 +0,0 @@
---
'openzeppelin-solidity': minor
---
`Strings`: Add `parseUint`, `parseInt`, `parseHexUint` and `parseAddress` to parse strings into numbers and addresses. Also provide variants of these functions that parse substrings, and `tryXxx` variants that do not revert on invalid input.

@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---
`ERC2771Forwarder`: Expose the `_isTrustedByTarget` internal function to check whether a target trusts the forwarder.

@ -1,5 +0,0 @@
---
"openzeppelin-solidity": minor
---
`Clones`: Add `cloneWithImmutableArgs` and `cloneDeterministicWithImmutableArgs` variants that create clones with per-instance immutable arguments. The immutable arguments can be retrieved using `fetchCloneArgs`. The corresponding `predictDeterministicWithImmutableArgs` function is also included.

@ -0,0 +1,5 @@
---
"openzeppelin-solidity": minor
---
`Calldata`: Library with `emptyBytes` and `emptyString` functions to generate empty `bytes` and `string` calldata types.

@ -1,5 +0,0 @@
---
'openzeppelin-solidity': patch
---
`VotesExtended`: Create an extension of `Votes` which checkpoints balances and delegates.

@ -0,0 +1,5 @@
---
"openzeppelin-solidity": minor
---
`Pausable`: Stop explicitly setting `paused` to `false` during construction.

@ -1,5 +0,0 @@
---
'openzeppelin-solidity': minor
---
`CAIP2` and `CAIP10`: Add libraries for formatting and parsing CAIP-2 and CAIP-10 identifiers.

@ -1,5 +0,0 @@
---
'openzeppelin-solidity': minor
---
`Packing`: Add variants for packing `bytes10` and `bytes22`

@ -1,5 +0,0 @@
---
'openzeppelin-solidity': minor
---
`NoncesKeyed`: Add a variant of `Nonces` that implements the ERC-4337 entrypoint nonce system.

@ -1,5 +0,0 @@
---
'openzeppelin-solidity': patch
---
`GovernorCountingOverridable`: Add a governor counting module that enables token holders to override the vote of their delegate.

@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---
`GovernorProposalGuardian`: Add a governance extension that defines a proposal guardian who can cancel proposals at any stage in their lifecycle.

@ -1,5 +0,0 @@
---
'openzeppelin-solidity': minor
---
`Bytes`: Add a library of common operations that operate on `bytes` objects.

@ -1,5 +0,0 @@
---
'openzeppelin-solidity': patch
---
Update some pragma directives to ensure that all file requirements match that of the files they import.

@ -1,5 +0,0 @@
---
'openzeppelin-solidity': minor
---
`ERC7579Utils`: Add a reusable library to interact with ERC-7579 modular accounts

@ -1,5 +0,0 @@
---
'openzeppelin-solidity': minor
---
`ERC1363Utils`: Add helper similar to the existing `ERC721Utils` and `ERC1155Utils`

@ -1,5 +0,0 @@
---
'openzeppelin-solidity': minor
---
`ERC4337Utils`: Add a reusable library to manipulate user operations and interact with ERC-4337 contracts

@ -44,7 +44,8 @@ runs:
shell: bash shell: bash
- name: Save report - name: Save report
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: gasreport name: gasreport
overwrite: true
path: ${{ inputs.out_report }} path: ${{ inputs.out_report }}

@ -50,7 +50,8 @@ runs:
shell: bash shell: bash
- name: Save artifacts - name: Save artifacts
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: layout name: layout
overwrite: true
path: ${{ inputs.out_layout }} path: ${{ inputs.out_layout }}

1
.gitignore vendored

@ -37,6 +37,7 @@ npm-debug.log
# docs artifacts # docs artifacts
docs/modules/api docs/modules/api
build/site
# only used to package @openzeppelin/contracts # only used to package @openzeppelin/contracts
contracts/build/ contracts/build/

@ -1,12 +1,62 @@
# Changelog # Changelog
### Breaking Changes
- Replace `GovernorCountingOverridable.VoteReceipt` struct parameter member names `hasOverriden` and `overridenWeight` for `hasOverridden` and `overriddenWeight` respectively.
#### Custom error changes
- Replace `GovernorAlreadyOverridenVote` with `GovernorAlreadyOverriddenVote`.
## 5.2.0 (2025-01-08)
### Breaking Changes
#### Custom error changes
This version comes with changes to the custom error identifiers. Contracts previously depending on the following errors should be replaced accordingly:
- Replace `Errors.FailedCall` with a bubbled-up revert reason in `Address.sendValue`.
### Changes by category
#### General
- Update some pragma directives to ensure that all file requirements match that of the files they import. ([#5273](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5273))
#### Account
- `ERC4337Utils`: Add a reusable library to manipulate user operations and interact with ERC-4337 contracts ([#5274](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5274))
- `ERC7579Utils`: Add a reusable library to interact with ERC-7579 modular accounts ([#5274](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5274))
#### Governance
- `GovernorCountingOverridable`: Add a governor counting module that enables token holders to override the vote of their delegate. ([#5192](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5192))
- `VotesExtended`: Create an extension of `Votes` which checkpoints balances and delegates. ([#5192](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5192))
### Proxy
- `Clones`: Add `cloneWithImmutableArgs` and `cloneDeterministicWithImmutableArgs` variants that create clones with per-instance immutable arguments. The immutable arguments can be retrieved using `fetchCloneArgs`. The corresponding `predictDeterministicWithImmutableArgs` function is also included. ([#5109](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5109))
### Tokens
- `ERC1363Utils`: Add helper similar to the existing `ERC721Utils` and `ERC1155Utils` ([#5133](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5133))
### Utils
- `Address`: bubble up revert data on `sendValue` failed call ([#5418](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5418))
- `Bytes`: Add a library of common operations that operate on `bytes` objects. ([#5252](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5252))
- `CAIP2` and `CAIP10`: Add libraries for formatting and parsing CAIP-2 and CAIP-10 identifiers. ([#5252](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5252))
- `NoncesKeyed`: Add a variant of `Nonces` that implements the ERC-4337 entrypoint nonce system. ([#5272](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5272))
- `Packing`: Add variants for packing `bytes10` and `bytes22` ([#5274](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5274))
- `Strings`: Add `parseUint`, `parseInt`, `parseHexUint` and `parseAddress` to parse strings into numbers and addresses. Also provide variants of these functions that parse substrings, and `tryXxx` variants that do not revert on invalid input. ([#5166](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5166))
## 5.1.0 (2024-10-17) ## 5.1.0 (2024-10-17)
### Breaking changes ### Breaking changes
- `ERC1967Utils`: Removed duplicate declaration of the `Upgraded`, `AdminChanged` and `BeaconUpgraded` events. These events are still available through the `IERC1967` interface located under the `contracts/interfaces/` directory. Minimum pragma version is now 0.8.21. - `ERC1967Utils`: Removed duplicate declaration of the `Upgraded`, `AdminChanged` and `BeaconUpgraded` events. These events are still available through the `IERC1967` interface located under the `contracts/interfaces/` directory. Minimum pragma version is now 0.8.21.
- `Governor`, `GovernorCountingSimple`: The `_countVote` virtual function now returns an `uint256` with the total votes casted. This change allows for more flexibility for partial and fractional voting. Upgrading users may get a compilation error that can be fixed by adding a return statement to the `_countVote` function. - `Governor`, `GovernorCountingSimple`: The `_countVote` virtual function now returns an `uint256` with the total votes cast. This change allows for more flexibility for partial and fractional voting. Upgrading users may get a compilation error that can be fixed by adding a return statement to the `_countVote` function.
#### Custom error changes #### Custom error changes

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2016-2024 Zeppelin Group Ltd Copyright (c) 2016-2025 Zeppelin Group Ltd
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the

@ -70,7 +70,7 @@ The guides in the [documentation site](https://docs.openzeppelin.com/contracts)
* [Tokens](https://docs.openzeppelin.com/contracts/tokens): create tradeable assets or collectives, and distribute them via [Crowdsales](https://docs.openzeppelin.com/contracts/crowdsales). * [Tokens](https://docs.openzeppelin.com/contracts/tokens): create tradeable assets or collectives, and distribute them via [Crowdsales](https://docs.openzeppelin.com/contracts/crowdsales).
* [Utilities](https://docs.openzeppelin.com/contracts/utilities): generic useful tools including non-overflowing math, signature verification, and trustless paying systems. * [Utilities](https://docs.openzeppelin.com/contracts/utilities): generic useful tools including non-overflowing math, signature verification, and trustless paying systems.
The [full API](https://docs.openzeppelin.com/contracts/api/token/ERC20) is also thoroughly documented, and serves as a great reference when developing your smart contract application. You can also ask for help or follow Contracts's development in the [community forum](https://forum.openzeppelin.com). The [full API](https://docs.openzeppelin.com/contracts/api/token/ERC20) is also thoroughly documented, and serves as a great reference when developing your smart contract application. You can also ask for help or follow Contracts' development in the [community forum](https://forum.openzeppelin.com).
Finally, you may want to take a look at the [guides on our blog](https://blog.openzeppelin.com/), which cover several common use cases and good practices. The following articles provide great background reading, though please note that some of the referenced tools have changed, as the tooling in the ecosystem continues to rapidly evolve. Finally, you may want to take a look at the [guides on our blog](https://blog.openzeppelin.com/), which cover several common use cases and good practices. The following articles provide great background reading, though please note that some of the referenced tools have changed, as the tooling in the ecosystem continues to rapidly evolve.

@ -4,7 +4,7 @@ OpenZeppelin Contracts uses a fully automated release process that takes care of
## Changesets ## Changesets
[Changesets](https://github.com/changesets/changesets/) is used as part of our release process for `CHANGELOG.md` management. Each change that is relevant for the codebase is expected to include a changeset. [Changesets](https://github.com/changesets/changesets/) are used as part of our release process for `CHANGELOG.md` management. Each change that is relevant for the codebase is expected to include a changeset.
## Branching model ## Branching model

@ -40,4 +40,4 @@ Note as well that the Solidity language itself only guarantees security updates
## Legal ## Legal
Smart contracts are a nascent technology and carry a high level of technical risk and uncertainty. OpenZeppelin Contracts is made available under the MIT License, which disclaims all warranties in relation to the project and which limits the liability of those that contribute and maintain the project, including OpenZeppelin. Your use of the project is also governed by the terms found at www.openzeppelin.com/tos (the "Terms"). As set out in the Terms, you are solely responsible for any use of OpenZeppelin Contracts and you assume all risks associated with any such use. This Security Policy in no way evidences or represents an on-going duty by any contributor, including OpenZeppelin, to correct any flaws or alert you to all or any of the potential risks of utilizing the project. Smart contracts are a nascent technology and carry a high level of technical risk and uncertainty. OpenZeppelin Contracts is made available under the MIT License, which disclaims all warranties in relation to the project and which limits the liability of those that contribute and maintain the project, including OpenZeppelin. Your use of the project is also governed by the terms found at www.openzeppelin.com/tos (the "Terms"). As set out in the Terms, you are solely responsible for any use of OpenZeppelin Contracts and you assume all risks associated with any such use. This Security Policy in no way evidences or represents an ongoing duty by any contributor, including OpenZeppelin, to correct any flaws or alert you to all or any of the potential risks of utilizing the project.

@ -20,7 +20,7 @@ The git commit hash we evaluated is:
# Disclaimer # Disclaimer
The audit makes no statements or warrantees about utility of the code, safety of the code, suitability of the business model, regulatory regime for the business model, or any other statements about fitness of the contracts to purpose, or their bugfree status. The audit documentation is for discussion purposes only. The audit makes no statements or warrantees about utility of the code, safety of the code, suitability of the business model, regulatory regime for the business model, or any other statements about fitness of the contracts to purpose, or their bug free status. The audit documentation is for discussion purposes only.
# Executive Summary # Executive Summary

@ -2,14 +2,14 @@
These instructions detail the process for running Certora Verification Tool on OpenZeppelin Contracts. These instructions detail the process for running Certora Verification Tool on OpenZeppelin Contracts.
Documentation for CVT and the specification language are available [here](https://certora.atlassian.net/wiki/spaces/CPD/overview). Documentation for CVT and the specification language is available [here](https://certora.atlassian.net/wiki/spaces/CPD/overview).
## Prerequisites ## Prerequisites
Follow the [Certora installation guide](https://docs.certora.com/en/latest/docs/user-guide/getting-started/install.html) in order to get the Certora Prover Package and the `solc` executable folder in your path. Follow the [Certora installation guide](https://docs.certora.com/en/latest/docs/user-guide/getting-started/install.html) in order to get the Certora Prover Package and the `solc` executable folder in your path.
> **Note** > **Note**
> An API Key is required for local testing. Although the prover will run on a Github Actions' CI environment on selected Pull Requests. > An API Key is required for local testing. Although the prover will run on a GitHub Actions' CI environment on selected Pull Requests.
## Running the verification ## Running the verification

@ -160,7 +160,7 @@ function writeEntry(spec, contract, success, url) {
formatRow( formatRow(
spec, spec,
contract, contract,
success ? ':x:' : ':heavy_check_mark:', success ? ':heavy_check_mark:' : ':x:',
url ? `[link](${url?.replace('/output/', '/jobStatus/')})` : 'error', url ? `[link](${url?.replace('/output/', '/jobStatus/')})` : 'error',
url ? `[link](${url})` : 'error', url ? `[link](${url})` : 'error',
), ),

@ -176,7 +176,7 @@ rule renounceRoleEffect(env e, bytes32 role) {
/* /*
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule: defaultAdmin is only affected by accepting an admin transfer or renoucing │ Rule: defaultAdmin is only affected by accepting an admin transfer or renouncing │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
*/ */
rule noDefaultAdminChange(env e, method f, calldataarg args) { rule noDefaultAdminChange(env e, method f, calldataarg args) {
@ -188,7 +188,7 @@ rule noDefaultAdminChange(env e, method f, calldataarg args) {
f.selector == sig:acceptDefaultAdminTransfer().selector || f.selector == sig:acceptDefaultAdminTransfer().selector ||
f.selector == sig:renounceRole(bytes32,address).selector f.selector == sig:renounceRole(bytes32,address).selector
), ),
"default admin is only affected by accepting an admin transfer or renoucing"; "default admin is only affected by accepting an admin transfer or renouncing";
} }
/* /*

@ -18,7 +18,7 @@ methods {
*/ */
ghost mapping(address => mathint) trackedMintAmount; ghost mapping(address => mathint) trackedMintAmount;
ghost mapping(address => mathint) trackedBurnAmount; ghost mapping(address => mathint) trackedBurnAmount;
ghost mapping(address => mapping(address => mathint)) trackedTransferedAmount; ghost mapping(address => mapping(address => mathint)) trackedTransferredAmount;
function specUpdate(address from, address to, uint256 amount) { function specUpdate(address from, address to, uint256 amount) {
if (from == 0 && to == 0) { assert(false); } // defensive if (from == 0 && to == 0) { assert(false); } // defensive
@ -28,7 +28,7 @@ function specUpdate(address from, address to, uint256 amount) {
} else if (to == 0) { } else if (to == 0) {
trackedBurnAmount[from] = amount; trackedBurnAmount[from] = amount;
} else { } else {
trackedTransferedAmount[from][to] = amount; trackedTransferredAmount[from][to] = amount;
} }
} }
@ -51,5 +51,5 @@ rule checkMintAndBurn(env e) {
assert trackedMintAmount[receiver] == to_mathint(amount); assert trackedMintAmount[receiver] == to_mathint(amount);
assert trackedBurnAmount[receiver] == amount + to_mathint(recipient == 0 ? fees : 0); assert trackedBurnAmount[receiver] == amount + to_mathint(recipient == 0 ? fees : 0);
assert (fees > 0 && recipient != 0) => trackedTransferedAmount[receiver][recipient] == to_mathint(fees); assert (fees > 0 && recipient != 0) => trackedTransferredAmount[receiver][recipient] == to_mathint(fees);
} }

@ -191,7 +191,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
} }
/** /**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked. * @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
* *
* Internal function without access restriction. * Internal function without access restriction.
* *

@ -1,9 +1,11 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (account/utils/draft-ERC4337Utils.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
import {PackedUserOperation} from "../../interfaces/draft-IERC4337.sol"; import {PackedUserOperation} from "../../interfaces/draft-IERC4337.sol";
import {Math} from "../../utils/math/Math.sol"; import {Math} from "../../utils/math/Math.sol";
import {Calldata} from "../../utils/Calldata.sol";
import {Packing} from "../../utils/Packing.sol"; import {Packing} from "../../utils/Packing.sol";
/** /**
@ -106,7 +108,7 @@ library ERC4337Utils {
/// @dev Returns `factoryData` from the {PackedUserOperation}, or empty bytes if the initCode is empty or not properly formatted. /// @dev Returns `factoryData` from the {PackedUserOperation}, or empty bytes if the initCode is empty or not properly formatted.
function factoryData(PackedUserOperation calldata self) internal pure returns (bytes calldata) { function factoryData(PackedUserOperation calldata self) internal pure returns (bytes calldata) {
return self.initCode.length < 20 ? _emptyCalldataBytes() : self.initCode[20:]; return self.initCode.length < 20 ? Calldata.emptyBytes() : self.initCode[20:];
} }
/// @dev Returns `verificationGasLimit` from the {PackedUserOperation}. /// @dev Returns `verificationGasLimit` from the {PackedUserOperation}.
@ -156,14 +158,6 @@ library ERC4337Utils {
/// @dev Returns the fourth section of `paymasterAndData` from the {PackedUserOperation}. /// @dev Returns the fourth section of `paymasterAndData` from the {PackedUserOperation}.
function paymasterData(PackedUserOperation calldata self) internal pure returns (bytes calldata) { function paymasterData(PackedUserOperation calldata self) internal pure returns (bytes calldata) {
return self.paymasterAndData.length < 52 ? _emptyCalldataBytes() : self.paymasterAndData[52:]; return self.paymasterAndData.length < 52 ? Calldata.emptyBytes() : self.paymasterAndData[52:];
}
// slither-disable-next-line write-after-write
function _emptyCalldataBytes() private pure returns (bytes calldata result) {
assembly ("memory-safe") {
result.offset := 0
result.length := 0
}
} }
} }

@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (account/utils/draft-ERC7579Utils.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (finance/VestingWallet.sol) // OpenZeppelin Contracts (last updated v5.2.0) (finance/VestingWallet.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol"; import {IERC20} from "../token/ERC20/IERC20.sol";
@ -16,7 +16,7 @@ import {Ownable} from "../access/Ownable.sol";
* Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly) * Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly)
* be immediately releasable. * be immediately releasable.
* *
* By setting the duration to 0, one can configure this contract to behave like an asset timelock that hold tokens for * By setting the duration to 0, one can configure this contract to behave like an asset timelock that holds tokens for
* a beneficiary until a specified time. * a beneficiary until a specified time.
* *
* NOTE: Since the wallet is {Ownable}, and ownership can be transferred, it is possible to sell unvested tokens. * NOTE: Since the wallet is {Ownable}, and ownership can be transferred, it is possible to sell unvested tokens.

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (governance/Governor.sol) // OpenZeppelin Contracts (last updated v5.2.0) (governance/Governor.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
@ -455,7 +455,7 @@ abstract contract Governor is Context, ERC165, EIP712, Nonces, IGovernor, IERC72
* performed (for example adding a vault/timelock). * performed (for example adding a vault/timelock).
* *
* NOTE: Calling this function directly will NOT check the current state of the proposal, set the executed flag to * NOTE: Calling this function directly will NOT check the current state of the proposal, set the executed flag to
* true or emit the `ProposalExecuted` event. Executing a proposal should be done using {execute} or {_execute}. * true or emit the `ProposalExecuted` event. Executing a proposal should be done using {execute}.
*/ */
function _executeOperations( function _executeOperations(
uint256 /* proposalId */, uint256 /* proposalId */,
@ -484,11 +484,8 @@ abstract contract Governor is Context, ERC165, EIP712, Nonces, IGovernor, IERC72
// changes it. The `getProposalId` duplication has a cost that is limited, and that we accept. // changes it. The `getProposalId` duplication has a cost that is limited, and that we accept.
uint256 proposalId = getProposalId(targets, values, calldatas, descriptionHash); uint256 proposalId = getProposalId(targets, values, calldatas, descriptionHash);
// public cancel restrictions (on top of existing _cancel restrictions). address caller = _msgSender();
_validateStateBitmap(proposalId, _encodeStateBitmap(ProposalState.Pending)); if (!_validateCancel(proposalId, caller)) revert GovernorUnableToCancel(proposalId, caller);
if (_msgSender() != proposalProposer(proposalId)) {
revert GovernorOnlyProposer(_msgSender());
}
return _cancel(targets, values, calldatas, descriptionHash); return _cancel(targets, values, calldatas, descriptionHash);
} }
@ -805,6 +802,15 @@ abstract contract Governor is Context, ERC165, EIP712, Nonces, IGovernor, IERC72
} }
} }
/**
* @dev Check if the `caller` can cancel the proposal with the given `proposalId`.
*
* The default implementation allows the proposal proposer to cancel the proposal during the pending state.
*/
function _validateCancel(uint256 proposalId, address caller) internal view virtual returns (bool) {
return (state(proposalId) == ProposalState.Pending) && caller == proposalProposer(proposalId);
}
/** /**
* @inheritdoc IERC6372 * @inheritdoc IERC6372
*/ */

@ -39,11 +39,6 @@ interface IGovernor is IERC165, IERC6372 {
*/ */
error GovernorDisabledDeposit(); error GovernorDisabledDeposit();
/**
* @dev The `account` is not a proposer.
*/
error GovernorOnlyProposer(address account);
/** /**
* @dev The `account` is not the governance executor. * @dev The `account` is not the governance executor.
*/ */
@ -112,6 +107,11 @@ interface IGovernor is IERC165, IERC6372 {
*/ */
error GovernorInvalidSignature(address voter); error GovernorInvalidSignature(address voter);
/**
* @dev The given `account` is unable to cancel the proposal with given `proposalId`.
*/
error GovernorUnableToCancel(uint256 proposalId, address account);
/** /**
* @dev Emitted when a proposal is created. * @dev Emitted when a proposal is created.
*/ */

@ -20,7 +20,7 @@ For a written walkthrough, check out our guide on xref:ROOT:governance.adoc[How
Votes modules determine the source of voting power, and sometimes quorum number. Votes modules determine the source of voting power, and sometimes quorum number.
* {GovernorVotes}: Extracts voting weight from an {ERC20Votes}, or since v4.5 an {ERC721Votes} token. * {GovernorVotes}: Extracts voting weight from an {IVotes} contract.
* {GovernorVotesQuorumFraction}: Combines with `GovernorVotes` to set the quorum as a fraction of the total token supply. * {GovernorVotesQuorumFraction}: Combines with `GovernorVotes` to set the quorum as a fraction of the total token supply.
@ -30,7 +30,7 @@ Counting modules determine valid voting options.
* {GovernorCountingFractional}: A more modular voting system that allows a user to vote with only part of its voting power, and to split that weight arbitrarily between the 3 different options (Against, For and Abstain). * {GovernorCountingFractional}: A more modular voting system that allows a user to vote with only part of its voting power, and to split that weight arbitrarily between the 3 different options (Against, For and Abstain).
* {GovernorCountingOverridable}: An extended version of `GovernorCountingSimple` which allows delegatees to override their delegates while the vote is live. * {GovernorCountingOverridable}: An extended version of `GovernorCountingSimple` which allows delegatees to override their delegates while the vote is live. Must be used in conjunction with {VotesExtended}.
Timelock extensions add a delay for governance decisions to be executed. The workflow is extended to require a `queue` step before execution. With these modules, proposals are executed by the external timelock contract, thus it is the timelock that has to hold the assets that are being governed. Timelock extensions add a delay for governance decisions to be executed. The workflow is extended to require a `queue` step before execution. With these modules, proposals are executed by the external timelock contract, thus it is the timelock that has to hold the assets that are being governed.
@ -48,6 +48,8 @@ Other extensions can customize the behavior or interface in multiple ways.
* {GovernorPreventLateQuorum}: Ensures there is a minimum voting period after quorum is reached as a security protection against large voters. * {GovernorPreventLateQuorum}: Ensures there is a minimum voting period after quorum is reached as a security protection against large voters.
* {GovernorProposalGuardian}: Adds a proposal guardian that can cancel proposals at any stage in their lifecycle--this permission is passed on to the proposers if the guardian is not set.
In addition to modules and extensions, the core contract requires a few virtual functions to be implemented to your particular specifications: In addition to modules and extensions, the core contract requires a few virtual functions to be implemented to your particular specifications:
* <<Governor-votingDelay-,`votingDelay()`>>: Delay (in ERC-6372 clock) since the proposal is submitted until voting power is fixed and voting starts. This can be used to enforce a delay after a proposal is published for users to buy tokens, or delegate their votes. * <<Governor-votingDelay-,`votingDelay()`>>: Delay (in ERC-6372 clock) since the proposal is submitted until voting power is fixed and voting starts. This can be used to enforce a delay after a proposal is published for users to buy tokens, or delegate their votes.
@ -68,7 +70,7 @@ NOTE: Functions of the `Governor` contract do not include access control. If you
{{GovernorCountingFractional}} {{GovernorCountingFractional}}
{{GovernorCountingOverride}} {{GovernorCountingOverridable}}
{{GovernorVotes}} {{GovernorVotes}}
@ -88,6 +90,8 @@ NOTE: Functions of the `Governor` contract do not include access control. If you
{{GovernorStorage}} {{GovernorStorage}}
{{GovernorProposalGuardian}}
== Utils == Utils
{{Votes}} {{Votes}}
@ -103,7 +107,7 @@ In a governance system, the {TimelockController} contract is in charge of introd
[[timelock-terminology]] [[timelock-terminology]]
==== Terminology ==== Terminology
* *Operation:* A transaction (or a set of transactions) that is the subject of the timelock. It has to be scheduled by a proposer and executed by an executor. The timelock enforces a minimum delay between the proposition and the execution (see xref:access-control.adoc#operation_lifecycle[operation lifecycle]). If the operation contains multiple transactions (batch mode), they are executed atomically. Operations are identified by the hash of their content. * *Operation:* A transaction (or a set of transactions) that is the subject of the timelock. It has to be scheduled by a proposer and executed by an executor. The timelock enforces a minimum delay between the proposition and the execution. If the operation contains multiple transactions (batch mode), they are executed atomically. Operations are identified by the hash of their content.
* *Operation status:* * *Operation status:*
** *Unset:* An operation that is not part of the timelock mechanism. ** *Unset:* An operation that is not part of the timelock mechanism.
** *Waiting:* An operation that has been scheduled, before the timer expires. ** *Waiting:* An operation that has been scheduled, before the timer expires.

@ -125,9 +125,9 @@ abstract contract GovernorCountingFractional is Governor {
* *
* `abi.encodePacked(uint128(againstVotes), uint128(forVotes), uint128(abstainVotes))` * `abi.encodePacked(uint128(againstVotes), uint128(forVotes), uint128(abstainVotes))`
* *
* NOTE: Consider that fractional voting restricts the number of casted vote (in each category) to 128 bits. * NOTE: Consider that fractional voting restricts the number of casted votes (in each category) to 128 bits.
* Depending on how many decimals the underlying token has, a single voter may require to split their vote into * Depending on how many decimals the underlying token has, a single voter may require to split their vote into
* multiple vote operations. For precision higher than ~30 decimals, large token holders may require an * multiple vote operations. For precision higher than ~30 decimals, large token holders may require a
* potentially large number of calls to cast all their votes. The voter has the possibility to cast all the * potentially large number of calls to cast all their votes. The voter has the possibility to cast all the
* remaining votes in a single operation using the traditional "bravo" vote. * remaining votes in a single operation using the traditional "bravo" vote.
*/ */

@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (governance/extensions/GovernorCountingOverridable.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
@ -26,8 +27,8 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
struct VoteReceipt { struct VoteReceipt {
uint8 casted; // 0 if vote was not casted. Otherwise: support + 1 uint8 casted; // 0 if vote was not casted. Otherwise: support + 1
bool hasOverriden; bool hasOverridden;
uint208 overridenWeight; uint208 overriddenWeight;
} }
struct ProposalVote { struct ProposalVote {
@ -41,7 +42,7 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
/// @dev A delegated vote on `proposalId` was overridden by `weight` /// @dev A delegated vote on `proposalId` was overridden by `weight`
event OverrideVoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason); event OverrideVoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);
error GovernorAlreadyOverridenVote(address account); error GovernorAlreadyOverriddenVote(address account);
mapping(uint256 proposalId => ProposalVote) private _proposalVotes; mapping(uint256 proposalId => ProposalVote) private _proposalVotes;
@ -69,7 +70,7 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
* @dev Check if an `account` has overridden their delegate for a proposal. * @dev Check if an `account` has overridden their delegate for a proposal.
*/ */
function hasVotedOverride(uint256 proposalId, address account) public view virtual returns (bool) { function hasVotedOverride(uint256 proposalId, address account) public view virtual returns (bool) {
return _proposalVotes[proposalId].voteReceipt[account].hasOverriden; return _proposalVotes[proposalId].voteReceipt[account].hasOverridden;
} }
/** /**
@ -121,7 +122,7 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
revert GovernorAlreadyCastVote(account); revert GovernorAlreadyCastVote(account);
} }
totalWeight -= proposalVote.voteReceipt[account].overridenWeight; totalWeight -= proposalVote.voteReceipt[account].overriddenWeight;
proposalVote.votes[support] += totalWeight; proposalVote.votes[support] += totalWeight;
proposalVote.voteReceipt[account].casted = support + 1; proposalVote.voteReceipt[account].casted = support + 1;
@ -140,26 +141,26 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
revert GovernorInvalidVoteType(); revert GovernorInvalidVoteType();
} }
if (proposalVote.voteReceipt[account].hasOverriden) { if (proposalVote.voteReceipt[account].hasOverridden) {
revert GovernorAlreadyOverridenVote(account); revert GovernorAlreadyOverriddenVote(account);
} }
uint256 snapshot = proposalSnapshot(proposalId); uint256 snapshot = proposalSnapshot(proposalId);
uint256 overridenWeight = VotesExtended(address(token())).getPastBalanceOf(account, snapshot); uint256 overriddenWeight = VotesExtended(address(token())).getPastBalanceOf(account, snapshot);
address delegate = VotesExtended(address(token())).getPastDelegate(account, snapshot); address delegate = VotesExtended(address(token())).getPastDelegate(account, snapshot);
uint8 delegateCasted = proposalVote.voteReceipt[delegate].casted; uint8 delegateCasted = proposalVote.voteReceipt[delegate].casted;
proposalVote.voteReceipt[account].hasOverriden = true; proposalVote.voteReceipt[account].hasOverridden = true;
proposalVote.votes[support] += overridenWeight; proposalVote.votes[support] += overriddenWeight;
if (delegateCasted == 0) { if (delegateCasted == 0) {
proposalVote.voteReceipt[delegate].overridenWeight += SafeCast.toUint208(overridenWeight); proposalVote.voteReceipt[delegate].overriddenWeight += SafeCast.toUint208(overriddenWeight);
} else { } else {
uint8 delegateSupport = delegateCasted - 1; uint8 delegateSupport = delegateCasted - 1;
proposalVote.votes[delegateSupport] -= overridenWeight; proposalVote.votes[delegateSupport] -= overriddenWeight;
emit VoteReduced(delegate, proposalId, delegateSupport, overridenWeight); emit VoteReduced(delegate, proposalId, delegateSupport, overriddenWeight);
} }
return overridenWeight; return overriddenWeight;
} }
/// @dev Variant of {Governor-_castVote} that deals with vote overrides. Returns the overridden weight. /// @dev Variant of {Governor-_castVote} that deals with vote overrides. Returns the overridden weight.
@ -171,13 +172,13 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
) internal virtual returns (uint256) { ) internal virtual returns (uint256) {
_validateStateBitmap(proposalId, _encodeStateBitmap(ProposalState.Active)); _validateStateBitmap(proposalId, _encodeStateBitmap(ProposalState.Active));
uint256 overridenWeight = _countOverride(proposalId, account, support); uint256 overriddenWeight = _countOverride(proposalId, account, support);
emit OverrideVoteCast(account, proposalId, support, overridenWeight, reason); emit OverrideVoteCast(account, proposalId, support, overriddenWeight, reason);
_tallyUpdated(proposalId); _tallyUpdated(proposalId);
return overridenWeight; return overriddenWeight;
} }
/// @dev Public function for casting an override vote. Returns the overridden weight. /// @dev Public function for casting an override vote. Returns the overridden weight.

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (governance/extensions/GovernorPreventLateQuorum.sol) // OpenZeppelin Contracts (last updated v5.2.0) (governance/extensions/GovernorPreventLateQuorum.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;

@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Governor} from "../Governor.sol";
/**
* @dev Extension of {Governor} which adds a proposal guardian that can cancel proposals at any stage in the proposal's lifecycle.
*
* NOTE: if the proposal guardian is not configured, then proposers take this role for their proposals.
*/
abstract contract GovernorProposalGuardian is Governor {
address private _proposalGuardian;
event ProposalGuardianSet(address oldProposalGuardian, address newProposalGuardian);
/**
* @dev Getter that returns the address of the proposal guardian.
*/
function proposalGuardian() public view virtual returns (address) {
return _proposalGuardian;
}
/**
* @dev Update the proposal guardian's address. This operation can only be performed through a governance proposal.
*
* Emits a {ProposalGuardianSet} event.
*/
function setProposalGuardian(address newProposalGuardian) public virtual onlyGovernance {
_setProposalGuardian(newProposalGuardian);
}
/**
* @dev Internal setter for the proposal guardian.
*
* Emits a {ProposalGuardianSet} event.
*/
function _setProposalGuardian(address newProposalGuardian) internal virtual {
emit ProposalGuardianSet(_proposalGuardian, newProposalGuardian);
_proposalGuardian = newProposalGuardian;
}
/**
* @dev Override {Governor-_validateCancel} to implement the extended cancellation logic.
*
* * The {proposalGuardian} can cancel any proposal at any point.
* * If no proposal guardian is set, the {IGovernor-proposalProposer} can cancel their proposals at any point.
* * In any case, permissions defined in {Governor-_validateCancel} (or another override) remains valid.
*/
function _validateCancel(uint256 proposalId, address caller) internal view virtual override returns (bool) {
address guardian = proposalGuardian();
return
guardian == caller ||
(guardian == address(0) && caller == proposalProposer(proposalId)) ||
super._validateCancel(proposalId, caller);
}
}

@ -50,7 +50,7 @@ abstract contract GovernorStorage is Governor {
} }
/** /**
* @dev Version of {IGovernorTimelock-queue} with only `proposalId` as an argument. * @dev Version of {IGovernor-queue} with only `proposalId` as an argument.
*/ */
function queue(uint256 proposalId) public virtual { function queue(uint256 proposalId) public virtual {
// here, using storage is more efficient than memory // here, using storage is more efficient than memory

@ -277,7 +277,7 @@ abstract contract GovernorTimelockAccess is Governor {
} }
/** /**
* @dev See {IGovernor-_cancel} * @dev See {Governor-_cancel}
*/ */
function _cancel( function _cancel(
address[] memory targets, address[] memory targets,

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (governance/utils/Votes.sol) // OpenZeppelin Contracts (last updated v5.2.0) (governance/utils/Votes.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
import {IERC5805} from "../../interfaces/IERC5805.sol"; import {IERC5805} from "../../interfaces/IERC5805.sol";

@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (governance/utils/VotesExtended.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
import {Checkpoints} from "../../utils/structs/Checkpoints.sol"; import {Checkpoints} from "../../utils/structs/Checkpoints.sol";

@ -11,7 +11,7 @@ interface IERC1271 {
/** /**
* @dev Should return whether the signature provided is valid for the provided data * @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed * @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data * @param signature Signature byte array associated with `hash`
*/ */
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
} }

@ -74,10 +74,12 @@ are useful to interact with third party contracts that implement them.
{{IERC4626}} {{IERC4626}}
{{IERC5313}} {{IERC4906}}
{{IERC5267}} {{IERC5267}}
{{IERC5313}}
{{IERC5805}} {{IERC5805}}
{{IERC6372}} {{IERC6372}}

@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (interfaces/draft-IERC4337.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;

@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (interfaces/draft-IERC7579.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
import {PackedUserOperation} from "./draft-IERC4337.sol"; import {PackedUserOperation} from "./draft-IERC4337.sol";
@ -120,7 +121,7 @@ interface IERC7579Execution {
* MUST ensure adequate authorization control: e.g. onlyEntryPointOrSelf if used with ERC-4337 * MUST ensure adequate authorization control: e.g. onlyEntryPointOrSelf if used with ERC-4337
* If a mode is requested that is not supported by the Account, it MUST revert * If a mode is requested that is not supported by the Account, it MUST revert
*/ */
function execute(bytes32 mode, bytes calldata executionCalldata) external; function execute(bytes32 mode, bytes calldata executionCalldata) external payable;
/** /**
* @dev Executes a transaction on behalf of the account. * @dev Executes a transaction on behalf of the account.
@ -135,7 +136,7 @@ interface IERC7579Execution {
function executeFromExecutor( function executeFromExecutor(
bytes32 mode, bytes32 mode,
bytes calldata executionCalldata bytes calldata executionCalldata
) external returns (bytes[] memory returnData); ) external payable returns (bytes[] memory returnData);
} }
/** /**

@ -302,8 +302,11 @@ contract ERC2771Forwarder is EIP712, Nonces {
* *
* This function performs a static call to the target contract calling the * This function performs a static call to the target contract calling the
* {ERC2771Context-isTrustedForwarder} function. * {ERC2771Context-isTrustedForwarder} function.
*
* NOTE: Consider the execution of this forwarder is permissionless. Without this check, anyone may transfer assets
* that are owned by, or are approved to this forwarder.
*/ */
function _isTrustedByTarget(address target) private view returns (bool) { function _isTrustedByTarget(address target) internal view virtual returns (bool) {
bytes memory encodedParams = abi.encodeCall(ERC2771Context.isTrustedForwarder, (address(this))); bytes memory encodedParams = abi.encodeCall(ERC2771Context.isTrustedForwarder, (address(this)));
bool success; bool success;

@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Governor} from "../../governance/Governor.sol";
import {GovernorSettings} from "../../governance/extensions/GovernorSettings.sol";
import {GovernorCountingSimple} from "../../governance/extensions/GovernorCountingSimple.sol";
import {GovernorVotesQuorumFraction} from "../../governance/extensions/GovernorVotesQuorumFraction.sol";
import {GovernorProposalGuardian} from "../../governance/extensions/GovernorProposalGuardian.sol";
abstract contract GovernorProposalGuardianMock is
GovernorSettings,
GovernorVotesQuorumFraction,
GovernorCountingSimple,
GovernorProposalGuardian
{
function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) {
return super.proposalThreshold();
}
function _validateCancel(
uint256 proposalId,
address caller
) internal view override(Governor, GovernorProposalGuardian) returns (bool) {
return super._validateCancel(proposalId, caller);
}
}

@ -1,7 +1,7 @@
{ {
"name": "@openzeppelin/contracts", "name": "@openzeppelin/contracts",
"description": "Secure Smart Contract library for Solidity", "description": "Secure Smart Contract library for Solidity",
"version": "5.1.0", "version": "5.2.0",
"files": [ "files": [
"**/*.sol", "**/*.sol",
"/build/contracts/*.json", "/build/contracts/*.json",

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (proxy/Clones.sol) // OpenZeppelin Contracts (last updated v5.2.0) (proxy/Clones.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (proxy/ERC1967/ERC1967Proxy.sol) // OpenZeppelin Contracts (last updated v5.2.0) (proxy/ERC1967/ERC1967Proxy.sol)
pragma solidity ^0.8.22; pragma solidity ^0.8.22;

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (proxy/ERC1967/ERC1967Utils.sol) // OpenZeppelin Contracts (last updated v5.2.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.22; pragma solidity ^0.8.22;

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (proxy/beacon/BeaconProxy.sol) // OpenZeppelin Contracts (last updated v5.2.0) (proxy/beacon/BeaconProxy.sol)
pragma solidity ^0.8.22; pragma solidity ^0.8.22;

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (proxy/transparent/ProxyAdmin.sol) // OpenZeppelin Contracts (last updated v5.2.0) (proxy/transparent/ProxyAdmin.sol)
pragma solidity ^0.8.22; pragma solidity ^0.8.22;

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (proxy/transparent/TransparentUpgradeableProxy.sol) // OpenZeppelin Contracts (last updated v5.2.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
pragma solidity ^0.8.22; pragma solidity ^0.8.22;

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (proxy/utils/UUPSUpgradeable.sol) // OpenZeppelin Contracts (last updated v5.2.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.22; pragma solidity ^0.8.22;
@ -91,7 +91,6 @@ abstract contract UUPSUpgradeable is IERC1822Proxiable {
/** /**
* @dev Reverts if the execution is not performed via delegatecall or the execution * @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC-1967 compliant implementation pointing to self. * context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
* See {_onlyProxy}.
*/ */
function _checkProxy() internal view virtual { function _checkProxy() internal view virtual {
if ( if (

@ -81,7 +81,7 @@ interface IERC1155 is IERC165 {
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`.
* *
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens * WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155Received} on the receiver. * to an untrusted contract, when invoking {IERC1155Receiver-onERC1155Received} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing * Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts. * reentrancy guards when interacting with untrusted contracts.
* *
@ -101,7 +101,7 @@ interface IERC1155 is IERC165 {
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
* *
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens * WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver. * to an untrusted contract, when invoking {IERC1155Receiver-onERC1155BatchReceived} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing * Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts. * reentrancy guards when interacting with untrusted contracts.
* *

@ -15,7 +15,7 @@ import {IERC1155Errors} from "../../../interfaces/draft-IERC6093.sol";
*/ */
library ERC1155Utils { library ERC1155Utils {
/** /**
* @dev Performs an acceptance check for the provided `operator` by calling {IERC1155-onERC1155Received} * @dev Performs an acceptance check for the provided `operator` by calling {IERC1155Receiver-onERC1155Received}
* on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`). * on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`).
* *
* The acceptance call is not executed and treated as a no-op if the target address doesn't contain code (i.e. an EOA). * The acceptance call is not executed and treated as a no-op if the target address doesn't contain code (i.e. an EOA).
@ -50,7 +50,7 @@ library ERC1155Utils {
} }
/** /**
* @dev Performs a batch acceptance check for the provided `operator` by calling {IERC1155-onERC1155BatchReceived} * @dev Performs a batch acceptance check for the provided `operator` by calling {IERC1155Receiver-onERC1155BatchReceived}
* on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`). * on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`).
* *
* The acceptance call is not executed and treated as a no-op if the target address doesn't contain code (i.e. an EOA). * The acceptance call is not executed and treated as a no-op if the target address doesn't contain code (i.e. an EOA).

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol) // OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/ERC1363.sol) // OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/extensions/ERC1363.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;

@ -34,7 +34,7 @@ import {Math} from "../../../utils/math/Math.sol";
* offset (0) makes it non-profitable even if an attacker is able to capture value from multiple user deposits, as a result * offset (0) makes it non-profitable even if an attacker is able to capture value from multiple user deposits, as a result
* of the value being captured by the virtual shares (out of the attacker's donation) matching the attacker's expected gains. * of the value being captured by the virtual shares (out of the attacker's donation) matching the attacker's expected gains.
* With a larger offset, the attack becomes orders of magnitude more expensive than it is profitable. More details about the * With a larger offset, the attack becomes orders of magnitude more expensive than it is profitable. More details about the
* underlying math can be found xref:erc4626.adoc#inflation-attack[here]. * underlying math can be found xref:ROOT:erc4626.adoc#inflation-attack[here].
* *
* The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued
* to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/utils/SafeERC20.sol) // OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;

@ -3,7 +3,7 @@
[.readme-notice] [.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc721 NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc721
This set of interfaces, contracts, and utilities are all related to the https://eips.ethereum.org/EIPS/eip-721[ERC-721 Non-Fungible Token Standard]. This set of interfaces, contracts, and utilities is all related to the https://eips.ethereum.org/EIPS/eip-721[ERC-721 Non-Fungible Token Standard].
TIP: For a walk through on how to create an ERC-721 token read our xref:ROOT:erc721.adoc[ERC-721 guide]. TIP: For a walk through on how to create an ERC-721 token read our xref:ROOT:erc721.adoc[ERC-721 guide].

@ -159,7 +159,7 @@ abstract contract ERC721Consecutive is IERC2309, ERC721 {
} }
/** /**
* @dev Used to offset the first token id in {_nextConsecutiveId} * @dev Used to offset the first token id in `_nextConsecutiveId`
*/ */
function _firstConsecutiveId() internal view virtual returns (uint96) { function _firstConsecutiveId() internal view virtual returns (uint96) {
return 0; return 0;

@ -52,7 +52,7 @@ abstract contract ERC721URIStorage is IERC4906, ERC721 {
/** /**
* @dev Sets `_tokenURI` as the tokenURI of `tokenId`. * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
* *
* Emits {MetadataUpdate}. * Emits {IERC4906-MetadataUpdate}.
*/ */
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
_tokenURIs[tokenId] = _tokenURI; _tokenURIs[tokenId] = _tokenURI;

@ -15,7 +15,7 @@ import {IERC721Errors} from "../../../interfaces/draft-IERC6093.sol";
*/ */
library ERC721Utils { library ERC721Utils {
/** /**
* @dev Performs an acceptance check for the provided `operator` by calling {IERC721-onERC721Received} * @dev Performs an acceptance check for the provided `operator` by calling {IERC721Receiver-onERC721Received}
* on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`). * on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`).
* *
* The acceptance call is not executed and treated as a no-op if the target address doesn't contain code (i.e. an EOA). * The acceptance call is not executed and treated as a no-op if the target address doesn't contain code (i.e. an EOA).

@ -39,7 +39,7 @@ abstract contract ERC2981 is IERC2981, ERC165 {
error ERC2981InvalidDefaultRoyaltyReceiver(address receiver); error ERC2981InvalidDefaultRoyaltyReceiver(address receiver);
/** /**
* @dev The royalty set for an specific `tokenId` is invalid (eg. (numerator / denominator) >= 1). * @dev The royalty set for a specific `tokenId` is invalid (eg. (numerator / denominator) >= 1).
*/ */
error ERC2981InvalidTokenRoyalty(uint256 tokenId, uint256 numerator, uint256 denominator); error ERC2981InvalidTokenRoyalty(uint256 tokenId, uint256 numerator, uint256 denominator);

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol) // OpenZeppelin Contracts (last updated v5.2.0) (utils/Address.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;

@ -448,7 +448,7 @@ library Arrays {
} }
/** /**
* @dev Helper to set the length of an dynamic array. Directly writing to `.length` is forbidden. * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
* *
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased. * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/ */
@ -459,7 +459,7 @@ library Arrays {
} }
/** /**
* @dev Helper to set the length of an dynamic array. Directly writing to `.length` is forbidden. * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
* *
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased. * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/ */
@ -470,7 +470,7 @@ library Arrays {
} }
/** /**
* @dev Helper to set the length of an dynamic array. Directly writing to `.length` is forbidden. * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
* *
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased. * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/ */

@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/Bytes.sol)
pragma solidity ^0.8.24; pragma solidity ^0.8.24;

@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/CAIP10.sol)
pragma solidity ^0.8.24; pragma solidity ^0.8.24;

@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/CAIP2.sol)
pragma solidity ^0.8.24; pragma solidity ^0.8.24;

@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Helper library for manipulating objects in calldata.
*/
library Calldata {
// slither-disable-next-line write-after-write
function emptyBytes() internal pure returns (bytes calldata result) {
assembly ("memory-safe") {
result.offset := 0
result.length := 0
}
}
// slither-disable-next-line write-after-write
function emptyString() internal pure returns (string calldata result) {
assembly ("memory-safe") {
result.offset := 0
result.length := 0
}
}
}

@ -13,10 +13,10 @@ import {Context} from "./Context.sol";
* careful about sending transactions invoking {multicall}. For example, a relay address that filters function * careful about sending transactions invoking {multicall}. For example, a relay address that filters function
* selectors won't filter calls nested within a {multicall} operation. * selectors won't filter calls nested within a {multicall} operation.
* *
* NOTE: Since 5.0.1 and 4.9.4, this contract identifies non-canonical contexts (i.e. `msg.sender` is not {_msgSender}). * NOTE: Since 5.0.1 and 4.9.4, this contract identifies non-canonical contexts (i.e. `msg.sender` is not {Context-_msgSender}).
* If a non-canonical context is identified, the following self `delegatecall` appends the last bytes of `msg.data` * If a non-canonical context is identified, the following self `delegatecall` appends the last bytes of `msg.data`
* to the subcall. This makes it safe to use with {ERC2771Context}. Contexts that don't affect the resolution of * to the subcall. This makes it safe to use with {ERC2771Context}. Contexts that don't affect the resolution of
* {_msgSender} are not propagated to subcalls. * {Context-_msgSender} are not propagated to subcalls.
*/ */
abstract contract Multicall is Context { abstract contract Multicall is Context {
/** /**

@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/NoncesKeyed.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
import {Nonces} from "./Nonces.sol"; import {Nonces} from "./Nonces.sol";

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Packing.sol) // OpenZeppelin Contracts (last updated v5.2.0) (utils/Packing.sol)
// This file was procedurally generated from scripts/generate/templates/Packing.js. // This file was procedurally generated from scripts/generate/templates/Packing.js.
pragma solidity ^0.8.20; pragma solidity ^0.8.20;

@ -37,13 +37,6 @@ abstract contract Pausable is Context {
*/ */
error ExpectedPause(); error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/** /**
* @dev Modifier to make a function callable only when the contract is not paused. * @dev Modifier to make a function callable only when the contract is not paused.
* *

@ -33,8 +33,9 @@ Miscellaneous contracts and libraries containing utility functions you can use t
* {Arrays}: Collection of functions that operate on https://docs.soliditylang.org/en/latest/types.html#arrays[`arrays`]. * {Arrays}: Collection of functions that operate on https://docs.soliditylang.org/en/latest/types.html#arrays[`arrays`].
* {Base64}: On-chain base64 and base64URL encoding according to https://datatracker.ietf.org/doc/html/rfc4648[RFC-4648]. * {Base64}: On-chain base64 and base64URL encoding according to https://datatracker.ietf.org/doc/html/rfc4648[RFC-4648].
* {Bytes}: Common operations on bytes objects. * {Bytes}: Common operations on bytes objects.
* {Calldata}: Helpers for manipulating calldata.
* {Strings}: Common operations for strings formatting. * {Strings}: Common operations for strings formatting.
* {ShortString}: Library to encode (and decode) short strings into (or from) a single bytes32 slot for optimizing costs. Short strings are limited to 31 characters. * {ShortStrings}: Library to encode (and decode) short strings into (or from) a single bytes32 slot for optimizing costs. Short strings are limited to 31 characters.
* {SlotDerivation}: Methods for deriving storage slot from ERC-7201 namespaces as well as from constructions such as mapping and arrays. * {SlotDerivation}: Methods for deriving storage slot from ERC-7201 namespaces as well as from constructions such as mapping and arrays.
* {StorageSlot}: Methods for accessing specific storage slots formatted as common primitive types. * {StorageSlot}: Methods for accessing specific storage slots formatted as common primitive types.
* {TransientSlot}: Primitives for reading from and writing to transient storage (only value types are currently supported). * {TransientSlot}: Primitives for reading from and writing to transient storage (only value types are currently supported).
@ -128,6 +129,10 @@ Ethereum contracts have no native concept of an interface, so applications must
{{Base64}} {{Base64}}
{{Bytes}}
{{Calldata}}
{{Strings}} {{Strings}}
{{ShortStrings}} {{ShortStrings}}
@ -147,3 +152,7 @@ Ethereum contracts have no native concept of an interface, so applications must
{{Panic}} {{Panic}}
{{Comparators}} {{Comparators}}
{{CAIP2}}
{{CAIP10}}

@ -38,7 +38,7 @@ abstract contract ReentrancyGuardTransient {
} }
function _nonReentrantBefore() private { function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED // On the first call to nonReentrant, REENTRANCY_GUARD_STORAGE.asBoolean().tload() will be false
if (_reentrancyGuardEntered()) { if (_reentrancyGuardEntered()) {
revert ReentrancyGuardReentrantCall(); revert ReentrancyGuardReentrantCall();
} }

@ -95,7 +95,7 @@ library ShortStrings {
} }
/** /**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}. * @dev Decode a string that was encoded to `ShortString` or written to storage using {toShortStringWithFallback}.
*/ */
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) { function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) { if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
@ -107,7 +107,7 @@ library ShortStrings {
/** /**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using * @dev Return the length of a string that was encoded to `ShortString` or written to storage using
* {setWithFallback}. * {toShortStringWithFallback}.
* *
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes. * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Strings.sol) // OpenZeppelin Contracts (last updated v5.2.0) (utils/Strings.sol)
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
@ -139,7 +139,7 @@ library Strings {
} }
/** /**
* @dev Variant of {parseUint} that parses a substring of `input` located between position `begin` (included) and * @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded). * `end` (excluded).
* *
* Requirements: * Requirements:
@ -177,7 +177,7 @@ library Strings {
} }
/** /**
* @dev Implementation of {tryParseUint} that does not check bounds. Caller should make sure that * @dev Implementation of {tryParseUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior. * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/ */
function _tryParseUintUncheckedBounds( function _tryParseUintUncheckedBounds(
@ -250,7 +250,7 @@ library Strings {
} }
/** /**
* @dev Implementation of {tryParseInt} that does not check bounds. Caller should make sure that * @dev Implementation of {tryParseInt-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior. * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/ */
function _tryParseIntUncheckedBounds( function _tryParseIntUncheckedBounds(
@ -287,7 +287,7 @@ library Strings {
} }
/** /**
* @dev Variant of {parseHexUint} that parses a substring of `input` located between position `begin` (included) and * @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded). * `end` (excluded).
* *
* Requirements: * Requirements:
@ -325,7 +325,7 @@ library Strings {
} }
/** /**
* @dev Implementation of {tryParseHexUint} that does not check bounds. Caller should make sure that * @dev Implementation of {tryParseHexUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior. * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/ */
function _tryParseHexUintUncheckedBounds( function _tryParseHexUintUncheckedBounds(
@ -346,7 +346,7 @@ library Strings {
result *= 16; result *= 16;
unchecked { unchecked {
// Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check). // Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).
// This guaratees that adding a value < 16 will not cause an overflow, hence the unchecked. // This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked.
result += chr; result += chr;
} }
} }
@ -364,7 +364,7 @@ library Strings {
} }
/** /**
* @dev Variant of {parseAddress} that parses a substring of `input` located between position `begin` (included) and * @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded). * `end` (excluded).
* *
* Requirements: * Requirements:
@ -378,7 +378,7 @@ library Strings {
/** /**
* @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly * @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly
* formatted address. See {parseAddress} requirements. * formatted address. See {parseAddress-string} requirements.
*/ */
function tryParseAddress(string memory input) internal pure returns (bool success, address value) { function tryParseAddress(string memory input) internal pure returns (bool success, address value) {
return tryParseAddress(input, 0, bytes(input).length); return tryParseAddress(input, 0, bytes(input).length);
@ -386,7 +386,7 @@ library Strings {
/** /**
* @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly * @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly
* formatted address. See {parseAddress} requirements. * formatted address. See {parseAddress-string-uint256-uint256} requirements.
*/ */
function tryParseAddress( function tryParseAddress(
string memory input, string memory input,

@ -109,7 +109,7 @@ abstract contract EIP712 is IERC5267 {
} }
/** /**
* @dev See {IERC-5267}. * @inheritdoc IERC5267
*/ */
function eip712Domain() function eip712Domain()
public public

@ -19,7 +19,7 @@ library MessageHashUtils {
* *
* The digest is calculated by prefixing a bytes32 `messageHash` with * The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method.
* *
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will * keccak256, although any bytes32 value can be safely used because the final digest will
@ -41,7 +41,7 @@ library MessageHashUtils {
* *
* The digest is calculated by prefixing an arbitrary `message` with * The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method.
* *
* See {ECDSA-recover}. * See {ECDSA-recover}.
*/ */

@ -88,7 +88,7 @@ library MerkleTree {
// Build each root of zero-filled subtrees // Build each root of zero-filled subtrees
bytes32 currentZero = zero; bytes32 currentZero = zero;
for (uint32 i = 0; i < treeDepth; ++i) { for (uint256 i = 0; i < treeDepth; ++i) {
Arrays.unsafeAccess(self._zeros, i).value = currentZero; Arrays.unsafeAccess(self._zeros, i).value = currentZero;
currentZero = fnHash(currentZero, currentZero); currentZero = fnHash(currentZero, currentZero);
} }
@ -143,7 +143,7 @@ library MerkleTree {
// Rebuild branch from leaf to root // Rebuild branch from leaf to root
uint256 currentIndex = index; uint256 currentIndex = index;
bytes32 currentLevelHash = leaf; bytes32 currentLevelHash = leaf;
for (uint32 i = 0; i < treeDepth; i++) { for (uint256 i = 0; i < treeDepth; i++) {
// Reaching the parent node, is currentLevelHash the left child? // Reaching the parent node, is currentLevelHash the left child?
bool isLeft = currentIndex % 2 == 0; bool isLeft = currentIndex % 2 == 0;

@ -1,11 +0,0 @@
= Crowdsales
All crowdsale-related contracts were removed from the OpenZeppelin Contracts library on the https://forum.openzeppelin.com/t/openzeppelin-contracts-v3-0-beta-release/2256[v3.0.0 release] due to both a decline in their usage and the complexity associated with migrating them to Solidity v0.6.
They are however still available on the v2.5 release of OpenZeppelin Contracts, which you can install by running:
```console
$ npm install @openzeppelin/contracts@v2.5
```
Refer to the https://docs.openzeppelin.com/contracts/2.x/crowdsales[v2.x documentation] when working with them.

@ -1,19 +0,0 @@
= Drafts
All draft contracts were either moved into a different directory or removed from the OpenZeppelin Contracts library on the https://forum.openzeppelin.com/t/openzeppelin-contracts-v3-0-beta-release/2256[v3.0.0 release].
* `ERC20Migrator`: removed.
* xref:api:token/ERC20.adoc#ERC20Snapshot[`ERC20Snapshot`]: moved to `token/ERC20`.
* `ERC20Detailed` and `ERC1046`: removed.
* `TokenVesting`: removed. Pending a replacement that is being discussed in https://github.com/OpenZeppelin/openzeppelin-contracts/issues/1214[`#1214`].
* xref:api:utils.adoc#Counters[`Counters`]: moved to xref:api:utils.adoc[`utils`].
* xref:api:utils.adoc#Strings[`Strings`]: moved to xref:api:utils.adoc[`utils`].
* xref:api:utils.adoc#SignedSafeMath[`SignedSafeMath`]: moved to xref:api:utils.adoc[`utils`].
Removed contracts are still available on the v2.5 release of OpenZeppelin Contracts, which you can install by running:
```console
$ npm install @openzeppelin/contracts@v2.5
```
Refer to the xref:2.x@contracts:api:drafts.adoc[v2.x documentation] when working with them.

@ -68,4 +68,4 @@ include::api:example$ERC20WithAutoMinerReward.sol[]
[[wrapping-up]] [[wrapping-up]]
== Wrapping Up == Wrapping Up
We've seen how to implement a ERC-20 supply mechanism: internally through `_mint`. Hopefully this has helped you understand how to use OpenZeppelin Contracts and some of the design principles behind it, and you can apply them to your own smart contracts. We've seen how to implement an ERC-20 supply mechanism: internally through `_mint`. Hopefully this has helped you understand how to use OpenZeppelin Contracts and some of the design principles behind it, and you can apply them to your own smart contracts.

@ -20,7 +20,7 @@ The ERC-20 extension to keep track of votes and vote delegation is one such case
=== Governor & GovernorStorage === Governor & GovernorStorage
An OpenZeppelin Governor contract is not interface-compatible with Compound's GovernorAlpha or Bravo. Even though events are fully compatible, proposal lifecycle functions (creation, execution, etc.) have different signatures that are meant to optimize storage use. Other functions from GovernorAlpha and Bravo are likewise not available. It’s possible to opt in some Bravo-like behavior by inheriting from the GovernorStorage module. This module provides proposal enumerability and alternate versions of the `queue`, `execute` and `cancel` function that only take the proposal id. This module reduces the calldata needed by some operations in exchange for an increased the storage footprint. This might be a good trade-off for some L2 chains. It also provides primitives for indexer-free frontends. An OpenZeppelin Governor contract is not interface-compatible with Compound's GovernorAlpha or Bravo. Even though events are fully compatible, proposal lifecycle functions (creation, execution, etc.) have different signatures that are meant to optimize storage use. Other functions from GovernorAlpha and Bravo are likewise not available. It’s possible to opt in some Bravo-like behavior by inheriting from the GovernorStorage module. This module provides proposal enumerability and alternate versions of the `queue`, `execute` and `cancel` function that only take the proposal id. This module reduces the calldata needed by some operations in exchange for an increased storage footprint. This might be a good trade-off for some L2 chains. It also provides primitives for indexer-free frontends.
Note that even with the use of this module, one important difference with Compound's GovernorBravo is the way that `proposalId`s are calculated. Governor uses the hash of the proposal parameters with the purpose of keeping its data off-chain by event indexing, while the original Bravo implementation uses sequential `proposalId`s. Note that even with the use of this module, one important difference with Compound's GovernorBravo is the way that `proposalId`s are calculated. Governor uses the hash of the proposal parameters with the purpose of keeping its data off-chain by event indexing, while the original Bravo implementation uses sequential `proposalId`s.
@ -52,7 +52,7 @@ If your project already has a live token that does not include ERC20Votes and is
include::api:example$governance/MyTokenWrapped.sol[] include::api:example$governance/MyTokenWrapped.sol[]
``` ```
NOTE: The only other source of voting power available in OpenZeppelin Contracts currently is xref:api:token/ERC721.adoc#ERC721Votes[`ERC721Votes`]. ERC-721 tokens that don't provide this functionality can be wrapped into a voting tokens using a combination of xref:api:token/ERC721.adoc#ERC721Votes[`ERC721Votes`] and xref:api:token/ERC721Wrapper.adoc#ERC721Wrapper[`ERC721Wrapper`]. NOTE: The only other source of voting power available in OpenZeppelin Contracts currently is xref:api:token/ERC721.adoc#ERC721Votes[`ERC721Votes`]. ERC-721 tokens that don't provide this functionality can be wrapped into a voting tokens using a combination of xref:api:token/ERC721.adoc#ERC721Votes[`ERC721Votes`] and xref:api:token/ERC721.adoc#ERC721Wrapper[`ERC721Wrapper`].
NOTE: The internal clock used by the token to store voting balances will dictate the operating mode of the Governor contract attached to it. By default, block numbers are used. Since v4.9, developers can override the xref:api:interfaces.adoc#IERC6372[IERC6372] clock to use timestamps instead of block numbers. NOTE: The internal clock used by the token to store voting balances will dictate the operating mode of the Governor contract attached to it. By default, block numbers are used. Since v4.9, developers can override the xref:api:interfaces.adoc#IERC6372[IERC6372] clock to use timestamps instead of block numbers.

@ -2,6 +2,10 @@
:{{name}}: pass:normal[xref:#{{anchor}}[`++{{name}}++`]] :{{name}}: pass:normal[xref:#{{anchor}}[`++{{name}}++`]]
{{/each}} {{/each}}
{{#each functions}}
:{{fullname}}: pass:normal[xref:#{{anchor}}[`++{{name}}++`]]
{{/each}}
[.contract] [.contract]
[[{{anchor}}]] [[{{anchor}}]]
=== `++{{name}}++` link:https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v{{oz-version}}/{{__item_context.file.absolutePath}}[{github-icon},role=heading-link] === `++{{name}}++` link:https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v{{oz-version}}/{{__item_context.file.absolutePath}}[{github-icon},role=heading-link]

@ -17,6 +17,22 @@ module.exports.anchor = function anchor({ item, contract }) {
return res; return res;
}; };
module.exports.fullname = function fullname({ item }) {
let res = '';
res += item.name;
if ('parameters' in item) {
const signature = item.parameters.parameters.map(v => v.typeName.typeDescriptions.typeString).join(',');
res += slug('(' + signature + ')');
}
if (isNodeType('VariableDeclaration', item)) {
res += '-' + slug(item.typeName.typeDescriptions.typeString);
}
if (res.charAt(res.length - 1) === '-') {
return res.slice(0, -1);
}
return res;
};
module.exports.inheritance = function ({ item, build }) { module.exports.inheritance = function ({ item, build }) {
if (!isNodeType('ContractDefinition', item)) { if (!isNodeType('ContractDefinition', item)) {
throw new Error('used inherited-items on non-contract'); throw new Error('used inherited-items on non-contract');

@ -1,4 +1,4 @@
certora-cli==4.13.1 certora-cli==4.13.1
# File uses a custom name (fv-requirements.txt) so that it isn't picked by Netlify's build # File uses a custom name (fv-requirements.txt) so that it isn't picked by Netlify's build
# whose latest Python version is 0.3.8, incompatible with most recent versions of Halmos # whose latest Python version is 0.3.8, incompatible with most recent versions of Halmos
halmos==0.2.3 halmos==0.2.4

@ -1,7 +1,7 @@
{ {
"name": "openzeppelin-solidity", "name": "openzeppelin-solidity",
"description": "Secure Smart Contract library for Solidity", "description": "Secure Smart Contract library for Solidity",
"version": "5.1.0", "version": "5.2.0",
"private": true, "private": true,
"files": [ "files": [
"/contracts/**/*.sol", "/contracts/**/*.sol",

@ -346,7 +346,7 @@ function unsafeMemoryAccess(${type}[] memory arr, uint256 pos) internal pure ret
const unsafeSetLength = type => `\ const unsafeSetLength = type => `\
/** /**
* @dev Helper to set the length of an dynamic array. Directly writing to \`.length\` is forbidden. * @dev Helper to set the length of a dynamic array. Directly writing to \`.length\` is forbidden.
* *
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased. * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/ */

@ -27,7 +27,7 @@ describe('ERC4337Utils', function () {
const validAfter = 0x9abcdef0n; const validAfter = 0x9abcdef0n;
const validationData = packValidationData(validAfter, validUntil, authorizer); const validationData = packValidationData(validAfter, validUntil, authorizer);
expect(this.utils.$parseValidationData(validationData)).to.eventually.deep.equal([ await expect(this.utils.$parseValidationData(validationData)).to.eventually.deep.equal([
authorizer.address, authorizer.address,
validAfter, validAfter,
validUntil, validUntil,
@ -39,7 +39,7 @@ describe('ERC4337Utils', function () {
const validAfter = 0x12345678n; const validAfter = 0x12345678n;
const validationData = packValidationData(validAfter, 0, authorizer); const validationData = packValidationData(validAfter, 0, authorizer);
expect(this.utils.$parseValidationData(validationData)).to.eventually.deep.equal([ await expect(this.utils.$parseValidationData(validationData)).to.eventually.deep.equal([
authorizer.address, authorizer.address,
validAfter, validAfter,
MAX_UINT48, MAX_UINT48,
@ -47,13 +47,13 @@ describe('ERC4337Utils', function () {
}); });
it('parse canonical values', async function () { it('parse canonical values', async function () {
expect(this.utils.$parseValidationData(this.SIG_VALIDATION_SUCCESS)).to.eventually.deep.equal([ await expect(this.utils.$parseValidationData(this.SIG_VALIDATION_SUCCESS)).to.eventually.deep.equal([
ethers.ZeroAddress, ethers.ZeroAddress,
0n, 0n,
MAX_UINT48, MAX_UINT48,
]); ]);
expect(this.utils.$parseValidationData(this.SIG_VALIDATION_FAILED)).to.eventually.deep.equal([ await expect(this.utils.$parseValidationData(this.SIG_VALIDATION_FAILED)).to.eventually.deep.equal([
ADDRESS_ONE, ADDRESS_ONE,
0n, 0n,
MAX_UINT48, MAX_UINT48,
@ -68,7 +68,7 @@ describe('ERC4337Utils', function () {
const validAfter = 0x9abcdef0n; const validAfter = 0x9abcdef0n;
const validationData = packValidationData(validAfter, validUntil, authorizer); const validationData = packValidationData(validAfter, validUntil, authorizer);
expect( await expect(
this.utils.$packValidationData(ethers.Typed.address(authorizer), validAfter, validUntil), this.utils.$packValidationData(ethers.Typed.address(authorizer), validAfter, validUntil),
).to.eventually.equal(validationData); ).to.eventually.equal(validationData);
}); });
@ -79,22 +79,22 @@ describe('ERC4337Utils', function () {
const validAfter = 0x9abcdef0n; const validAfter = 0x9abcdef0n;
const validationData = packValidationData(validAfter, validUntil, false); const validationData = packValidationData(validAfter, validUntil, false);
expect(this.utils.$packValidationData(ethers.Typed.bool(success), validAfter, validUntil)).to.eventually.equal( await expect(
validationData, this.utils.$packValidationData(ethers.Typed.bool(success), validAfter, validUntil),
); ).to.eventually.equal(validationData);
}); });
it('packing reproduced canonical values', async function () { it('packing reproduced canonical values', async function () {
expect(this.utils.$packValidationData(ethers.Typed.address(ethers.ZeroAddress), 0n, 0n)).to.eventually.equal( await expect(
this.SIG_VALIDATION_SUCCESS, this.utils.$packValidationData(ethers.Typed.address(ethers.ZeroAddress), 0n, 0n),
); ).to.eventually.equal(this.SIG_VALIDATION_SUCCESS);
expect(this.utils.$packValidationData(ethers.Typed.bool(true), 0n, 0n)).to.eventually.equal( await expect(this.utils.$packValidationData(ethers.Typed.bool(true), 0n, 0n)).to.eventually.equal(
this.SIG_VALIDATION_SUCCESS, this.SIG_VALIDATION_SUCCESS,
); );
expect(this.utils.$packValidationData(ethers.Typed.address(ADDRESS_ONE), 0n, 0n)).to.eventually.equal( await expect(this.utils.$packValidationData(ethers.Typed.address(ADDRESS_ONE), 0n, 0n)).to.eventually.equal(
this.SIG_VALIDATION_FAILED, this.SIG_VALIDATION_FAILED,
); );
expect(this.utils.$packValidationData(ethers.Typed.bool(false), 0n, 0n)).to.eventually.equal( await expect(this.utils.$packValidationData(ethers.Typed.bool(false), 0n, 0n)).to.eventually.equal(
this.SIG_VALIDATION_FAILED, this.SIG_VALIDATION_FAILED,
); );
}); });
@ -112,8 +112,8 @@ describe('ERC4337Utils', function () {
const expected = packValidationData(validAfter2, validUntil1, true); const expected = packValidationData(validAfter2, validUntil1, true);
// check symmetry // check symmetry
expect(this.utils.$combineValidationData(validationData1, validationData2)).to.eventually.equal(expected); await expect(this.utils.$combineValidationData(validationData1, validationData2)).to.eventually.equal(expected);
expect(this.utils.$combineValidationData(validationData2, validationData1)).to.eventually.equal(expected); await expect(this.utils.$combineValidationData(validationData2, validationData1)).to.eventually.equal(expected);
}); });
for (const [authorizer1, authorizer2] of [ for (const [authorizer1, authorizer2] of [
@ -126,8 +126,8 @@ describe('ERC4337Utils', function () {
const expected = packValidationData(validAfter2, validUntil1, false); const expected = packValidationData(validAfter2, validUntil1, false);
// check symmetry // check symmetry
expect(this.utils.$combineValidationData(validationData1, validationData2)).to.eventually.equal(expected); await expect(this.utils.$combineValidationData(validationData1, validationData2)).to.eventually.equal(expected);
expect(this.utils.$combineValidationData(validationData2, validationData1)).to.eventually.equal(expected); await expect(this.utils.$combineValidationData(validationData2, validationData1)).to.eventually.equal(expected);
}); });
} }
}); });
@ -139,7 +139,7 @@ describe('ERC4337Utils', function () {
const validUntil = MAX_UINT48; const validUntil = MAX_UINT48;
const validationData = packValidationData(validAfter, validUntil, aggregator); const validationData = packValidationData(validAfter, validUntil, aggregator);
expect(this.utils.$getValidationData(validationData)).to.eventually.deep.equal([aggregator.address, false]); await expect(this.utils.$getValidationData(validationData)).to.eventually.deep.equal([aggregator.address, false]);
}); });
it('returns the validation data with invalid validity range (expired)', async function () { it('returns the validation data with invalid validity range (expired)', async function () {
@ -148,7 +148,7 @@ describe('ERC4337Utils', function () {
const validUntil = 1; const validUntil = 1;
const validationData = packValidationData(validAfter, validUntil, aggregator); const validationData = packValidationData(validAfter, validUntil, aggregator);
expect(this.utils.$getValidationData(validationData)).to.eventually.deep.equal([aggregator.address, true]); await expect(this.utils.$getValidationData(validationData)).to.eventually.deep.equal([aggregator.address, true]);
}); });
it('returns the validation data with invalid validity range (not yet valid)', async function () { it('returns the validation data with invalid validity range (not yet valid)', async function () {
@ -157,11 +157,11 @@ describe('ERC4337Utils', function () {
const validUntil = MAX_UINT48; const validUntil = MAX_UINT48;
const validationData = packValidationData(validAfter, validUntil, aggregator); const validationData = packValidationData(validAfter, validUntil, aggregator);
expect(this.utils.$getValidationData(validationData)).to.eventually.deep.equal([aggregator.address, true]); await expect(this.utils.$getValidationData(validationData)).to.eventually.deep.equal([aggregator.address, true]);
}); });
it('returns address(0) and false for validationData = 0', function () { it('returns address(0) and false for validationData = 0', async function () {
expect(this.utils.$getValidationData(0n)).to.eventually.deep.equal([ethers.ZeroAddress, false]); await expect(this.utils.$getValidationData(0n)).to.eventually.deep.equal([ethers.ZeroAddress, false]);
}); });
}); });
@ -172,13 +172,13 @@ describe('ERC4337Utils', function () {
const otherChainId = 0xdeadbeef; const otherChainId = 0xdeadbeef;
// check that helper matches entrypoint logic // check that helper matches entrypoint logic
expect(entrypoint.getUserOpHash(userOp.packed)).to.eventually.equal(userOp.hash(entrypoint, chainId)); await expect(entrypoint.getUserOpHash(userOp.packed)).to.eventually.equal(userOp.hash(entrypoint, chainId));
// check library against helper // check library against helper
expect(this.utils.$hash(userOp.packed, entrypoint, chainId)).to.eventually.equal( await expect(this.utils.$hash(userOp.packed, entrypoint, chainId)).to.eventually.equal(
userOp.hash(entrypoint, chainId), userOp.hash(entrypoint, chainId),
); );
expect(this.utils.$hash(userOp.packed, entrypoint, otherChainId)).to.eventually.equal( await expect(this.utils.$hash(userOp.packed, entrypoint, otherChainId)).to.eventually.equal(
userOp.hash(entrypoint, otherChainId), userOp.hash(entrypoint, otherChainId),
); );
}); });
@ -202,34 +202,34 @@ describe('ERC4337Utils', function () {
}); });
it('returns factory', async function () { it('returns factory', async function () {
expect(this.utils.$factory(this.userOp.packed)).to.eventually.equal(this.factory); await expect(this.utils.$factory(this.userOp.packed)).to.eventually.equal(this.factory);
expect(this.utils.$factory(this.emptyUserOp.packed)).to.eventually.equal(ethers.ZeroAddress); await expect(this.utils.$factory(this.emptyUserOp.packed)).to.eventually.equal(ethers.ZeroAddress);
}); });
it('returns factoryData', async function () { it('returns factoryData', async function () {
expect(this.utils.$factoryData(this.userOp.packed)).to.eventually.equal('0x123456'); await expect(this.utils.$factoryData(this.userOp.packed)).to.eventually.equal('0x123456');
expect(this.utils.$factoryData(this.emptyUserOp.packed)).to.eventually.equal('0x'); await expect(this.utils.$factoryData(this.emptyUserOp.packed)).to.eventually.equal('0x');
}); });
}); });
it('returns verificationGasLimit', async function () { it('returns verificationGasLimit', async function () {
const userOp = new UserOperation({ sender: this.sender, nonce: 1, verificationGas: 0x12345678n }); const userOp = new UserOperation({ sender: this.sender, nonce: 1, verificationGas: 0x12345678n });
expect(this.utils.$verificationGasLimit(userOp.packed)).to.eventually.equal(userOp.verificationGas); await expect(this.utils.$verificationGasLimit(userOp.packed)).to.eventually.equal(userOp.verificationGas);
}); });
it('returns callGasLimit', async function () { it('returns callGasLimit', async function () {
const userOp = new UserOperation({ sender: this.sender, nonce: 1, callGas: 0x12345678n }); const userOp = new UserOperation({ sender: this.sender, nonce: 1, callGas: 0x12345678n });
expect(this.utils.$callGasLimit(userOp.packed)).to.eventually.equal(userOp.callGas); await expect(this.utils.$callGasLimit(userOp.packed)).to.eventually.equal(userOp.callGas);
}); });
it('returns maxPriorityFeePerGas', async function () { it('returns maxPriorityFeePerGas', async function () {
const userOp = new UserOperation({ sender: this.sender, nonce: 1, maxPriorityFee: 0x12345678n }); const userOp = new UserOperation({ sender: this.sender, nonce: 1, maxPriorityFee: 0x12345678n });
expect(this.utils.$maxPriorityFeePerGas(userOp.packed)).to.eventually.equal(userOp.maxPriorityFee); await expect(this.utils.$maxPriorityFeePerGas(userOp.packed)).to.eventually.equal(userOp.maxPriorityFee);
}); });
it('returns maxFeePerGas', async function () { it('returns maxFeePerGas', async function () {
const userOp = new UserOperation({ sender: this.sender, nonce: 1, maxFeePerGas: 0x12345678n }); const userOp = new UserOperation({ sender: this.sender, nonce: 1, maxFeePerGas: 0x12345678n });
expect(this.utils.$maxFeePerGas(userOp.packed)).to.eventually.equal(userOp.maxFeePerGas); await expect(this.utils.$maxFeePerGas(userOp.packed)).to.eventually.equal(userOp.maxFeePerGas);
}); });
it('returns gasPrice', async function () { it('returns gasPrice', async function () {
@ -239,7 +239,7 @@ describe('ERC4337Utils', function () {
maxPriorityFee: 0x12345678n, maxPriorityFee: 0x12345678n,
maxFeePerGas: 0x87654321n, maxFeePerGas: 0x87654321n,
}); });
expect(this.utils.$gasPrice(userOp.packed)).to.eventually.equal(userOp.maxPriorityFee); await expect(this.utils.$gasPrice(userOp.packed)).to.eventually.equal(userOp.maxPriorityFee);
}); });
describe('paymasterAndData', function () { describe('paymasterAndData', function () {
@ -260,27 +260,27 @@ describe('ERC4337Utils', function () {
}); });
it('returns paymaster', async function () { it('returns paymaster', async function () {
expect(this.utils.$paymaster(this.userOp.packed)).to.eventually.equal(this.userOp.paymaster); await expect(this.utils.$paymaster(this.userOp.packed)).to.eventually.equal(this.userOp.paymaster);
expect(this.utils.$paymaster(this.emptyUserOp.packed)).to.eventually.equal(ethers.ZeroAddress); await expect(this.utils.$paymaster(this.emptyUserOp.packed)).to.eventually.equal(ethers.ZeroAddress);
}); });
it('returns verificationGasLimit', async function () { it('returns verificationGasLimit', async function () {
expect(this.utils.$paymasterVerificationGasLimit(this.userOp.packed)).to.eventually.equal( await expect(this.utils.$paymasterVerificationGasLimit(this.userOp.packed)).to.eventually.equal(
this.userOp.paymasterVerificationGasLimit, this.userOp.paymasterVerificationGasLimit,
); );
expect(this.utils.$paymasterVerificationGasLimit(this.emptyUserOp.packed)).to.eventually.equal(0n); await expect(this.utils.$paymasterVerificationGasLimit(this.emptyUserOp.packed)).to.eventually.equal(0n);
}); });
it('returns postOpGasLimit', async function () { it('returns postOpGasLimit', async function () {
expect(this.utils.$paymasterPostOpGasLimit(this.userOp.packed)).to.eventually.equal( await expect(this.utils.$paymasterPostOpGasLimit(this.userOp.packed)).to.eventually.equal(
this.userOp.paymasterPostOpGasLimit, this.userOp.paymasterPostOpGasLimit,
); );
expect(this.utils.$paymasterPostOpGasLimit(this.emptyUserOp.packed)).to.eventually.equal(0n); await expect(this.utils.$paymasterPostOpGasLimit(this.emptyUserOp.packed)).to.eventually.equal(0n);
}); });
it('returns data', async function () { it('returns data', async function () {
expect(this.utils.$paymasterData(this.userOp.packed)).to.eventually.equal(this.userOp.paymasterData); await expect(this.utils.$paymasterData(this.userOp.packed)).to.eventually.equal(this.userOp.paymasterData);
expect(this.utils.$paymasterData(this.emptyUserOp.packed)).to.eventually.equal('0x'); await expect(this.utils.$paymasterData(this.emptyUserOp.packed)).to.eventually.equal('0x');
}); });
}); });
}); });

@ -36,7 +36,7 @@ describe('ERC7579Utils', function () {
await expect(this.utils.$execSingle(data, EXEC_TYPE_DEFAULT)).to.emit(this.target, 'MockFunctionCalled'); await expect(this.utils.$execSingle(data, EXEC_TYPE_DEFAULT)).to.emit(this.target, 'MockFunctionCalled');
expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value); await expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value);
}); });
it('calls the target with value and args', async function () { it('calls the target with value and args', async function () {
@ -51,7 +51,7 @@ describe('ERC7579Utils', function () {
.to.emit(this.target, 'MockFunctionCalledWithArgs') .to.emit(this.target, 'MockFunctionCalledWithArgs')
.withArgs(42, '0x1234'); .withArgs(42, '0x1234');
expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value); await expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value);
}); });
it('reverts when target reverts in default ExecType', async function () { it('reverts when target reverts in default ExecType', async function () {
@ -107,8 +107,8 @@ describe('ERC7579Utils', function () {
.to.emit(this.target, 'MockFunctionCalled') .to.emit(this.target, 'MockFunctionCalled')
.to.emit(this.anotherTarget, 'MockFunctionCalled'); .to.emit(this.anotherTarget, 'MockFunctionCalled');
expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value1); await expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value1);
expect(ethers.provider.getBalance(this.anotherTarget)).to.eventually.equal(value2); await expect(ethers.provider.getBalance(this.anotherTarget)).to.eventually.equal(value2);
}); });
it('calls the targets with value and args', async function () { it('calls the targets with value and args', async function () {
@ -127,8 +127,8 @@ describe('ERC7579Utils', function () {
.to.emit(this.target, 'MockFunctionCalledWithArgs') .to.emit(this.target, 'MockFunctionCalledWithArgs')
.to.emit(this.anotherTarget, 'MockFunctionCalledWithArgs'); .to.emit(this.anotherTarget, 'MockFunctionCalledWithArgs');
expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value1); await expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value1);
expect(ethers.provider.getBalance(this.anotherTarget)).to.eventually.equal(value2); await expect(ethers.provider.getBalance(this.anotherTarget)).to.eventually.equal(value2);
}); });
it('reverts when any target reverts in default ExecType', async function () { it('reverts when any target reverts in default ExecType', async function () {
@ -161,8 +161,8 @@ describe('ERC7579Utils', function () {
); );
// Check balances // Check balances
expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value1); await expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value1);
expect(ethers.provider.getBalance(this.anotherTarget)).to.eventually.equal(0); await expect(ethers.provider.getBalance(this.anotherTarget)).to.eventually.equal(0);
}); });
it('reverts with an invalid exec type', async function () { it('reverts with an invalid exec type', async function () {
@ -188,9 +188,9 @@ describe('ERC7579Utils', function () {
this.target.interface.encodeFunctionData('mockFunctionWritesStorage', [slot, value]), this.target.interface.encodeFunctionData('mockFunctionWritesStorage', [slot, value]),
); );
expect(ethers.provider.getStorage(this.utils.target, slot)).to.eventually.equal(ethers.ZeroHash); await expect(ethers.provider.getStorage(this.utils.target, slot)).to.eventually.equal(ethers.ZeroHash);
await this.utils.$execDelegateCall(data, EXEC_TYPE_DEFAULT); await this.utils.$execDelegateCall(data, EXEC_TYPE_DEFAULT);
expect(ethers.provider.getStorage(this.utils.target, slot)).to.eventually.equal(value); await expect(ethers.provider.getStorage(this.utils.target, slot)).to.eventually.equal(value);
}); });
it('reverts when target reverts in default ExecType', async function () { it('reverts when target reverts in default ExecType', async function () {
@ -227,7 +227,7 @@ describe('ERC7579Utils', function () {
const selector = '0x12345678'; const selector = '0x12345678';
const payload = ethers.toBeHex(0, 22); const payload = ethers.toBeHex(0, 22);
expect(this.utils.$encodeMode(callType, execType, selector, payload)).to.eventually.equal( await expect(this.utils.$encodeMode(callType, execType, selector, payload)).to.eventually.equal(
encodeMode({ encodeMode({
callType, callType,
execType, execType,
@ -243,7 +243,7 @@ describe('ERC7579Utils', function () {
const selector = '0x12345678'; const selector = '0x12345678';
const payload = ethers.toBeHex(0, 22); const payload = ethers.toBeHex(0, 22);
expect( await expect(
this.utils.$decodeMode( this.utils.$decodeMode(
encodeMode({ encodeMode({
callType, callType,
@ -260,7 +260,7 @@ describe('ERC7579Utils', function () {
const value = 0x123; const value = 0x123;
const data = '0x12345678'; const data = '0x12345678';
expect(this.utils.$encodeSingle(target, value, data)).to.eventually.equal(encodeSingle(target, value, data)); await expect(this.utils.$encodeSingle(target, value, data)).to.eventually.equal(encodeSingle(target, value, data));
}); });
it('decodes single', async function () { it('decodes single', async function () {
@ -268,7 +268,7 @@ describe('ERC7579Utils', function () {
const value = 0x123; const value = 0x123;
const data = '0x12345678'; const data = '0x12345678';
expect(this.utils.$decodeSingle(encodeSingle(target, value, data))).to.eventually.deep.equal([ await expect(this.utils.$decodeSingle(encodeSingle(target, value, data))).to.eventually.deep.equal([
target.target, target.target,
value, value,
data, data,
@ -281,7 +281,7 @@ describe('ERC7579Utils', function () {
[this.anotherTarget, 0x456, '0x12345678'], [this.anotherTarget, 0x456, '0x12345678'],
]; ];
expect(this.utils.$encodeBatch(entries)).to.eventually.equal(encodeBatch(...entries)); await expect(this.utils.$encodeBatch(entries)).to.eventually.equal(encodeBatch(...entries));
}); });
it('decodes batch', async function () { it('decodes batch', async function () {
@ -290,63 +290,66 @@ describe('ERC7579Utils', function () {
[this.anotherTarget.target, 0x456, '0x12345678'], [this.anotherTarget.target, 0x456, '0x12345678'],
]; ];
expect(this.utils.$decodeBatch(encodeBatch(...entries))).to.eventually.deep.equal(entries); await expect(this.utils.$decodeBatch(encodeBatch(...entries))).to.eventually.deep.equal(entries);
}); });
it('encodes delegate', async function () { it('encodes delegate', async function () {
const target = this.target; const target = this.target;
const data = '0x12345678'; const data = '0x12345678';
expect(this.utils.$encodeDelegate(target, data)).to.eventually.equal(encodeDelegate(target, data)); await expect(this.utils.$encodeDelegate(target, data)).to.eventually.equal(encodeDelegate(target, data));
}); });
it('decodes delegate', async function () { it('decodes delegate', async function () {
const target = this.target; const target = this.target;
const data = '0x12345678'; const data = '0x12345678';
expect(this.utils.$decodeDelegate(encodeDelegate(target, data))).to.eventually.deep.equal([target.target, data]); await expect(this.utils.$decodeDelegate(encodeDelegate(target, data))).to.eventually.deep.equal([
target.target,
data,
]);
}); });
describe('global', function () { describe('global', function () {
describe('eqCallTypeGlobal', function () { describe('eqCallTypeGlobal', function () {
it('returns true if both call types are equal', async function () { it('returns true if both call types are equal', async function () {
expect(this.utilsGlobal.$eqCallTypeGlobal(CALL_TYPE_BATCH, CALL_TYPE_BATCH)).to.eventually.be.true; await expect(this.utilsGlobal.$eqCallTypeGlobal(CALL_TYPE_BATCH, CALL_TYPE_BATCH)).to.eventually.be.true;
}); });
it('returns false if both call types are different', async function () { it('returns false if both call types are different', async function () {
expect(this.utilsGlobal.$eqCallTypeGlobal(CALL_TYPE_CALL, CALL_TYPE_BATCH)).to.eventually.be.false; await expect(this.utilsGlobal.$eqCallTypeGlobal(CALL_TYPE_CALL, CALL_TYPE_BATCH)).to.eventually.be.false;
}); });
}); });
describe('eqExecTypeGlobal', function () { describe('eqExecTypeGlobal', function () {
it('returns true if both exec types are equal', async function () { it('returns true if both exec types are equal', async function () {
expect(this.utilsGlobal.$eqExecTypeGlobal(EXEC_TYPE_TRY, EXEC_TYPE_TRY)).to.eventually.be.true; await expect(this.utilsGlobal.$eqExecTypeGlobal(EXEC_TYPE_TRY, EXEC_TYPE_TRY)).to.eventually.be.true;
}); });
it('returns false if both exec types are different', async function () { it('returns false if both exec types are different', async function () {
expect(this.utilsGlobal.$eqExecTypeGlobal(EXEC_TYPE_DEFAULT, EXEC_TYPE_TRY)).to.eventually.be.false; await expect(this.utilsGlobal.$eqExecTypeGlobal(EXEC_TYPE_DEFAULT, EXEC_TYPE_TRY)).to.eventually.be.false;
}); });
}); });
describe('eqModeSelectorGlobal', function () { describe('eqModeSelectorGlobal', function () {
it('returns true if both selectors are equal', async function () { it('returns true if both selectors are equal', async function () {
expect(this.utilsGlobal.$eqModeSelectorGlobal('0x12345678', '0x12345678')).to.eventually.be.true; await expect(this.utilsGlobal.$eqModeSelectorGlobal('0x12345678', '0x12345678')).to.eventually.be.true;
}); });
it('returns false if both selectors are different', async function () { it('returns false if both selectors are different', async function () {
expect(this.utilsGlobal.$eqModeSelectorGlobal('0x12345678', '0x87654321')).to.eventually.be.false; await expect(this.utilsGlobal.$eqModeSelectorGlobal('0x12345678', '0x87654321')).to.eventually.be.false;
}); });
}); });
describe('eqModePayloadGlobal', function () { describe('eqModePayloadGlobal', function () {
it('returns true if both payloads are equal', async function () { it('returns true if both payloads are equal', async function () {
expect(this.utilsGlobal.$eqModePayloadGlobal(ethers.toBeHex(0, 22), ethers.toBeHex(0, 22))).to.eventually.be await expect(this.utilsGlobal.$eqModePayloadGlobal(ethers.toBeHex(0, 22), ethers.toBeHex(0, 22))).to.eventually
.true; .be.true;
}); });
it('returns false if both payloads are different', async function () { it('returns false if both payloads are different', async function () {
expect(this.utilsGlobal.$eqModePayloadGlobal(ethers.toBeHex(0, 22), ethers.toBeHex(1, 22))).to.eventually.be await expect(this.utilsGlobal.$eqModePayloadGlobal(ethers.toBeHex(0, 22), ethers.toBeHex(1, 22))).to.eventually
.false; .be.false;
}); });
}); });
}); });

@ -624,8 +624,8 @@ describe('Governor', function () {
await this.helper.connect(this.proposer).propose(); await this.helper.connect(this.proposer).propose();
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, 'GovernorUnableToCancel')
.withArgs(this.owner); .withArgs(this.proposal.id, this.owner);
}); });
it('after vote started', async function () { it('after vote started', async function () {
@ -633,12 +633,8 @@ describe('Governor', function () {
await this.helper.waitForSnapshot(1n); // snapshot + 1 block await this.helper.waitForSnapshot(1n); // snapshot + 1 block
await expect(this.helper.cancel('external')) await expect(this.helper.cancel('external'))
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnableToCancel')
.withArgs( .withArgs(this.proposal.id, this.owner);
this.proposal.id,
ProposalState.Active,
GovernorHelper.proposalStatesToBitMap([ProposalState.Pending]),
);
}); });
it('after vote', async function () { it('after vote', async function () {
@ -647,12 +643,8 @@ describe('Governor', function () {
await this.helper.connect(this.voter1).vote({ support: 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, 'GovernorUnableToCancel')
.withArgs( .withArgs(this.proposal.id, this.voter1);
this.proposal.id,
ProposalState.Active,
GovernorHelper.proposalStatesToBitMap([ProposalState.Pending]),
);
}); });
it('after deadline', async function () { it('after deadline', async function () {
@ -662,12 +654,8 @@ describe('Governor', function () {
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, 'GovernorUnableToCancel')
.withArgs( .withArgs(this.proposal.id, this.voter1);
this.proposal.id,
ProposalState.Succeeded,
GovernorHelper.proposalStatesToBitMap([ProposalState.Pending]),
);
}); });
it('after execution', async function () { it('after execution', async function () {
@ -678,12 +666,8 @@ describe('Governor', function () {
await this.helper.execute(); await this.helper.execute();
await expect(this.helper.cancel('external')) await expect(this.helper.cancel('external'))
.to.be.revertedWithCustomError(this.mock, 'GovernorUnexpectedProposalState') .to.be.revertedWithCustomError(this.mock, 'GovernorUnableToCancel')
.withArgs( .withArgs(this.proposal.id, this.voter1);
this.proposal.id,
ProposalState.Executed,
GovernorHelper.proposalStatesToBitMap([ProposalState.Pending]),
);
}); });
}); });
}); });

@ -264,7 +264,7 @@ describe('GovernorCountingOverridable', function () {
.to.emit(this.mock, 'OverrideVoteCast') .to.emit(this.mock, 'OverrideVoteCast')
.withArgs(this.voter1, this.helper.id, VoteType.Against, ethers.parseEther('10'), ''); .withArgs(this.voter1, this.helper.id, VoteType.Against, ethers.parseEther('10'), '');
await expect(this.mock.connect(this.voter1).castOverrideVote(this.helper.id, VoteType.Abstain, '')) await expect(this.mock.connect(this.voter1).castOverrideVote(this.helper.id, VoteType.Abstain, ''))
.to.be.revertedWithCustomError(this.mock, 'GovernorAlreadyOverridenVote') .to.be.revertedWithCustomError(this.mock, 'GovernorAlreadyOverriddenVote')
.withArgs(this.voter1.address); .withArgs(this.voter1.address);
}); });

@ -0,0 +1,132 @@
const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { impersonate } = require('../../helpers/account');
const { GovernorHelper } = require('../../helpers/governance');
const { ProposalState } = require('../../helpers/enums');
const TOKENS = [
{ Token: '$ERC20Votes', mode: 'blocknumber' },
{ Token: '$ERC20VotesTimestampMock', mode: 'timestamp' },
];
const name = 'Proposal Guardian Governor';
const version = '1';
const tokenName = 'MockToken';
const tokenSymbol = 'MTKN';
const tokenSupply = ethers.parseEther('100');
const votingDelay = 4n;
const votingPeriod = 16n;
const value = ethers.parseEther('1');
describe('GovernorProposalGuardian', function () {
for (const { Token, mode } of TOKENS) {
const fixture = async () => {
const [owner, proposer, guardian, voter1, voter2, voter3, voter4, other] = await ethers.getSigners();
const receiver = await ethers.deployContract('CallReceiverMock');
const token = await ethers.deployContract(Token, [tokenName, tokenSymbol, tokenName, version]);
const mock = await ethers.deployContract('$GovernorProposalGuardianMock', [
name, // name
votingDelay, // initialVotingDelay
votingPeriod, // initialVotingPeriod
0n, // initialProposalThreshold
token, // tokenAddress
10n, // quorumNumeratorValue
]);
await impersonate(mock.target);
await owner.sendTransaction({ to: mock, value });
await token.$_mint(owner, tokenSupply);
const helper = new GovernorHelper(mock, mode);
await helper.connect(owner).delegate({ token, to: voter1, value: ethers.parseEther('10') });
await helper.connect(owner).delegate({ token, to: voter2, value: ethers.parseEther('7') });
await helper.connect(owner).delegate({ token, to: voter3, value: ethers.parseEther('5') });
await helper.connect(owner).delegate({ token, to: voter4, value: ethers.parseEther('2') });
return { owner, proposer, guardian, voter1, voter2, voter3, voter4, other, receiver, token, mock, helper };
};
describe(`using ${Token}`, function () {
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
// default proposal
this.proposal = this.helper.setProposal(
[
{
target: this.receiver.target,
value,
data: this.receiver.interface.encodeFunctionData('mockFunction'),
},
],
'<proposal description>',
);
});
it('deployment check', async function () {
await expect(this.mock.name()).to.eventually.equal(name);
await expect(this.mock.token()).to.eventually.equal(this.token);
await expect(this.mock.votingDelay()).to.eventually.equal(votingDelay);
await expect(this.mock.votingPeriod()).to.eventually.equal(votingPeriod);
});
describe('set proposal guardian', function () {
it('from governance', async function () {
const governorSigner = await ethers.getSigner(this.mock.target);
await expect(this.mock.connect(governorSigner).setProposalGuardian(this.guardian))
.to.emit(this.mock, 'ProposalGuardianSet')
.withArgs(ethers.ZeroAddress, this.guardian);
await expect(this.mock.proposalGuardian()).to.eventually.equal(this.guardian);
});
it('from non-governance', async function () {
await expect(this.mock.connect(this.other).setProposalGuardian(this.guardian))
.to.be.revertedWithCustomError(this.mock, 'GovernorOnlyExecutor')
.withArgs(this.other);
});
});
it('cancel proposal during pending state from proposer when proposal guardian is non-zero', async function () {
await this.mock.$_setProposalGuardian(this.guardian);
await this.helper.connect(this.proposer).propose();
await expect(this.helper.connect(this.proposer).cancel())
.to.emit(this.mock, 'ProposalCanceled')
.withArgs(this.proposal.id);
});
describe('cancel proposal during active state', function () {
beforeEach(async function () {
await this.helper.connect(this.proposer).propose();
await this.helper.waitForSnapshot(1n);
await expect(this.mock.state(this.proposal.id)).to.eventually.equal(ProposalState.Active);
});
it('from proposal guardian', async function () {
await this.mock.$_setProposalGuardian(this.guardian);
await expect(this.helper.connect(this.guardian).cancel())
.to.emit(this.mock, 'ProposalCanceled')
.withArgs(this.proposal.id);
});
it('from proposer when proposal guardian is non-zero', async function () {
await this.mock.$_setProposalGuardian(this.guardian);
await expect(this.helper.connect(this.proposer).cancel())
.to.be.revertedWithCustomError(this.mock, 'GovernorUnableToCancel')
.withArgs(this.proposal.id, this.proposer);
});
it('from proposer when proposal guardian is zero', async function () {
await this.mock.$_setProposalGuardian(ethers.ZeroAddress);
await expect(this.helper.connect(this.proposer).cancel())
.to.emit(this.mock, 'ProposalCanceled')
.withArgs(this.proposal.id);
});
});
});
}
});

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save