Skip to content

Commit

Permalink
Merge branch 'master' into feat/ERC-7540
Browse files Browse the repository at this point in the history
  • Loading branch information
DeeJayElly authored Jan 28, 2025
2 parents 891b3a0 + 0d0e4aa commit 6135402
Show file tree
Hide file tree
Showing 32 changed files with 287 additions and 94 deletions.
5 changes: 5 additions & 0 deletions .changeset/pretty-lobsters-tan.md
Original file line number Diff line number Diff line change
@@ -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.
3 changes: 2 additions & 1 deletion .github/actions/gas-compare/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ runs:
shell: bash
- name: Save report
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: gasreport
overwrite: true
path: ${{ inputs.out_report }}
3 changes: 2 additions & 1 deletion .github/actions/storage-layout/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ runs:
shell: bash
- name: Save artifacts
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: layout
overwrite: true
path: ${{ inputs.out_layout }}
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ This version comes with changes to the custom error identifiers. Contracts previ
### 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.
- `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

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
* [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.

Expand Down
2 changes: 1 addition & 1 deletion RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ OpenZeppelin Contracts uses a fully automated release process that takes care of

## 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

Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ Note as well that the Solidity language itself only guarantees security updates

## 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.
2 changes: 1 addition & 1 deletion audits/2017-03.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The git commit hash we evaluated is:

# 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

Expand Down
4 changes: 2 additions & 2 deletions certora/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

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

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**
> 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

Expand Down
2 changes: 1 addition & 1 deletion certora/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ function writeEntry(spec, contract, success, url) {
formatRow(
spec,
contract,
success ? ':x:' : ':heavy_check_mark:',
success ? ':heavy_check_mark:' : ':x:',
url ? `[link](${url?.replace('/output/', '/jobStatus/')})` : 'error',
url ? `[link](${url})` : 'error',
),
Expand Down
4 changes: 2 additions & 2 deletions certora/specs/AccessControlDefaultAdminRules.spec
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -188,7 +188,7 @@ rule noDefaultAdminChange(env e, method f, calldataarg args) {
f.selector == sig:acceptDefaultAdminTransfer().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";
}

/*
Expand Down
6 changes: 3 additions & 3 deletions certora/specs/ERC20FlashMint.spec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ methods {
*/
ghost mapping(address => mathint) trackedMintAmount;
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) {
if (from == 0 && to == 0) { assert(false); } // defensive
Expand All @@ -28,7 +28,7 @@ function specUpdate(address from, address to, uint256 amount) {
} else if (to == 0) {
trackedBurnAmount[from] = amount;
} else {
trackedTransferedAmount[from][to] = amount;
trackedTransferredAmount[from][to] = amount;
}
}

Expand All @@ -51,5 +51,5 @@ rule checkMintAndBurn(env e) {

assert trackedMintAmount[receiver] == to_mathint(amount);
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);
}
2 changes: 1 addition & 1 deletion contracts/finance/VestingWallet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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)
* 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.
*
* NOTE: Since the wallet is {Ownable}, and ownership can be transferred, it is possible to sell unvested tokens.
Expand Down
16 changes: 11 additions & 5 deletions contracts/governance/Governor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
uint256 proposalId = getProposalId(targets, values, calldatas, descriptionHash);

// public cancel restrictions (on top of existing _cancel restrictions).
_validateStateBitmap(proposalId, _encodeStateBitmap(ProposalState.Pending));
if (_msgSender() != proposalProposer(proposalId)) {
revert GovernorOnlyProposer(_msgSender());
}
address caller = _msgSender();
if (!_validateCancel(proposalId, caller)) revert GovernorUnableToCancel(proposalId, caller);

return _cancel(targets, values, calldatas, descriptionHash);
}
Expand Down Expand Up @@ -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
*/
Expand Down
10 changes: 5 additions & 5 deletions contracts/governance/IGovernor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ interface IGovernor is IERC165, IERC6372 {
*/
error GovernorDisabledDeposit();

/**
* @dev The `account` is not a proposer.
*/
error GovernorOnlyProposer(address account);

/**
* @dev The `account` is not the governance executor.
*/
Expand Down Expand Up @@ -112,6 +107,11 @@ interface IGovernor is IERC165, IERC6372 {
*/
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.
*/
Expand Down
4 changes: 4 additions & 0 deletions contracts/governance/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.

* {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:

* <<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.
Expand Down Expand Up @@ -88,6 +90,8 @@ NOTE: Functions of the `Governor` contract do not include access control. If you

{{GovernorStorage}}

{{GovernorProposalGuardian}}

== Utils

{{Votes}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ abstract contract GovernorCountingFractional is Governor {
*
* `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
* 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
* remaining votes in a single operation using the traditional "bravo" vote.
*/
Expand Down
57 changes: 57 additions & 0 deletions contracts/governance/extensions/GovernorProposalGuardian.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}
2 changes: 1 addition & 1 deletion contracts/interfaces/IERC1271.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @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);
}
4 changes: 3 additions & 1 deletion contracts/interfaces/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,12 @@ are useful to interact with third party contracts that implement them.

{{IERC4626}}

{{IERC5313}}
{{IERC4906}}

{{IERC5267}}

{{IERC5313}}

{{IERC5805}}

{{IERC6372}}
Expand Down
27 changes: 27 additions & 0 deletions contracts/mocks/governance/GovernorProposalGuardianMock.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}
2 changes: 1 addition & 1 deletion contracts/token/ERC721/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[.readme-notice]
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].

Expand Down
2 changes: 1 addition & 1 deletion contracts/token/common/ERC2981.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ abstract contract ERC2981 is IERC2981, ERC165 {
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);

Expand Down
Loading

0 comments on commit 6135402

Please sign in to comment.