diff --git a/CHANGELOG.md b/CHANGELOG.md index d1983f07d10..872274bbae7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,7 +87,7 @@ ### Pragma changes -- Reduced pragma requirement of interface files +- Reduced pragma requirement of interface files. ### Changes by category diff --git a/contracts/access/manager/IAccessManager.sol b/contracts/access/manager/IAccessManager.sol index 749fe2654a3..7873da757e1 100644 --- a/contracts/access/manager/IAccessManager.sol +++ b/contracts/access/manager/IAccessManager.sol @@ -36,7 +36,7 @@ interface IAccessManager { * * NOTE: The meaning of the `since` argument depends on the `newMember` argument. * If the role is granted to a new member, the `since` argument indicates when the account becomes a member of the role, - * otherwise it indicates the execution delay for this account and roleId is updated. + * otherwise it indicates the timestamp when the execution delay update takes effect for this account and roleId. */ event RoleGranted(uint64 indexed roleId, address indexed account, uint32 delay, uint48 since, bool newMember); @@ -196,6 +196,7 @@ interface IAccessManager { * Requirements: * * - the caller must be a global admin + * - `roleId` must not be the `ADMIN_ROLE` or `PUBLIC_ROLE` * * Emits a {RoleLabel} event. */ @@ -254,6 +255,10 @@ interface IAccessManager { * Requirements: * * - the caller must be a global admin + * - `roleId` must not be the `ADMIN_ROLE` or `PUBLIC_ROLE` + * + * NOTE: Setting `admin` to the `PUBLIC_ROLE` is allowed, but the target `roleId` itself + * must not be a locked role (`ADMIN_ROLE` or `PUBLIC_ROLE`). * * Emits a {RoleAdminChanged} event */ @@ -265,6 +270,10 @@ interface IAccessManager { * Requirements: * * - the caller must be a global admin + * - `roleId` must not be the `ADMIN_ROLE` or `PUBLIC_ROLE` + * + * NOTE: Setting `guardian` to the `PUBLIC_ROLE` is allowed, but the target `roleId` itself + * must not be a locked role (`ADMIN_ROLE` or `PUBLIC_ROLE`). * * Emits a {RoleGuardianChanged} event */ @@ -276,6 +285,7 @@ interface IAccessManager { * Requirements: * * - the caller must be a global admin + * - `roleId` must not be the `PUBLIC_ROLE` * * Emits a {RoleGrantDelayChanged} event. */ diff --git a/contracts/account/extensions/draft-AccountERC7579.sol b/contracts/account/extensions/draft-AccountERC7579.sol index 2302d8359b6..7ba2b92bb4d 100644 --- a/contracts/account/extensions/draft-AccountERC7579.sol +++ b/contracts/account/extensions/draft-AccountERC7579.sol @@ -400,7 +400,7 @@ abstract contract AccountERC7579 is Account, IERC1271, IERC7579Execution, IERC75 * actual copy. However, this would require `_installModule` to get a calldata bytes object instead of a memory * bytes object. This would prevent calling `_installModule` from a contract constructor and would force the use * of external initializers. That may change in the future, as most accounts will probably be deployed as - * clones/proxy/ERC-7702 delegates and therefore rely on initializers anyway. + * clones/proxy/EIP-7702 delegates and therefore rely on initializers anyway. */ function _decodeFallbackData( bytes memory data diff --git a/contracts/account/utils/EIP7702Utils.sol b/contracts/account/utils/EIP7702Utils.sol index df9ca8acc81..974b5ef6642 100644 --- a/contracts/account/utils/EIP7702Utils.sol +++ b/contracts/account/utils/EIP7702Utils.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.20; /** * @dev Library with common EIP-7702 utility functions. * - * See https://eips.ethereum.org/EIPS/eip-7702[ERC-7702]. + * See https://eips.ethereum.org/EIPS/eip-7702[EIP-7702]. */ library EIP7702Utils { bytes3 internal constant EIP7702_PREFIX = 0xef0100; diff --git a/contracts/governance/TimelockController.sol b/contracts/governance/TimelockController.sol index 52aadcee19e..71e3d5f7cce 100644 --- a/contracts/governance/TimelockController.sol +++ b/contracts/governance/TimelockController.sol @@ -7,7 +7,6 @@ import {AccessControl} from "../access/AccessControl.sol"; import {ERC721Holder} from "../token/ERC721/utils/ERC721Holder.sol"; import {ERC1155Holder} from "../token/ERC1155/utils/ERC1155Holder.sol"; import {Address} from "../utils/Address.sol"; -import {IERC165} from "../utils/introspection/ERC165.sol"; /** * @dev Contract module which acts as a timelocked controller. When set as the @@ -155,7 +154,7 @@ contract TimelockController is AccessControl, ERC721Holder, ERC1155Holder { */ receive() external payable virtual {} - /// @inheritdoc IERC165 + /// @inheritdoc AccessControl function supportsInterface( bytes4 interfaceId ) public view virtual override(AccessControl, ERC1155Holder) returns (bool) { diff --git a/contracts/interfaces/draft-IERC7579.sol b/contracts/interfaces/draft-IERC7579.sol index 7688dbcc725..0755fa41836 100644 --- a/contracts/interfaces/draft-IERC7579.sol +++ b/contracts/interfaces/draft-IERC7579.sol @@ -116,7 +116,7 @@ struct Execution { interface IERC7579Execution { /** * @dev Executes a transaction on behalf of the account. - * @param mode The encoded execution mode of the transaction. See ModeLib.sol for details + * @param mode The encoded execution mode of the transaction. See account/utils/draft-ERC7579Utils.sol (Mode encoding via encodeMode/decodeMode) for details * @param executionCalldata The encoded execution call data * * MUST ensure adequate authorization control: e.g. onlyEntryPointOrSelf if used with ERC-4337 @@ -127,7 +127,7 @@ interface IERC7579Execution { /** * @dev Executes a transaction on behalf of the account. * This function is intended to be called by Executor Modules - * @param mode The encoded execution mode of the transaction. See ModeLib.sol for details + * @param mode The encoded execution mode of the transaction. See account/utils/draft-ERC7579Utils.sol (Mode encoding via encodeMode/decodeMode) for details * @param executionCalldata The encoded execution call data * @return returnData An array with the returned data of each executed subcall * diff --git a/contracts/metatx/ERC2771Context.sol b/contracts/metatx/ERC2771Context.sol index ce6eca82c0c..56c7aba8426 100644 --- a/contracts/metatx/ERC2771Context.sol +++ b/contracts/metatx/ERC2771Context.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** - * @dev Context variant with ERC-2771 support. + * @dev Context variant with ERC-2771 support. See {_msgSender} for the calldata format. * * WARNING: Avoid using this pattern in contracts that rely on a specific calldata length as they'll * be affected by any forwarder whose `msg.data` is suffixed with the `from` address according to the ERC-2771 diff --git a/contracts/mocks/DummyImplementation.sol b/contracts/mocks/DummyImplementation.sol index d4f10138bd1..443dea9a1e0 100644 --- a/contracts/mocks/DummyImplementation.sol +++ b/contracts/mocks/DummyImplementation.sol @@ -5,10 +5,6 @@ pragma solidity ^0.8.21; import {ERC1967Utils} from "../proxy/ERC1967/ERC1967Utils.sol"; import {StorageSlot} from "../utils/StorageSlot.sol"; -abstract contract Impl { - function version() public pure virtual returns (string memory); -} - contract DummyImplementation { uint256 public value; string public text; diff --git a/contracts/mocks/account/AccountMock.sol b/contracts/mocks/account/AccountMock.sol index b5e17bf42c7..ae526c8e817 100644 --- a/contracts/mocks/account/AccountMock.sol +++ b/contracts/mocks/account/AccountMock.sol @@ -81,7 +81,7 @@ abstract contract AccountWebAuthnMock is Account, SignerWebAuthn, ERC7739, ERC78 } } -abstract contract AccountERC7702Mock is Account, SignerEIP7702, ERC7739, ERC7821, ERC721Holder, ERC1155Holder { +abstract contract AccountEIP7702Mock is Account, SignerEIP7702, ERC7739, ERC7821, ERC721Holder, ERC1155Holder { /// @inheritdoc ERC7821 function _erc7821AuthorizedExecutor( address caller, @@ -92,7 +92,7 @@ abstract contract AccountERC7702Mock is Account, SignerEIP7702, ERC7739, ERC7821 } } -abstract contract AccountERC7702WithModulesMock is +abstract contract AccountEIP7702WithModulesMock is Account, AccountERC7579, SignerEIP7702, @@ -119,7 +119,7 @@ abstract contract AccountERC7702WithModulesMock is return erc7739magic == bytes4(0xffffffff) ? AccountERC7579.isValidSignature(hash, signature) : erc7739magic; } - /// @dev Enable signature using the ERC-7702 signer. + /// @dev Enable signature using the EIP-7702 signer. function _rawSignatureValidation( bytes32 hash, bytes calldata signature diff --git a/contracts/mocks/docs/ERC4626Fees.sol b/contracts/mocks/docs/ERC4626Fees.sol index b8ee33f3326..1924e22620e 100644 --- a/contracts/mocks/docs/ERC4626Fees.sol +++ b/contracts/mocks/docs/ERC4626Fees.sol @@ -45,7 +45,7 @@ abstract contract ERC4626Fees is ERC4626 { return assets - _feeOnTotal(assets, _exitFeeBasisPoints()); } - /// @dev Send entry fee to {_entryFeeRecipient}. See {IERC4626-_deposit}. + /// @dev Send entry fee to {_entryFeeRecipient}. See {ERC4626-_deposit}. function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual override { uint256 fee = _feeOnTotal(assets, _entryFeeBasisPoints()); address recipient = _entryFeeRecipient(); @@ -57,7 +57,7 @@ abstract contract ERC4626Fees is ERC4626 { } } - /// @dev Send exit fee to {_exitFeeRecipient}. See {IERC4626-_deposit}. + /// @dev Send exit fee to {_exitFeeRecipient}. See {ERC4626-_withdraw}. function _withdraw( address caller, address receiver, diff --git a/contracts/mocks/docs/account/MyAccountERC7702.sol b/contracts/mocks/docs/account/MyAccountEIP7702.sol similarity index 89% rename from contracts/mocks/docs/account/MyAccountERC7702.sol rename to contracts/mocks/docs/account/MyAccountEIP7702.sol index e28b578a0da..b7d8052c21d 100644 --- a/contracts/mocks/docs/account/MyAccountERC7702.sol +++ b/contracts/mocks/docs/account/MyAccountEIP7702.sol @@ -1,4 +1,4 @@ -// contracts/MyAccountERC7702.sol +// contracts/MyAccountEIP7702.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; @@ -8,7 +8,7 @@ import {ERC1155Holder} from "../../../token/ERC1155/utils/ERC1155Holder.sol"; import {ERC7821} from "../../../account/extensions/draft-ERC7821.sol"; import {SignerEIP7702} from "../../../utils/cryptography/signers/SignerEIP7702.sol"; -contract MyAccountERC7702 is Account, SignerEIP7702, ERC7821, ERC721Holder, ERC1155Holder { +contract MyAccountEIP7702 is Account, SignerEIP7702, ERC7821, ERC721Holder, ERC1155Holder { /// @dev Allows the entry point as an authorized executor. function _erc7821AuthorizedExecutor( address caller, diff --git a/contracts/mocks/proxy/ClashingImplementation.sol b/contracts/mocks/proxy/ClashingImplementation.sol index 43d5a34f24e..157feaa4636 100644 --- a/contracts/mocks/proxy/ClashingImplementation.sol +++ b/contracts/mocks/proxy/ClashingImplementation.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.20; /** - * @dev Implementation contract with a payable changeAdmin(address) function made to clash with - * TransparentUpgradeableProxy's to test correct functioning of the Transparent Proxy feature. + * @dev Implementation contract with a payable upgradeToAndCall(address,bytes) function made to clash with + * TransparentUpgradeableProxy's interface to test correct functioning of the Transparent Proxy pattern. */ contract ClashingImplementation { event ClashingImplementationCall(); diff --git a/contracts/proxy/ERC1967/ERC1967Utils.sol b/contracts/proxy/ERC1967/ERC1967Utils.sol index ffa77cc7da8..46e6f56b57e 100644 --- a/contracts/proxy/ERC1967/ERC1967Utils.sol +++ b/contracts/proxy/ERC1967/ERC1967Utils.sol @@ -86,7 +86,7 @@ library ERC1967Utils { * @dev Returns the current admin. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using - * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. + * the https://ethereum.org/developers/docs/apis/json-rpc/#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` */ function getAdmin() internal view returns (address) { diff --git a/contracts/proxy/README.adoc b/contracts/proxy/README.adoc index 1c4d0105c8b..54ec3aa8aa2 100644 --- a/contracts/proxy/README.adoc +++ b/contracts/proxy/README.adoc @@ -21,10 +21,10 @@ There are two alternative ways to add upgradeability to an ERC-1967 proxy. Their CAUTION: Using upgradeable proxies correctly and securely is a difficult task that requires deep knowledge of the proxy pattern, Solidity, and the EVM. Unless you want a lot of low level control, we recommend using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins] for Hardhat and Foundry. -A different family of proxies are beacon proxies. This pattern, popularized by Dharma, allows multiple proxies to be upgraded to a different implementation in a single transaction. +A different family of proxies is beacon proxies. This pattern, popularized by Dharma, allows multiple proxies to be upgraded to a different implementation in a single transaction. - {BeaconProxy}: A proxy that retrieves its implementation from a beacon contract. -- {UpgradeableBeacon}: A beacon contract with a built in admin that can upgrade the {BeaconProxy} pointing to it. +- {UpgradeableBeacon}: A beacon contract with a built-in admin that can upgrade the {BeaconProxy} pointing to it. In this pattern, the proxy contract doesn't hold the implementation address in storage like an ERC-1967 proxy. Instead, the address is stored in a separate beacon contract. The `upgrade` operations are sent to the beacon instead of to the proxy contract, and all proxies that follow that beacon are automatically upgraded. diff --git a/contracts/token/ERC20/extensions/ERC20FlashMint.sol b/contracts/token/ERC20/extensions/ERC20FlashMint.sol index 4d3a31f6df2..142d8494d56 100644 --- a/contracts/token/ERC20/extensions/ERC20FlashMint.sol +++ b/contracts/token/ERC20/extensions/ERC20FlashMint.sol @@ -68,14 +68,10 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { * @dev Returns the fee applied when doing flash loans. By default this * implementation has 0 fees. This function can be overloaded to make * the flash loan mechanism deflationary. - * @param token The token to be flash loaned. - * @param value The amount of tokens to be loaned. + * * @return The fees applied to the corresponding flash loan. */ - function _flashFee(address token, uint256 value) internal view virtual returns (uint256) { - // silence warning about unused variable without the addition of bytecode. - token; - value; + function _flashFee(address /*token*/, uint256 /*value*/) internal view virtual returns (uint256) { return 0; } diff --git a/contracts/token/ERC20/extensions/ERC4626.sol b/contracts/token/ERC20/extensions/ERC4626.sol index 93a84862b78..307b0b60a60 100644 --- a/contracts/token/ERC20/extensions/ERC4626.sol +++ b/contracts/token/ERC20/extensions/ERC4626.sol @@ -84,12 +84,12 @@ abstract contract ERC4626 is ERC20, IERC4626 { error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max); /** - * @dev Attempted to withdraw more assets than the max amount for `receiver`. + * @dev Attempted to withdraw more assets than the max amount for `owner`. */ error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max); /** - * @dev Attempted to redeem more shares than the max amount for `receiver`. + * @dev Attempted to redeem more shares than the max amount for `owner`. */ error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max); diff --git a/contracts/token/ERC721/extensions/ERC721Enumerable.sol b/contracts/token/ERC721/extensions/ERC721Enumerable.sol index 07e2202dab5..d258541d486 100644 --- a/contracts/token/ERC721/extensions/ERC721Enumerable.sol +++ b/contracts/token/ERC721/extensions/ERC721Enumerable.sol @@ -153,7 +153,8 @@ abstract contract ERC721Enumerable is ERC721, IERC721Enumerable { } /** - * See {ERC721-_increaseBalance}. We need that to account tokens that were minted in batch + * See {ERC721-_increaseBalance}. We need to forbid batch minting because the enumeration + * extension does not support it. */ function _increaseBalance(address account, uint128 amount) internal virtual override { if (amount > 0) { diff --git a/contracts/utils/LowLevelCall.sol b/contracts/utils/LowLevelCall.sol index 06276930231..160bc0121ab 100644 --- a/contracts/utils/LowLevelCall.sol +++ b/contracts/utils/LowLevelCall.sol @@ -15,7 +15,7 @@ library LowLevelCall { return callNoReturn(target, 0, data); } - /// @dev Same as {callNoReturn}, but allows to specify the value to be sent in the call. + /// @dev Same as {xref-LowLevelCall-callNoReturn-address-uint256-bytes-}[`callNoReturn`], but allows to specify the value to be sent in the call. function callNoReturn(address target, uint256 value, bytes memory data) internal returns (bool success) { assembly ("memory-safe") { success := call(gas(), target, value, add(data, 0x20), mload(data), 0x00, 0x00) @@ -34,7 +34,7 @@ library LowLevelCall { return callReturn64Bytes(target, 0, data); } - /// @dev Same as {callReturnBytes32Pair}, but allows to specify the value to be sent in the call. + /// @dev Same as {xref-LowLevelCall-callReturn64Bytes-address-uint256-bytes-}[`callReturn64Bytes`], but allows to specify the value to be sent in the call. function callReturn64Bytes( address target, uint256 value, diff --git a/contracts/utils/cryptography/SignatureChecker.sol b/contracts/utils/cryptography/SignatureChecker.sol index de619568990..f105a1a861e 100644 --- a/contracts/utils/cryptography/SignatureChecker.sol +++ b/contracts/utils/cryptography/SignatureChecker.sol @@ -73,8 +73,8 @@ library SignatureChecker { // Encoded calldata is : // [ 0x00 - 0x03 ] // [ 0x04 - 0x23 ] - // [ 0x24 - 0x44 ] (0x40) - // [ 0x44 - 0x64 ] + // [ 0x24 - 0x43 ] (0x40) + // [ 0x44 - 0x63 ] // [ 0x64 - ... ] let ptr := mload(0x40) mstore(ptr, selector) diff --git a/contracts/utils/cryptography/draft-ERC7739Utils.sol b/contracts/utils/cryptography/draft-ERC7739Utils.sol index 94fd1b6c0db..f3944136ea5 100644 --- a/contracts/utils/cryptography/draft-ERC7739Utils.sol +++ b/contracts/utils/cryptography/draft-ERC7739Utils.sol @@ -62,7 +62,6 @@ library ERC7739Utils { * - `contentsDescr` is a descriptor of the "contents" part of the EIP-712 type of the nested signature. * * NOTE: This function returns empty if the input format is invalid instead of reverting. - * data instead. */ function decodeTypedDataSig( bytes calldata encodedSignature @@ -99,7 +98,7 @@ library ERC7739Utils { * This struct hash must be combined with a domain separator, using {MessageHashUtils-toTypedDataHash} before * being verified/recovered. * - * This is used to simulates the `personal_sign` RPC method in the context of smart contracts. + * This is used to simulate the `personal_sign` RPC method in the context of smart contracts. */ function personalSignStructHash(bytes32 contents) internal pure returns (bytes32) { return Hashes.efficientKeccak256(PERSONAL_SIGN_TYPEHASH, contents); diff --git a/contracts/utils/cryptography/signers/SignerEIP7702.sol b/contracts/utils/cryptography/signers/SignerEIP7702.sol index d8459cc5656..50ba35f609e 100644 --- a/contracts/utils/cryptography/signers/SignerEIP7702.sol +++ b/contracts/utils/cryptography/signers/SignerEIP7702.sol @@ -7,7 +7,7 @@ import {AbstractSigner} from "./AbstractSigner.sol"; import {ECDSA} from "../ECDSA.sol"; /** - * @dev Implementation of {AbstractSigner} for implementation for an EOA. Useful for ERC-7702 accounts. + * @dev Implementation of {AbstractSigner} for implementation for an EOA. Useful for EIP-7702 accounts. * * @custom:stateless */ diff --git a/contracts/utils/cryptography/verifiers/ERC7913WebAuthnVerifier.sol b/contracts/utils/cryptography/verifiers/ERC7913WebAuthnVerifier.sol index 35428608485..a9d789ba6d8 100644 --- a/contracts/utils/cryptography/verifiers/ERC7913WebAuthnVerifier.sol +++ b/contracts/utils/cryptography/verifiers/ERC7913WebAuthnVerifier.sol @@ -13,7 +13,7 @@ import {IERC7913SignatureVerifier} from "../../../interfaces/IERC7913.sol"; * The key is expected to be a 64-byte concatenation of the P256 public key coordinates (qx || qy). * The signature is expected to be an abi-encoded {WebAuthn-WebAuthnAuth} struct. * - * Uses {WebAuthn-verifyMinimal} for signature verification, which performs the essential + * Uses {WebAuthn-verify} for signature verification, which performs the essential * WebAuthn checks: type validation, challenge matching, and cryptographic signature verification. * * NOTE: Wallets that may require default P256 validation may install a P256 verifier separately. diff --git a/contracts/utils/math/SafeCast.sol b/contracts/utils/math/SafeCast.sol index b345ede1e69..5b4f23cc98d 100644 --- a/contracts/utils/math/SafeCast.sol +++ b/contracts/utils/math/SafeCast.sol @@ -18,12 +18,12 @@ pragma solidity ^0.8.20; */ library SafeCast { /** - * @dev Value doesn't fit in an uint of `bits` size. + * @dev Value doesn't fit in a uint of `bits` size. */ error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value); /** - * @dev An int value doesn't fit in an uint of `bits` size. + * @dev An int value doesn't fit in a uint of `bits` size. */ error SafeCastOverflowedIntToUint(int256 value); @@ -33,7 +33,7 @@ library SafeCast { error SafeCastOverflowedIntDowncast(uint8 bits, int256 value); /** - * @dev An uint value doesn't fit in an int of `bits` size. + * @dev A uint value doesn't fit in an int of `bits` size. */ error SafeCastOverflowedUintToInt(uint256 value); diff --git a/contracts/utils/structs/CircularBuffer.sol b/contracts/utils/structs/CircularBuffer.sol index 8d7801d1f72..599dab188d2 100644 --- a/contracts/utils/structs/CircularBuffer.sol +++ b/contracts/utils/structs/CircularBuffer.sol @@ -34,9 +34,19 @@ import {Panic} from "../Panic.sol"; * * // Declare a buffer storage variable * CircularBuffer.Bytes32CircularBuffer private myBuffer; + * + * constructor() { + * myBuffer.setup(16); // Initialize the buffer with a non-zero fixed size (e.g., 16) + * } + * + * function pushValue(bytes32 value) external { + * myBuffer.push(value); // Safe to push because the buffer was initialized in the constructor + * } * } * ``` * + * NOTE: Make sure to call {setup} on your buffer during construction/initialization + * * _Available since v5.1._ */ library CircularBuffer { diff --git a/contracts/utils/structs/Heap.sol b/contracts/utils/structs/Heap.sol index b5f0730ef69..6dea14090fd 100644 --- a/contracts/utils/structs/Heap.sol +++ b/contracts/utils/structs/Heap.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.24; import {Math} from "../math/Math.sol"; -import {SafeCast} from "../math/SafeCast.sol"; import {Comparators} from "../Comparators.sol"; import {Arrays} from "../Arrays.sol"; import {Panic} from "../Panic.sol"; @@ -18,9 +17,10 @@ import {StorageSlot} from "../StorageSlot.sol"; * index i is the child of the node at index (i-1)/2 and the parent of nodes at index 2*i+1 and 2*i+2. Each node * stores an element of the heap. * - * The structure is ordered so that each node is bigger than its parent. An immediate consequence is that the - * highest priority value is the one at the root. This value can be looked up in constant time (O(1)) at - * `heap.tree[0]` + * The structure is ordered so that, per the comparator, each node has lower priority than its parent; as a + * consequence, the highest-priority value is at the root. This value can be looked up in constant time (O(1)) at + * `heap.tree[0]`. By default, the comparator is `Comparators.lt`, which treats smaller values as higher priority + * (min-heap). Using `Comparators.gt` yields a max-heap. * * The structure is designed to perform the following operations with the corresponding complexities: * @@ -40,7 +40,6 @@ import {StorageSlot} from "../StorageSlot.sol"; library Heap { using Arrays for *; using Math for *; - using SafeCast for *; /** * @dev Binary heap that supports values of type uint256. diff --git a/docs/modules/ROOT/pages/access-control.adoc b/docs/modules/ROOT/pages/access-control.adoc index d8b8cdb78c1..52472462da3 100644 --- a/docs/modules/ROOT/pages/access-control.adoc +++ b/docs/modules/ROOT/pages/access-control.adoc @@ -31,7 +31,7 @@ In this way, you can use _composability_ to add additional layers of access cont [[role-based-access-control]] == Role-Based Access Control -While the simplicity of _ownership_ can be useful for simple systems or quick prototyping, different levels of authorization are often needed. You may want for an account to have permission to ban users from a system, but not create new tokens. https://en.wikipedia.org/wiki/Role-based_access_control[_Role-Based Access Control (RBAC)_] offers flexibility in this regard. +While the simplicity of _ownership_ can be useful for simple systems or quick prototyping, different levels of authorization are often needed. You may want an account to have permission to ban users from a system, but not create new tokens. https://en.wikipedia.org/wiki/Role-based_access_control[_Role-Based Access Control (RBAC)_] offers flexibility in this regard. In essence, we will be defining multiple _roles_, each allowed to perform different sets of actions. An account may have, for example, 'moderator', 'minter' or 'admin' roles, which you will then check for instead of simply using `onlyOwner`. This check can be enforced through the `onlyRole` modifier. Separately, you will be able to define rules for how accounts can be granted a role, have it revoked, and more. @@ -74,7 +74,7 @@ Every role has an associated admin role, which grants permission to call the `gr This mechanism can be used to create complex permissioning structures resembling organizational charts, but it also provides an easy way to manage simpler applications. `AccessControl` includes a special role, called `DEFAULT_ADMIN_ROLE`, which acts as the **default admin role for all roles**. An account with this role will be able to manage any other role, unless `_setRoleAdmin` is used to select a new admin role. -Since it is the admin for all roles by default, and in fact it is also its own admin, this role carries significant risk. To mitigate this risk we provide xref:api:access.adoc#AccessControlDefaultAdminRules[`AccessControlDefaultAdminRules`], a recommended extension of `AccessControl` that adds a number of enforced security measures for this role: the admin is restricted to a single account, with a 2-step transfer procedure with a delay in between steps. +Since it is the admin for all roles by default, and in fact it is also its own admin, this role carries significant risk. To mitigate this risk we provide xref:api:access.adoc#AccessControlDefaultAdminRules[`AccessControlDefaultAdminRules`], a recommended extension of `AccessControl` that adds a number of enforced security measures for this role: the admin is restricted to a single account, with a 2-step transfer procedure with a delay between steps. Let's take a look at the ERC-20 token example, this time taking advantage of the default admin role: @@ -124,7 +124,7 @@ By default, the address that deployed the xref:api:governance.adoc#TimelockContr The first step in configuring the xref:api:governance.adoc#TimelockController[`TimelockController`] is to assign at least one proposer and one executor. These can be assigned during construction or later by anyone with the administrator role. These roles are not exclusive, meaning an account can have both roles. -Roles are managed using the xref:api:access.adoc#AccessControl[`AccessControl`] interface and the `bytes32` values for each role are accessible through the `ADMIN_ROLE`, `PROPOSER_ROLE` and `EXECUTOR_ROLE` constants. +Roles are managed using the xref:api:access.adoc#AccessControl[`AccessControl`] interface and the `bytes32` values for each role are accessible through the `DEFAULT_ADMIN_ROLE`, `PROPOSER_ROLE`, `EXECUTOR_ROLE`, and `CANCELLER_ROLE` constants. There is an additional feature built on top of `AccessControl`: giving the executor role to `address(0)` opens access to anyone to execute a proposal once the timelock has expired. This feature, while useful, should be used with caution. @@ -199,7 +199,7 @@ await manager.setTargetFunctionRole( ); ``` -Even though each role has its own list of function permissions, each role member (`address`) has an execution delay that will dictate how long the account should wait to execute a function that requires its role. Delayed operations must have the xref:api:access.adoc#AccessManager-schedule-address-bytes-uint48-[`schedule`] function called on them first in the AccessManager before they can be executed, either by calling to the target function or using the AccessManager's xref:api:access.adoc#AccessManager-execute-address-bytes-[`execute`] function. +Even though each role has its own list of function permissions, each role member (`address`) has an execution delay that will dictate how long the account should wait to execute a function that requires its role. Delayed operations must have the xref:api:access.adoc#AccessManager-schedule-address-bytes-uint48-[`schedule`] function called on them first in the AccessManager before they can be executed, either by calling the target function or using the AccessManager's xref:api:access.adoc#AccessManager-execute-address-bytes-[`execute`] function. Additionally, roles can have a granting delay that prevents adding members immediately. The AccessManager admins can set this grant delay as follows: @@ -293,3 +293,5 @@ await accessControl.connect(admin).grantRole(DEFAULT_ADMIN_ROLE, accessManager); await accessControl.connect(admin).renounceRole(DEFAULT_ADMIN_ROLE, admin); ``` + +NOTE: After migrating to AccessManager, the `msg.sender` in restricted functions will be the AccessManager contract itself through the xref:api:access.adoc#AccessManager-execute-address-bytes-[`execute`] function, not the original caller. This is a fundamental change in how access control works and may require updates to your contract logic or frontend integration. diff --git a/docs/modules/ROOT/pages/account-abstraction.adoc b/docs/modules/ROOT/pages/account-abstraction.adoc index 200586f5960..01cfaf4abb1 100644 --- a/docs/modules/ROOT/pages/account-abstraction.adoc +++ b/docs/modules/ROOT/pages/account-abstraction.adoc @@ -18,9 +18,9 @@ struct PackedUserOperation { uint256 nonce; bytes initCode; // concatenation of factory address and factoryData (or empty) bytes callData; - bytes32 accountGasLimits; // concatenation of verificationGas (16 bytes) and callGas (16 bytes) + bytes32 accountGasLimits; // concatenation of verificationGasLimit (16 bytes) and callGasLimit (16 bytes) uint256 preVerificationGas; - bytes32 gasFees; // concatenation of maxPriorityFee (16 bytes) and maxFeePerGas (16 bytes) + bytes32 gasFees; // concatenation of maxPriorityFeePerGas (16 bytes) and maxFeePerGas (16 bytes) bytes paymasterAndData; // concatenation of paymaster fields (or empty) bytes signature; } diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 5d42eaf36ac..5376cfd9606 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -2,11 +2,11 @@ OpenZeppelin provides a simple xref:api:account.adoc#Account[`Account`] implementation including only the basic logic to handle user operations in compliance with ERC-4337. Developers who want to build their own account can leverage it to bootstrap custom implementations. -User operations are validated using an xref:api:utils.adoc#AbstractSigner[`AbstractSigner`], which requires to implement the internal xref:api:utils.adoc#AbstractSigner-_rawSignatureValidation-bytes32-bytes-[`_rawSignatureValidation`] function, of which we offer a set of implementations to cover a wide customization range. This is the lowest-level signature validation layer and is used to wrap other validation methods like the Account's xref:api:account.adoc#Account-validateUserOp-struct-PackedUserOperation-bytes32-uint256-[`validateUserOp`]. +User operations are validated using an xref:api:utils.adoc#AbstractSigner[`AbstractSigner`], which requires implementing the internal xref:api:utils.adoc#AbstractSigner-_rawSignatureValidation-bytes32-bytes-[`_rawSignatureValidation`] function, of which we offer a set of implementations to cover a wide customization range. This is the lowest-level signature validation layer and is used to wrap other validation methods like the Account's xref:api:account.adoc#Account-validateUserOp-struct-PackedUserOperation-bytes32-uint256-[`validateUserOp`]. == Setting up an account -To setup an account, you can either start configuring it using our Wizard and selecting a predefined validation scheme, or bring your own logic and start by inheriting xref:api:account.adoc#Account[`Account`] from scratch. +To set up an account, you can either start configuring it using our Wizard and selecting a predefined validation scheme, or bring your own logic and start by inheriting xref:api:account.adoc#Account[`Account`] from scratch. ++++ @@ -25,7 +25,7 @@ Since the minimum requirement of xref:api:account.adoc#Account[`Account`] is to * xref:api:utils/cryptography.adoc#SignerRSA[`SignerRSA`]: Verifies signatures of traditional PKI systems and X.509 certificates. * xref:api:utils/cryptography.adoc#SignerEIP7702[`SignerEIP7702`]: Checks EOA signatures delegated to this signer using https://eips.ethereum.org/EIPS/eip-7702#set-code-transaction[EIP-7702 authorizations] * xref:api:utils/cryptography.adoc#SignerERC7913[`SignerERC7913`]: Verifies generalized signatures following https://eips.ethereum.org/EIPS/eip-7913[ERC-7913]. -* https://docs.openzeppelin.com/community-contracts/0.0.1/api/utils#SignerZKEmail[`SignerZKEmail`]: Enables email-based authentication for smart contracts using zero knowledge proofs of email authority signatures. +* https://docs.openzeppelin.com/community-contracts/0.0.1/api/utils#SignerZKEmail[`SignerZKEmail`]: Enables email-based authentication for smart contracts using zero-knowledge proofs of email authority signatures. * xref:api:utils/cryptography.adoc#MultiSignerERC7913[`MultiSignerERC7913`]: Allows using multiple ERC-7913 signers with a threshold-based signature verification system. * xref:api:utils/cryptography.adoc#MultiSignerERC7913Weighted[`MultiSignerERC7913Weighted`]: Overrides the threshold mechanism of xref:api:utils/cryptography.adoc#MultiSignerERC7913[`MultiSignerERC7913`], offering different weights per signer. @@ -153,7 +153,7 @@ const userOpData = encodeFunctionData({ == Bundle a `UserOperation` -xref:account-abstraction.adoc#useroperation[UserOperations] are a powerful abstraction layer that enable more sophisticated transaction capabilities compared to traditional Ethereum transactions. To get started, you'll need to an account, which you can get by xref:accounts.adoc#accounts_factory[deploying a factory] for your implementation. +xref:account-abstraction.adoc#useroperation[UserOperations] are a powerful abstraction layer that enable more sophisticated transaction capabilities compared to traditional Ethereum transactions. To get started, you'll need an account, which you can get by xref:accounts.adoc#accounts_factory[deploying a factory] for your implementation. === Preparing a UserOp @@ -343,7 +343,7 @@ https://eips.ethereum.org/EIPS/eip-7702[EIP-7702] lets EOAs delegate to smart co The signature verification stays compatible: delegated EOAs are treated as contracts using ERC-1271, making it easy to redelegate to a contract with ERC-1271 support with little overhead by reusing the validation mechanism of the account. -TIP: Learn more about delegating to an ERC-7702 account in our xref:eoa-delegation.adoc[EOA Delegation] section. +TIP: Learn more about delegating to an EIP-7702 account in our xref:eoa-delegation.adoc[EOA Delegation] section. === ERC-7579 Modules diff --git a/docs/modules/ROOT/pages/eoa-delegation.adoc b/docs/modules/ROOT/pages/eoa-delegation.adoc index 088b0a83f4e..05e8f396292 100644 --- a/docs/modules/ROOT/pages/eoa-delegation.adoc +++ b/docs/modules/ROOT/pages/eoa-delegation.adoc @@ -6,7 +6,7 @@ https://eips.ethereum.org/EIPS/eip-7702[EIP-7702] introduces a new transaction t * Sponsoring transactions for other users. * Implementing privilege de-escalation (e.g., sub-keys with limited permissions) -This section walks you through the process of delegating an EOA to a contract following https://eips.ethereum.org/EIPS/eip-7702[ERC-7702]. This allows you to use your EOA's private key to sign and execute operations with custom execution logic. Combined with https://eips.ethereum.org/EIPS/eip-4337[ERC-4337] infrastructure, users can achieve gas sponsoring through https://docs.openzeppelin.com/community-contracts/paymasters[paymasters]. +This section walks you through the process of delegating an EOA to a contract following https://eips.ethereum.org/EIPS/eip-7702[EIP-7702]. This allows you to use your EOA's private key to sign and execute operations with custom execution logic. Combined with https://eips.ethereum.org/EIPS/eip-4337[ERC-4337] infrastructure, users can achieve gas sponsoring through https://docs.openzeppelin.com/community-contracts/paymasters[paymasters]. == Delegating execution @@ -14,7 +14,7 @@ EIP-7702 enables EOAs to delegate their execution capabilities to smart contract [source,solidity] ---- -include::api:example$account/MyAccountERC7702.sol[] +include::api:example$account/MyAccountEIP7702.sol[] ---- TIP: Users can delegate to an instance of xref:api:account.adoc#ERC7821[`ERC-7821`] for a minimal batch executor that does not use ERC-4337 related code. @@ -52,7 +52,7 @@ const authorization = await eoaClient.signAuthorization({ }); ---- -NOTE: When implementing delegate contracts, ensure they require signatures that avoids replayability (e.g. a domain separator, nonce). +NOTE: When implementing delegate contracts, ensure they require signatures that avoid replayability (e.g. a domain separator, nonce). A poorly implemented delegate can allow a malicious actor to take near complete control over a signer's EOA. === Send a Set Code Transaction @@ -80,7 +80,7 @@ console.log(userOpReceipt); To remove the delegation and restore your EOA to its original state, you can send a `SET_CODE_TX_TYPE` transaction with an authorization tuple that points to the zero address (`0x0000000000000000000000000000000000000000`). This will clear the account's code and reset its code hash to the empty hash, however, be aware that it will not automatically clean the EOA storage. -When changing an account's delegation, ensure the newly delegated code is purposely designed and tested as an upgrade to the old one. To ensure safe migration between delegate contracts, namespaced storage that avoids accidental collisions following ERC-7201. +When changing an account's delegation, ensure the newly delegated code is purposely designed and tested as an upgrade to the old one. To ensure safe migration between delegate contracts, use namespaced storage that avoids accidental collisions following ERC-7201. WARNING: Updating the delegation designator may render your EOA unusable due to potential storage collisions. We recommend following similar practices to those of https://docs.openzeppelin.com/upgrades-plugins/writing-upgradeable[writing upgradeable smart contracts]. diff --git a/docs/modules/ROOT/pages/erc20-supply.adoc b/docs/modules/ROOT/pages/erc20-supply.adoc index 273cb32521a..062c6841168 100644 --- a/docs/modules/ROOT/pages/erc20-supply.adoc +++ b/docs/modules/ROOT/pages/erc20-supply.adoc @@ -34,12 +34,12 @@ contract ERC20FixedSupply is ERC20 { Encapsulating state like this makes it safer to extend contracts. For instance, in the first example we had to manually keep the `totalSupply` in sync with the modified balances, which is easy to forget. In fact, we omitted something else that is also easily forgotten: the `Transfer` event that is required by the standard, and which is relied on by some clients. The second example does not have this bug, because the internal `_mint` function takes care of it. -[[rewarding-miners]] -== Rewarding Miners +[[rewarding-block-proposers]] +== Rewarding Block Proposers The internal xref:api:token/ERC20.adoc#ERC20-_mint-address-uint256-[`_mint`] function is the key building block that allows us to write ERC-20 extensions that implement a supply mechanism. -The mechanism we will implement is a token reward for the miners that produce Ethereum blocks. In Solidity, we can access the address of the current block's miner in the global variable `block.coinbase`. We will mint a token reward to this address whenever someone calls the function `mintMinerReward()` on our token. The mechanism may sound silly, but you never know what kind of dynamic this might result in, and it's worth analyzing and experimenting with! +The mechanism we will implement is a token reward for the block proposer (the address that receives transaction fees). In Solidity, we can access this address using the global variable `block.coinbase`. Note that in Proof of Stake networks, `block.coinbase` refers to the fee recipient address, not a miner. We will mint a token reward to this address whenever someone calls the function `mintMinerReward()` on our token. The mechanism may sound silly, but you never know what kind of dynamic this might result in, and it's worth analyzing and experimenting with! [source,solidity] ---- @@ -59,7 +59,7 @@ As we can see, `_mint` makes it super easy to do this correctly. So far our supply mechanism was triggered manually, but `ERC20` also allows us to extend the core functionality of the token through the xref:api:token/ERC20.adoc#ERC20-_update-address-address-uint256-[`_update`] function. -Adding to the supply mechanism from the previous section, we can use this function to mint a miner reward for every token transfer that is included in the blockchain. +Adding to the supply mechanism from the previous section, we can use this function to mint a block proposer reward for every token transfer that is included in the blockchain. ```solidity include::api:example$ERC20WithAutoMinerReward.sol[] diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 41b16c9e6a6..57e6fffe92e 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -15,7 +15,7 @@ Here's what a contract for tokenized items might look like: include::api:example$token/ERC721/GameItem.sol[] ---- -The xref:api:token/ERC721.adoc#ERC721URIStorage[`ERC721URIStorage`] contract is an implementation of ERC-721 that includes the metadata standard extensions (xref:api:token/ERC721.adoc#IERC721Metadata[`IERC721Metadata`]) as well as a mechanism for per-token metadata. That's where the xref:api:token/ERC721.adoc#ERC721-_setTokenURI-uint256-string-[`_setTokenURI`] method comes from: we use it to store an item's metadata. +The xref:api:token/ERC721.adoc#ERC721URIStorage[`ERC721URIStorage`] contract is an implementation of ERC-721 that includes the metadata standard extensions (xref:api:token/ERC721.adoc#IERC721Metadata[`IERC721Metadata`]) as well as a mechanism for per-token metadata. That's where the xref:api:token/ERC721.adoc#ERC721URIStorage-_setTokenURI-uint256-string-[`_setTokenURI`] method comes from: we use it to store an item's metadata. Also note that, unlike ERC-20, ERC-721 lacks a `decimals` field, since each token is distinct and cannot be partitioned. diff --git a/docs/modules/ROOT/pages/governance.adoc b/docs/modules/ROOT/pages/governance.adoc index 6be35129494..1bf96e47190 100644 --- a/docs/modules/ROOT/pages/governance.adoc +++ b/docs/modules/ROOT/pages/governance.adoc @@ -90,8 +90,11 @@ TimelockController uses an AccessControl setup that we need to understand in ord - The Proposer role is in charge of queueing operations: this is the role the Governor instance should be granted, and it should likely be the only proposer in the system. - The Executor role is in charge of executing already available operations: we can assign this role to the special zero address to allow anyone to execute (if operations can be particularly time sensitive, the Governor should be made Executor instead). +- The Canceller role can cancel queued operations in the timelock: the Governor should hold this role for proposal cancellations to work after queuing. If the Governor is set as a proposer at deployment, it will automatically receive the Canceller role as well. - Lastly, there is the Admin role, which can grant and revoke the two previous roles: this is a very sensitive role that will be granted automatically to the timelock itself, and optionally to a second account, which can be used for ease of setup but should promptly renounce the role. +WARNING: Granting additional proposers or cancellers besides the Governor is risky. They can execute operations as the timelock (bypassing governance) and cancel approved proposals, potentially causing a denial of service against governance. + == Proposal Lifecycle Let’s walk through how to create and execute a proposal on our newly deployed Governor. @@ -201,7 +204,7 @@ The Governor will automatically detect the clock mode used by the token and adap pragma solidity ^0.8.20; import {Governor} from "@openzeppelin/contracts/governance/Governor.sol"; -import {GovernorCountingSimple} from "@openzeppelin/contracts/governance/compatibility/GovernorCountingSimple.sol"; +import {GovernorCountingSimple} from "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol"; import {GovernorVotes} from "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; import {GovernorVotesQuorumFraction} from "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; import {GovernorTimelockControl} from "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol"; diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index 42a172c356e..fe4cb6413e4 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -46,7 +46,7 @@ function _verify( bytes32 s, bytes32 qx, bytes32 qy -) internal pure returns (bool) { +) internal view returns (bool) { return data.verify(data, r, s, qx, qy); } ---- @@ -63,7 +63,7 @@ function _verify( bytes32 s, bytes32 qx, bytes32 qy -) internal pure returns (bool) { +) internal view returns (bool) { // Will only call the precompile at address(0x100) return data.verifyNative(data, r, s, qx, qy); } @@ -255,7 +255,7 @@ TIP: While working with different data types that might require casting, you can [[structures]] == Structures -Some use cases require more powerful data structures than arrays and mappings offered natively in Solidity. Contracts provides these libraries for enhanced data structure management: +Some use cases require more powerful data structures than arrays and mappings offered natively in Solidity. Contracts provide these libraries for enhanced data structure management: - xref:api:utils.adoc#BitMaps[`BitMaps`]: Store packed booleans in storage. - xref:api:utils.adoc#Checkpoints[`Checkpoints`]: Checkpoint values with built-in lookups. @@ -292,7 +292,7 @@ function push(bytes32 leaf) public /* onlyOwner */ { The library also supports custom hashing functions, which can be passed as an extra parameter to the xref:api:utils.adoc#MerkleTree-push-struct-MerkleTree-Bytes32PushTree-bytes32-[`push`] and xref:api:utils.adoc#MerkleTree-setup-struct-MerkleTree-Bytes32PushTree-uint8-bytes32-[`setup`] functions. -Using custom hashing functions is a sensitive operation. After setup, it requires to keep using the same hashing function for every new value pushed to the tree to avoid corrupting the tree. For this reason, it's a good practice to keep your hashing function static in your implementation contract as follows: +Using custom hashing functions is a sensitive operation. After setup, it requires continuing to use the same hashing function for every new value pushed to the tree to avoid corrupting the tree. For this reason, it's a good practice to keep your hashing function static in your implementation contract as follows: [source,solidity] ---- @@ -346,9 +346,9 @@ function replace(Uint256Heap storage self, uint256 newValue) internal returns (u === Packing -The storage in the EVM is shaped in chunks of 32 bytes, each of this chunks is known as a _slot_, and can hold multiple values together as long as these values don't exceed its size. These properties of the storage allow for a technique known as _packing_, that consists of placing values together on a single storage slot to reduce the costs associated to reading and writing to multiple slots instead of just one. +The storage in the EVM is shaped in chunks of 32 bytes, each of these chunks is known as a _slot_, and can hold multiple values together as long as these values don't exceed its size. These properties of the storage allow for a technique known as _packing_, that consists of placing values together on a single storage slot to reduce the costs associated to reading and writing to multiple slots instead of just one. -Commonly, developers pack values using structs that place values together so they fit better in storage. However, this approach requires to load such struct from either calldata or memory. Although sometimes necessary, it may be useful to pack values in a single slot and treat it as a packed value without involving calldata or memory. +Commonly, developers pack values using structs that place values together so they fit better in storage. However, this approach requires loading such struct from either calldata or memory. Although sometimes necessary, it may be useful to pack values in a single slot and treat it as a packed value without involving calldata or memory. The xref:api:utils.adoc#Packing[`Packing`] library is a set of utilities for packing values that fit in 32 bytes. The library includes 3 main functionalities: diff --git a/fv/harnesses/AccountHarness.sol b/fv/harnesses/AccountHarness.sol index 5051702103b..5cd1e538ba8 100644 --- a/fv/harnesses/AccountHarness.sol +++ b/fv/harnesses/AccountHarness.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.26; -import {AccountERC7702WithModulesMock} from "../patched/mocks/account/AccountMock.sol"; +import {AccountEIP7702WithModulesMock} from "../patched/mocks/account/AccountMock.sol"; import {EIP712} from "../patched/utils/cryptography/EIP712.sol"; import {EnumerableSet} from "../patched/utils/structs/EnumerableSet.sol"; -contract AccountHarness is AccountERC7702WithModulesMock { +contract AccountHarness is AccountEIP7702WithModulesMock { using EnumerableSet for EnumerableSet.AddressSet; constructor(string memory name, string memory version) EIP712(name, version) {} diff --git a/scripts/generate/templates/SafeCast.js b/scripts/generate/templates/SafeCast.js index 21000cf4a62..16d1ee8f7f1 100644 --- a/scripts/generate/templates/SafeCast.js +++ b/scripts/generate/templates/SafeCast.js @@ -22,12 +22,12 @@ pragma solidity ^0.8.20; const errors = `\ /** - * @dev Value doesn't fit in an uint of \`bits\` size. + * @dev Value doesn't fit in a uint of \`bits\` size. */ error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value); /** - * @dev An int value doesn't fit in an uint of \`bits\` size. + * @dev An int value doesn't fit in a uint of \`bits\` size. */ error SafeCastOverflowedIntToUint(int256 value); @@ -37,7 +37,7 @@ error SafeCastOverflowedIntToUint(int256 value); error SafeCastOverflowedIntDowncast(uint8 bits, int256 value); /** - * @dev An uint value doesn't fit in an int of \`bits\` size. + * @dev A uint value doesn't fit in an int of \`bits\` size. */ error SafeCastOverflowedUintToInt(uint256 value); `; diff --git a/scripts/minimize-pragma.js b/scripts/minimize-pragma.js index fc6606df3a7..a4cbf03aa77 100755 --- a/scripts/minimize-pragma.js +++ b/scripts/minimize-pragma.js @@ -37,7 +37,9 @@ const limit = pLimit(concurrency); const updatePragma = (file, pragma) => fs.writeFileSync( file, - fs.readFileSync(file, 'utf8').replace(/pragma solidity [><=^]*[0-9]+.[0-9]+.[0-9]+;/, `pragma solidity ${pragma};`), + fs + .readFileSync(file, 'utf8') + .replace(/pragma solidity [><=^]*[0-9]+\.[0-9]+\.[0-9]+;/, `pragma solidity ${pragma};`), 'utf8', ); diff --git a/scripts/upgradeable/upgradeable.patch b/scripts/upgradeable/upgradeable.patch index 7eea0ffc419..d3d4e76cf1a 100644 --- a/scripts/upgradeable/upgradeable.patch +++ b/scripts/upgradeable/upgradeable.patch @@ -367,30 +367,30 @@ index 304d1386a..a1cd63bee 100644 -@openzeppelin/contracts/=contracts/ +@openzeppelin/contracts-upgradeable/=contracts/ +@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ -diff --git a/test/account/AccountERC7702.test.js b/test/account/AccountERC7702.test.js +diff --git a/test/account/AccountEIP7702.test.js b/test/account/AccountEIP7702.test.js index f442d49af..8f22dc926 100644 ---- a/test/account/AccountERC7702.test.js -+++ b/test/account/AccountERC7702.test.js +--- a/test/account/AccountEIP7702.test.js ++++ b/test/account/AccountEIP7702.test.js @@ -26,8 +26,8 @@ async function fixture() { // domain cannot be fetched using getDomain(mock) before the mock is deployed const domain = { -- name: 'AccountERC7702Mock', +- name: 'AccountEIP7702Mock', - version: '1', + name: '', // Not initialized in the context of signer + version: '', // Not initialized in the context of signer chainId: entrypointDomain.chainId, verifyingContract: mock.address, }; -diff --git a/test/account/examples/AccountERC7702WithModulesMock.test.js b/test/account/examples/AccountERC7702WithModulesMock.test.js +diff --git a/test/account/examples/AccountEIP7702WithModulesMock.test.js b/test/account/examples/AccountEIP7702WithModulesMock.test.js index 8ceab19d1..c3f4194a6 100644 ---- a/test/account/examples/AccountERC7702WithModulesMock.test.js -+++ b/test/account/examples/AccountERC7702WithModulesMock.test.js +--- a/test/account/examples/AccountEIP7702WithModulesMock.test.js ++++ b/test/account/examples/AccountEIP7702WithModulesMock.test.js @@ -36,8 +36,8 @@ async function fixture() { // domain cannot be fetched using getDomain(mock) before the mock is deployed const domain = { -- name: 'AccountERC7702WithModulesMock', +- name: 'AccountEIP7702WithModulesMock', - version: '1', + name: '', // Not initialized in the context of signer + version: '', // Not initialized in the context of signer diff --git a/test/access/manager/AccessManaged.test.js b/test/access/manager/AccessManaged.test.js index d666b5e6dcc..d69b568ff4c 100644 --- a/test/access/manager/AccessManaged.test.js +++ b/test/access/manager/AccessManaged.test.js @@ -65,7 +65,7 @@ describe('AccessManaged', function () { describe('when role is granted with execution delay', function () { beforeEach(async function () { - const executionDelay = 911n; + const executionDelay = 911n; // Arbitrary delay for testing execution delay functionality await this.authority.$_grantRole(this.role, this.roleMember, 0, executionDelay); }); diff --git a/test/account/AccountERC7702.t.sol b/test/account/AccountEIP7702.t.sol similarity index 91% rename from test/account/AccountERC7702.t.sol rename to test/account/AccountEIP7702.t.sol index c8c685ffb3a..b14ef17c5c4 100644 --- a/test/account/AccountERC7702.t.sol +++ b/test/account/AccountEIP7702.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; -import {AccountERC7702Mock} from "@openzeppelin/contracts/mocks/account/AccountMock.sol"; +import {AccountEIP7702Mock} from "@openzeppelin/contracts/mocks/account/AccountMock.sol"; import {CallReceiverMock} from "@openzeppelin/contracts/mocks/CallReceiverMock.sol"; import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; import { @@ -17,11 +17,11 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {PackedUserOperation} from "@openzeppelin/contracts/interfaces/draft-IERC4337.sol"; import {ERC7821} from "@openzeppelin/contracts/account/extensions/draft-ERC7821.sol"; -contract AccountERC7702MockConstructor is AccountERC7702Mock { +contract AccountEIP7702MockConstructor is AccountEIP7702Mock { constructor() EIP712("MyAccount", "1") {} } -contract AccountERC7702Test is Test { +contract AccountEIP7702Test is Test { using ERC7579Utils for *; using ERC4337Utils for PackedUserOperation; using Strings for *; @@ -33,7 +33,7 @@ contract AccountERC7702Test is Test { // ERC-4337 signer uint256 private _signerPrivateKey; - AccountERC7702MockConstructor private _signer; + AccountEIP7702MockConstructor private _signer; function setUp() public { // Deploy target contract @@ -41,11 +41,11 @@ contract AccountERC7702Test is Test { // Setup signer _signerPrivateKey = 0x1234; - _signer = AccountERC7702MockConstructor(payable(vm.addr(_signerPrivateKey))); + _signer = AccountEIP7702MockConstructor(payable(vm.addr(_signerPrivateKey))); vm.deal(address(_signer), MAX_ETH); // Sign and attach delegation - vm.signAndAttachDelegation(address(new AccountERC7702MockConstructor()), _signerPrivateKey); + vm.signAndAttachDelegation(address(new AccountEIP7702MockConstructor()), _signerPrivateKey); // Setup entrypoint address entrypoint = address(ERC4337Utils.ENTRYPOINT_V08); diff --git a/test/account/AccountERC7702.test.js b/test/account/AccountEIP7702.test.js similarity index 90% rename from test/account/AccountERC7702.test.js rename to test/account/AccountEIP7702.test.js index f442d49af41..e9b2bdef15a 100644 --- a/test/account/AccountERC7702.test.js +++ b/test/account/AccountEIP7702.test.js @@ -19,14 +19,14 @@ async function fixture() { // ERC-4337 account const helper = new ERC4337Helper(); - const mock = await helper.newAccount('$AccountERC7702Mock', ['AccountERC7702Mock', '1'], { erc7702signer: signer }); + const mock = await helper.newAccount('$AccountEIP7702Mock', ['AccountEIP7702Mock', '1'], { eip7702signer: signer }); // ERC-4337 Entrypoint domain const entrypointDomain = await getDomain(predeploy.entrypoint.v08); // domain cannot be fetched using getDomain(mock) before the mock is deployed const domain = { - name: 'AccountERC7702Mock', + name: 'AccountEIP7702Mock', version: '1', chainId: entrypointDomain.chainId, verifyingContract: mock.address, @@ -40,7 +40,7 @@ async function fixture() { return { helper, mock, domain, signer, target, beneficiary, other, signUserOp }; } -describe('AccountERC7702', function () { +describe('AccountEIP7702', function () { beforeEach(async function () { Object.assign(this, await loadFixture(fixture)); }); diff --git a/test/account/examples/AccountERC7702WithModulesMock.test.js b/test/account/examples/AccountEIP7702WithModulesMock.test.js similarity index 91% rename from test/account/examples/AccountERC7702WithModulesMock.test.js rename to test/account/examples/AccountEIP7702WithModulesMock.test.js index 8ceab19d1bd..8de7dfeeb02 100644 --- a/test/account/examples/AccountERC7702WithModulesMock.test.js +++ b/test/account/examples/AccountEIP7702WithModulesMock.test.js @@ -27,8 +27,8 @@ async function fixture() { // ERC-4337 account const helper = new ERC4337Helper(); - const mock = await helper.newAccount('$AccountERC7702WithModulesMock', ['AccountERC7702WithModulesMock', '1'], { - erc7702signer: eoa, + const mock = await helper.newAccount('$AccountEIP7702WithModulesMock', ['AccountEIP7702WithModulesMock', '1'], { + eip7702signer: eoa, }); // ERC-4337 Entrypoint domain @@ -36,7 +36,7 @@ async function fixture() { // domain cannot be fetched using getDomain(mock) before the mock is deployed const domain = { - name: 'AccountERC7702WithModulesMock', + name: 'AccountEIP7702WithModulesMock', version: '1', chainId: entrypointDomain.chainId, verifyingContract: mock.address, @@ -45,12 +45,12 @@ async function fixture() { return { helper, validator, mock, domain, entrypointDomain, eoa, target, anotherTarget, beneficiary, other }; } -describe('AccountERC7702WithModules: ERC-7702 account with ERC-7579 modules supports', function () { +describe('AccountEIP7702WithModules: EIP-7702 account with ERC-7579 modules supports', function () { beforeEach(async function () { Object.assign(this, await loadFixture(fixture)); }); - describe('using ERC-7702 signer', function () { + describe('using EIP-7702 signer', function () { beforeEach(async function () { this.signer = this.eoa; this.signUserOp = userOp => @@ -86,7 +86,7 @@ describe('AccountERC7702WithModules: ERC-7702 account with ERC-7579 modules supp // Use the first 20 bytes from the nonce key (24 bytes) to identify the validator module this.userOp = { nonce: ethers.zeroPadBytes(ethers.hexlify(this.validator.target), 32) }; - // Deploy (using ERC-7702) and add the validator module using EOA + // Deploy (using EIP-7702) and add the validator module using EOA await this.mock.deploy(); await this.mock.connect(this.eoa).installModule(MODULE_TYPE_VALIDATOR, this.validator, this.signer.address); }); diff --git a/test/governance/extensions/GovernorNoncesKeyed.test.js b/test/governance/extensions/GovernorNoncesKeyed.test.js index e2cbd8ff07d..955af4bf79c 100644 --- a/test/governance/extensions/GovernorNoncesKeyed.test.js +++ b/test/governance/extensions/GovernorNoncesKeyed.test.js @@ -91,11 +91,10 @@ describe('GovernorNoncesKeyed', function () { const maskedProposalId = BigInt(this.helper.id) & (2n ** 192n - 1n); - this.getNonce = async address => { - return await (nonceType === 'default' + this.getNonce = address => + nonceType === 'default' ? this.mock.nonces(address) - : this.mock['nonces(address,uint192)'](address, maskedProposalId)); - }; + : this.mock['nonces(address,uint192)'](address, maskedProposalId); }); it('votes with an EOA signature', async function () { diff --git a/test/helpers/erc4337.js b/test/helpers/erc4337.js index 2451fd361f9..847834980e7 100644 --- a/test/helpers/erc4337.js +++ b/test/helpers/erc4337.js @@ -109,11 +109,11 @@ class ERC4337Helper { const accountFactory = await ethers.getContractFactory(name); - if (params.erc7702signer) { + if (params.eip7702signer) { const delegate = await accountFactory.deploy(...extraArgs); - const instance = await params.erc7702signer.getAddress().then(address => accountFactory.attach(address)); - const authorization = await params.erc7702signer.authorize({ address: delegate.target }); - return new ERC7702SmartAccount(instance, authorization, env); + const instance = await params.eip7702signer.getAddress().then(address => accountFactory.attach(address)); + const authorization = await params.eip7702signer.authorize({ address: delegate.target }); + return new EIP7702SmartAccount(instance, authorization, env); } else { const initCode = await accountFactory .getDeployTransaction(...extraArgs) @@ -163,7 +163,7 @@ class SmartAccount extends ethers.BaseContract { } } -class ERC7702SmartAccount extends SmartAccount { +class EIP7702SmartAccount extends SmartAccount { constructor(instance, authorization, env) { super(instance, undefined, env); this.authorization = authorization; diff --git a/test/utils/Arrays.test.js b/test/utils/Arrays.test.js index c3bee1492c9..8a4bcb0162b 100644 --- a/test/utils/Arrays.test.js +++ b/test/utils/Arrays.test.js @@ -9,13 +9,13 @@ const { TYPES } = require('../../scripts/generate/templates/Arrays.opts'); // See https://en.cppreference.com/w/cpp/algorithm/lower_bound const lowerBound = (array, value) => { const i = array.findIndex(element => value <= element); - return i == -1 ? array.length : i; + return i === -1 ? array.length : i; }; // See https://en.cppreference.com/w/cpp/algorithm/upper_bound const upperBound = (array, value) => { const i = array.findIndex(element => value < element); - return i == -1 ? array.length : i; + return i === -1 ? array.length : i; }; const bigintSign = x => (x > 0n ? 1 : x < 0n ? -1 : 0); diff --git a/test/utils/structs/Heap.test.js b/test/utils/structs/Heap.test.js index 6d751205c7a..0c6581d68fc 100644 --- a/test/utils/structs/Heap.test.js +++ b/test/utils/structs/Heap.test.js @@ -62,18 +62,18 @@ describe('Heap', function () { for (const { op, value } of [ { op: 'insert', value: 712 }, // [712] { op: 'insert', value: 20 }, // [20, 712] - { op: 'insert', value: 4337 }, // [20, 712, 4437] - { op: 'pop' }, // 20, [712, 4437] - { op: 'insert', value: 1559 }, // [712, 1559, 4437] - { op: 'insert', value: 165 }, // [165, 712, 1559, 4437] - { op: 'insert', value: 155 }, // [155, 165, 712, 1559, 4437] - { op: 'insert', value: 7702 }, // [155, 165, 712, 1559, 4437, 7702] - { op: 'pop' }, // 155, [165, 712, 1559, 4437, 7702] - { op: 'replace', value: 721 }, // 165, [712, 721, 1559, 4437, 7702] - { op: 'pop' }, // 712, [721, 1559, 4437, 7702] - { op: 'pop' }, // 721, [1559, 4437, 7702] - { op: 'pop' }, // 1559, [4437, 7702] - { op: 'pop' }, // 4437, [7702] + { op: 'insert', value: 4337 }, // [20, 712, 4337] + { op: 'pop' }, // 20, [712, 4337] + { op: 'insert', value: 1559 }, // [712, 1559, 4337] + { op: 'insert', value: 165 }, // [165, 712, 1559, 4337] + { op: 'insert', value: 155 }, // [155, 165, 712, 1559, 4337] + { op: 'insert', value: 7702 }, // [155, 165, 712, 1559, 4337, 7702] + { op: 'pop' }, // 155, [165, 712, 1559, 4337, 7702] + { op: 'replace', value: 721 }, // 165, [712, 721, 1559, 4337, 7702] + { op: 'pop' }, // 712, [721, 1559, 4337, 7702] + { op: 'pop' }, // 721, [1559, 4337, 7702] + { op: 'pop' }, // 1559, [4337, 7702] + { op: 'pop' }, // 4337, [7702] { op: 'pop' }, // 7702, [] { op: 'pop' }, // panic { op: 'replace', value: '1363' }, // panic diff --git a/test/utils/types/Time.test.js b/test/utils/types/Time.test.js index 3ab6fefa8d1..17baacfd110 100644 --- a/test/utils/types/Time.test.js +++ b/test/utils/types/Time.test.js @@ -6,8 +6,8 @@ const { product } = require('../../helpers/iterate'); const { max } = require('../../helpers/math'); const time = require('../../helpers/time'); -const MAX_UINT32 = 1n << (32n - 1n); -const MAX_UINT48 = 1n << (48n - 1n); +const MAX_UINT32 = (1n << 32n) - 1n; +const MAX_UINT48 = (1n << 48n) - 1n; const SOME_VALUES = [0n, 1n, 2n, 15n, 16n, 17n, 42n]; const asUint = (value, size) => {