From ddd22e06af734426a442977a411675770921b343 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 5 Jan 2022 21:21:37 +0100 Subject: [PATCH 1/3] make some function virtual --- CHANGELOG.md | 1 + contracts/access/AccessControl.sol | 6 +++--- contracts/access/AccessControlEnumerable.sol | 4 ++-- contracts/governance/Governor.sol | 2 +- contracts/token/ERC20/extensions/ERC20FlashMint.sol | 3 ++- contracts/token/ERC20/extensions/ERC20Votes.sol | 6 +++--- contracts/token/ERC20/extensions/ERC20VotesComp.sol | 4 ++-- contracts/token/ERC777/ERC777.sol | 2 +- package.json | 1 + 9 files changed, 16 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be54f9160b5..d7f4ad2c3ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * `ERC2771Context`: use immutable storage to store the forwarder address, no longer an issue since Solidity >=0.8.8 allows reading immutable variables in the constructor. ([#2917](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2917)) * `Base64`: add a library to parse bytes into base64 strings using `encode(bytes memory)` function, and provide examples to show how to use to build URL-safe `tokenURIs` ([#2884](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#2884)) * `ERC20`: reduce allowance before triggering transfer. + * Some more functions have been made virtual to customize them via overrides. In many cases this will not imply that other functions in the contract will automatically adapt to the overridden definitions. People who wish to override should consult the source code to understand the impact and if they need to override any additional functions to achieve the desired behavior. ## 4.4.1 (2021-12-14) diff --git a/contracts/access/AccessControl.sol b/contracts/access/AccessControl.sol index 95f53b3303d..70c81f6c268 100644 --- a/contracts/access/AccessControl.sol +++ b/contracts/access/AccessControl.sol @@ -81,7 +81,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 { /** * @dev Returns `true` if `account` has been granted `role`. */ - function hasRole(bytes32 role, address account) public view override returns (bool) { + function hasRole(bytes32 role, address account) public view virtual override returns (bool) { return _roles[role].members[account]; } @@ -92,7 +92,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 { * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ - function _checkRole(bytes32 role, address account) internal view { + function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert( string( @@ -113,7 +113,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 { * * To change a role's admin, use {_setRoleAdmin}. */ - function getRoleAdmin(bytes32 role) public view override returns (bytes32) { + function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { return _roles[role].adminRole; } diff --git a/contracts/access/AccessControlEnumerable.sol b/contracts/access/AccessControlEnumerable.sol index cb1c88b65f1..153981e1345 100644 --- a/contracts/access/AccessControlEnumerable.sol +++ b/contracts/access/AccessControlEnumerable.sol @@ -34,7 +34,7 @@ abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessCon * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] * for more information. */ - function getRoleMember(bytes32 role, uint256 index) public view override returns (address) { + function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { return _roleMembers[role].at(index); } @@ -42,7 +42,7 @@ abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessCon * @dev Returns the number of accounts that have `role`. Can be used * together with {getRoleMember} to enumerate all bearers of a role. */ - function getRoleMemberCount(bytes32 role) public view override returns (uint256) { + function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { return _roleMembers[role].length(); } diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index e2cdc9c8e75..19a930ddb5d 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -370,7 +370,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor { address target, uint256 value, bytes calldata data - ) external onlyGovernance { + ) external virtual onlyGovernance { Address.functionCallWithValue(target, data, value); } diff --git a/contracts/token/ERC20/extensions/ERC20FlashMint.sol b/contracts/token/ERC20/extensions/ERC20FlashMint.sol index da3780b25b0..f7b205bf7fe 100644 --- a/contracts/token/ERC20/extensions/ERC20FlashMint.sol +++ b/contracts/token/ERC20/extensions/ERC20FlashMint.sol @@ -23,7 +23,7 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { * @param token The address of the token that is requested. * @return The amont of token that can be loaned. */ - function maxFlashLoan(address token) public view override returns (uint256) { + function maxFlashLoan(address token) public view virtual override returns (uint256) { return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0; } @@ -62,6 +62,7 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { uint256 amount, bytes calldata data ) public virtual override returns (bool) { + require(amount <= maxFlashLoan(token), "ERC20FlashMint: amount exceeds maxFlashLoan"); uint256 fee = flashFee(token, amount); _mint(address(receiver), amount); require( diff --git a/contracts/token/ERC20/extensions/ERC20Votes.sol b/contracts/token/ERC20/extensions/ERC20Votes.sol index 90e9a251233..f6ae124d1c0 100644 --- a/contracts/token/ERC20/extensions/ERC20Votes.sol +++ b/contracts/token/ERC20/extensions/ERC20Votes.sol @@ -61,7 +61,7 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { /** * @dev Gets the current votes balance for `account` */ - function getVotes(address account) public view override returns (uint256) { + function getVotes(address account) public view virtual override returns (uint256) { uint256 pos = _checkpoints[account].length; return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes; } @@ -73,7 +73,7 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { * * - `blockNumber` must have been already mined */ - function getPastVotes(address account, uint256 blockNumber) public view override returns (uint256) { + function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { require(blockNumber < block.number, "ERC20Votes: block not yet mined"); return _checkpointsLookup(_checkpoints[account], blockNumber); } @@ -86,7 +86,7 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { * * - `blockNumber` must have been already mined */ - function getPastTotalSupply(uint256 blockNumber) public view override returns (uint256) { + function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) { require(blockNumber < block.number, "ERC20Votes: block not yet mined"); return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); } diff --git a/contracts/token/ERC20/extensions/ERC20VotesComp.sol b/contracts/token/ERC20/extensions/ERC20VotesComp.sol index 422318bb8e8..3c122797cfa 100644 --- a/contracts/token/ERC20/extensions/ERC20VotesComp.sol +++ b/contracts/token/ERC20/extensions/ERC20VotesComp.sol @@ -26,14 +26,14 @@ abstract contract ERC20VotesComp is ERC20Votes { /** * @dev Comp version of the {getVotes} accessor, with `uint96` return type. */ - function getCurrentVotes(address account) external view returns (uint96) { + function getCurrentVotes(address account) external view virtual returns (uint96) { return SafeCast.toUint96(getVotes(account)); } /** * @dev Comp version of the {getPastVotes} accessor, with `uint96` return type. */ - function getPriorVotes(address account, uint256 blockNumber) external view returns (uint96) { + function getPriorVotes(address account, uint256 blockNumber) external view virtual returns (uint96) { return SafeCast.toUint96(getPastVotes(account, blockNumber)); } diff --git a/contracts/token/ERC777/ERC777.sol b/contracts/token/ERC777/ERC777.sol index f89e1e0242c..ab8e7073f83 100644 --- a/contracts/token/ERC777/ERC777.sol +++ b/contracts/token/ERC777/ERC777.sol @@ -457,7 +457,7 @@ contract ERC777 is Context, IERC777, IERC20 { address holder, address spender, uint256 value - ) internal { + ) internal virtual { require(holder != address(0), "ERC777: approve from the zero address"); require(spender != address(0), "ERC777: approve to the zero address"); diff --git a/package.json b/package.json index 0b0abb33afa..bcdbdd976bf 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "version": "scripts/release/version.sh", "test": "hardhat test", "test:inheritance": "node scripts/inheritanceOrdering artifacts/build-info/*", + "test:virtual": "node scripts/virtualFunctions artifacts/build-info/*", "gas-report": "env ENABLE_GAS_REPORT=true npm run test" }, "repository": { From 34511c503d858365675b78f827baca404dd50a05 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 5 Jan 2022 21:25:04 +0100 Subject: [PATCH 2/3] cleanup package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index bcdbdd976bf..0b0abb33afa 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "version": "scripts/release/version.sh", "test": "hardhat test", "test:inheritance": "node scripts/inheritanceOrdering artifacts/build-info/*", - "test:virtual": "node scripts/virtualFunctions artifacts/build-info/*", "gas-report": "env ENABLE_GAS_REPORT=true npm run test" }, "repository": { From 000f6b23ca134540b85c10cb355ac6c369998b0c Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 5 Jan 2022 21:27:12 +0100 Subject: [PATCH 3/3] add Multicall:multicall --- contracts/utils/Multicall.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/utils/Multicall.sol b/contracts/utils/Multicall.sol index 59291748bf1..d1c974c57c2 100644 --- a/contracts/utils/Multicall.sol +++ b/contracts/utils/Multicall.sol @@ -14,7 +14,7 @@ abstract contract Multicall { /** * @dev Receives and executes a batch of function calls on this contract. */ - function multicall(bytes[] calldata data) external returns (bytes[] memory results) { + function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) { results = new bytes[](data.length); for (uint256 i = 0; i < data.length; i++) { results[i] = Address.functionDelegateCall(address(this), data[i]);