Compare commits
164 Commits
v4.9.4
...
audit-v5.0
| Author | SHA1 | Date | |
|---|---|---|---|
| b5a3e693e7 | |||
| 736091afc4 | |||
| 9bb8008c23 | |||
| 1169bb1e51 | |||
| 54a235f895 | |||
| b81bec4552 | |||
| f715365ec4 | |||
| 21716722ad | |||
| d39df78f6c | |||
| 5ae630684a | |||
| 48cc8a92f5 | |||
| fa680739e9 | |||
| 00cbf5a236 | |||
| d6b63a48ba | |||
| f631d8a5f0 | |||
| aed5720a01 | |||
| 02ea01765a | |||
| 9445f96223 | |||
| 7c02b5cab2 | |||
| 7222a31d54 | |||
| 28d9ac2bdb | |||
| 19293f3ecd | |||
| f347b410cf | |||
| 21bb89ef5b | |||
| 121be5dd09 | |||
| 9cf873ea14 | |||
| 84db204a41 | |||
| a55af77c75 | |||
| 4d4a509b1f | |||
| b6c5abbde5 | |||
| 0abf18f305 | |||
| 921ac49ccb | |||
| 8b72e20e32 | |||
| 24ebff5ae9 | |||
| 3d0edbecf1 | |||
| cd981f6521 | |||
| 2a4396c9dd | |||
| 4bac6fa310 | |||
| e47b53bce4 | |||
| 5229b75785 | |||
| 6d74b91388 | |||
| f5bf7233cb | |||
| 0053ee040a | |||
| 996168f1f1 | |||
| 7ccea54dc1 | |||
| 6bf68a41d1 | |||
| 3fe28e19af | |||
| 3ff9b42ff5 | |||
| 63bfab1a0c | |||
| 90163661df | |||
| e3adf91e50 | |||
| 6e21422737 | |||
| bb64458928 | |||
| 06861dce54 | |||
| 37270eb08a | |||
| 621b867b1a | |||
| 04342118dc | |||
| 874c2d3c02 | |||
| d6a8b2ccd7 | |||
| 023894deef | |||
| f29307cfe0 | |||
| 8cab922347 | |||
| cb4bf950df | |||
| c44c220254 | |||
| da89c438f1 | |||
| a7a94c7746 | |||
| 1e0e4e20bb | |||
| 6ddacdbde8 | |||
| dac2457a80 | |||
| b66c77a1fc | |||
| 9fa550c62f | |||
| 2271e2c58d | |||
| 365aca6d60 | |||
| 1d0dbcf9ab | |||
| 1f4e33fb72 | |||
| fc19a7947c | |||
| c95a445130 | |||
| 1a77a508f9 | |||
| 002a7c8812 | |||
| cd48b3eab3 | |||
| 6724873895 | |||
| c014c8f148 | |||
| ff85c7b0eb | |||
| 05ef6924ac | |||
| 2477534260 | |||
| ac5480e7ca | |||
| ef103f37e4 | |||
| 5cc1ea0a39 | |||
| 7cc2cbfeb5 | |||
| 604025400f | |||
| d6c7cee321 | |||
| fe08f58c69 | |||
| b425a72240 | |||
| 08fd777f6d | |||
| e73f90fa9d | |||
| cc04263170 | |||
| 1d5bcd04e7 | |||
| d9474327a4 | |||
| df2778f38e | |||
| 4fd2f8be33 | |||
| 85696d80ad | |||
| 6c14de4f0c | |||
| 253bfa68c2 | |||
| ffceb3cd98 | |||
| eecd5e15c7 | |||
| 2d1da295e6 | |||
| 3902a410f1 | |||
| 5cef83d2c7 | |||
| 30256fa838 | |||
| 2ee1da12c4 | |||
| 4c713f8cea | |||
| 7bb5592ad5 | |||
| 15c5c71795 | |||
| 4448c13c3c | |||
| 5420879d9b | |||
| 25edd3cd62 | |||
| 238d17cab9 | |||
| 5955d8e85b | |||
| 09329f8a18 | |||
| cbc6145f5f | |||
| 13d5e0466a | |||
| 7e814a3074 | |||
| a1d57bac50 | |||
| 11d65442b3 | |||
| dfef6a68ee | |||
| 0f10efe232 | |||
| 96b95592c3 | |||
| 8de6eba8a3 | |||
| 1642b6639b | |||
| 3ec4307c8a | |||
| f355bd3a2a | |||
| 3e1b25a5cf | |||
| f7b236dca8 | |||
| e5dbc7435e | |||
| 9a2e4cb3a7 | |||
| d095542fa4 | |||
| dff520afae | |||
| a522187b50 | |||
| 7f5e91062e | |||
| fa112be682 | |||
| 2d05db171a | |||
| e919d96ff2 | |||
| 54c31ad98b | |||
| 5ad74c6d10 | |||
| c5d040beb9 | |||
| 30c3c6c16e | |||
| 147dc89054 | |||
| 7c6e289782 | |||
| d210847e28 | |||
| a290e13099 | |||
| 97bba5adaa | |||
| fbbaf3365b | |||
| 4147005b0c | |||
| 81dbe643a0 | |||
| 9cbe8b340c | |||
| 3c80a42866 | |||
| e2d2ebc8fc | |||
| bc8f442d00 | |||
| 4a9db80cb9 | |||
| 887985413c | |||
| b18cf4ba42 | |||
| 39a752e398 | |||
| 6ff283bebc | |||
| 1cf4db9615 |
5
.changeset/afraid-walls-smell.md
Normal file
5
.changeset/afraid-walls-smell.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC1155Receiver`: Removed in favor of `ERC1155Holder`.
|
||||
5
.changeset/angry-ties-switch.md
Normal file
5
.changeset/angry-ties-switch.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`TimelockController`: Changed the role architecture to use `DEFAULT_ADMIN_ROLE` as the admin for all roles, instead of the bespoke `TIMELOCK_ADMIN_ROLE` that was used previously. This aligns with the general recommendation for `AccessControl` and makes the addition of new roles easier. Accordingly, the `admin` parameter and timelock will now be granted `DEFAULT_ADMIN_ROLE` instead of `TIMELOCK_ADMIN_ROLE`. ([#3799](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3799))
|
||||
4
.changeset/big-plums-cover.md
Normal file
4
.changeset/big-plums-cover.md
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
Use `abi.encodeCall` in place of `abi.encodeWithSelector` and `abi.encodeWithSignature` for improved type-checking of parameters
|
||||
5
.changeset/blue-horses-do.md
Normal file
5
.changeset/blue-horses-do.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC2771Forwarder`: Added `deadline` for expiring transactions, batching, and more secure handling of `msg.value`.
|
||||
5
.changeset/blue-scissors-design.md
Normal file
5
.changeset/blue-scissors-design.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`Math`: Make `ceilDiv` to revert on 0 division even if the numerator is 0
|
||||
5
.changeset/brave-lobsters-punch.md
Normal file
5
.changeset/brave-lobsters-punch.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Governor`: Refactored internals to implement common queuing logic in the core module of the Governor. Added `queue` and `_queueOperations` functions that act at different levels. Modules that implement queuing via timelocks are expected to override `_queueOperations` to implement the timelock-specific logic. Added `_executeOperations` as the equivalent for execution.
|
||||
5
.changeset/bright-tomatoes-sing.md
Normal file
5
.changeset/bright-tomatoes-sing.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC20`, `ERC1155`: Deleted `_beforeTokenTransfer` and `_afterTokenTransfer` hooks, added a new internal `_update` function for customizations, and refactored all extensions using those hooks to use `_update` instead. ([#3838](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3838), [#3876](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3876))
|
||||
5
.changeset/chilled-spiders-attack.md
Normal file
5
.changeset/chilled-spiders-attack.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC1155Supply`: add a `totalSupply()` function that returns the total amount of token circulating, this change will restrict the total tokens minted across all ids to 2\*\*256-1 .
|
||||
5
.changeset/clever-pumas-beg.md
Normal file
5
.changeset/clever-pumas-beg.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Ownable`: Add an `initialOwner` parameter to the constructor, making the ownership initialization explicit.
|
||||
5
.changeset/eight-peaches-guess.md
Normal file
5
.changeset/eight-peaches-guess.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`Proxy`: Removed redundant `receive` function.
|
||||
5
.changeset/eighty-crabs-listen.md
Normal file
5
.changeset/eighty-crabs-listen.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
Optimize `Strings.equal`
|
||||
5
.changeset/empty-taxis-kiss.md
Normal file
5
.changeset/empty-taxis-kiss.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`UUPSUpgradeable`, `TransparentUpgradeableProxy` and `ProxyAdmin`: Removed `upgradeTo` and `upgrade` functions, and made `upgradeToAndCall` and `upgradeAndCall` ignore the data argument if it is empty. It is no longer possible to invoke the receive function (or send value with empty data) along with an upgrade.
|
||||
5
.changeset/fifty-owls-retire.md
Normal file
5
.changeset/fifty-owls-retire.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Address`: Removed the ability to customize error messages. A common custom error is always used if the underlying revert reason cannot be bubbled up.
|
||||
7
.changeset/flat-bottles-wonder.md
Normal file
7
.changeset/flat-bottles-wonder.md
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
Replace some uses of `abi.encodePacked` with clearer alternatives (e.g. `bytes.concat`, `string.concat`). (#4504)[https://github.com/OpenZeppelin/openzeppelin-contracts/pull/4504]
|
||||
|
||||
pr: #4296
|
||||
5
.changeset/fluffy-countries-buy.md
Normal file
5
.changeset/fluffy-countries-buy.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`Arrays`: Optimize `findUpperBound` by removing redundant SLOAD.
|
||||
5
.changeset/four-adults-knock.md
Normal file
5
.changeset/four-adults-knock.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`ECDSA`: Use unchecked arithmetic for the `tryRecover` function that receives the `r` and `vs` short-signature fields separately.
|
||||
5
.changeset/fresh-birds-kiss.md
Normal file
5
.changeset/fresh-birds-kiss.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Checkpoints`: library moved from `utils` to `utils/structs`
|
||||
5
.changeset/grumpy-bulldogs-call.md
Normal file
5
.changeset/grumpy-bulldogs-call.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Governor`: Optimized use of storage for proposal data
|
||||
5
.changeset/grumpy-worms-tease.md
Normal file
5
.changeset/grumpy-worms-tease.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC1967Utils`: Refactor the `ERC1967Upgrade` abstract contract as a library.
|
||||
5
.changeset/happy-falcons-walk.md
Normal file
5
.changeset/happy-falcons-walk.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`TransparentUpgradeableProxy`: Admin is now stored in an immutable variable (set during construction) to avoid unnecessary storage reads on every proxy call. This removed the ability to ever change the admin. Transfer of the upgrade capability is exclusively handled through the ownership of the `ProxyAdmin`.
|
||||
5
.changeset/healthy-gorillas-applaud.md
Normal file
5
.changeset/healthy-gorillas-applaud.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`VestingWallet`: Use `Ownable` instead of an immutable `beneficiary`.
|
||||
5
.changeset/heavy-drinks-fail.md
Normal file
5
.changeset/heavy-drinks-fail.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC20`: Remove `Approval` event previously emitted in `transferFrom` to indicate that part of the allowance was consumed. With this change, allowances are no longer reconstructible from events. See the code for guidelines on how to re-enable this event if needed.
|
||||
5
.changeset/hip-beds-provide.md
Normal file
5
.changeset/hip-beds-provide.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
Move the logic to validate ERC-1822 during an upgrade from `ERC1967Utils` to `UUPSUpgradeable`.
|
||||
5
.changeset/hip-goats-fail.md
Normal file
5
.changeset/hip-goats-fail.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`VestingWallet`: Fix revert during 1 second time window when duration is 0.
|
||||
5
.changeset/hot-coins-judge.md
Normal file
5
.changeset/hot-coins-judge.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`Arrays`: Add `unsafeMemoryAccess` helpers to read from a memory array without checking the length.
|
||||
5
.changeset/hot-dingos-kiss.md
Normal file
5
.changeset/hot-dingos-kiss.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`MessageHashUtils`: Add a new library for creating message digest to be used along with signing or recovery such as ECDSA or ERC-1271. These functions are moved from the `ECDSA` library.
|
||||
5
.changeset/hot-plums-approve.md
Normal file
5
.changeset/hot-plums-approve.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`GovernorTimelockControl`: Clean up timelock id on execution for gas refund.
|
||||
5
.changeset/little-falcons-build.md
Normal file
5
.changeset/little-falcons-build.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`EIP712`: Add internal getters for the name and version strings
|
||||
5
.changeset/loud-shrimps-play.md
Normal file
5
.changeset/loud-shrimps-play.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`TimelockController`: Add a state getter that returns an `OperationState` enum.
|
||||
5
.changeset/lovely-geckos-hide.md
Normal file
5
.changeset/lovely-geckos-hide.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
Replace revert strings and require statements with custom errors.
|
||||
5
.changeset/mean-walls-watch.md
Normal file
5
.changeset/mean-walls-watch.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Nonces`: Added a new contract to keep track of user nonces. Used for signatures in `ERC20Permit`, `ERC20Votes`, and `ERC721Votes`. ([#3816](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3816))
|
||||
6
.changeset/mighty-donuts-smile.md
Normal file
6
.changeset/mighty-donuts-smile.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`Governor`: Add validation in ERC1155 and ERC721 receiver hooks to ensure Governor is the executor.
|
||||
|
||||
5
.changeset/orange-apes-draw.md
Normal file
5
.changeset/orange-apes-draw.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
Switched to using explicit Solidity import statements. Some previously available symbols may now have to be separately imported.
|
||||
5
.changeset/pink-suns-mix.md
Normal file
5
.changeset/pink-suns-mix.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`Math`: Optimized stack operations in `mulDiv`.
|
||||
5
.changeset/popular-deers-raise.md
Normal file
5
.changeset/popular-deers-raise.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Governor`: Add support for casting votes with ERC-1271 signatures by using a `bytes memory signature` instead of `r`, `s` and `v` arguments in the `castVoteBySig` and `castVoteWithReasonAndParamsBySig` functions.
|
||||
5
.changeset/proud-seals-complain.md
Normal file
5
.changeset/proud-seals-complain.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`BeaconProxy`: Use an immutable variable to store the address of the beacon. It is no longer possible for a `BeaconProxy` to upgrade by changing to another beacon.
|
||||
5
.changeset/purple-cats-cheer.md
Normal file
5
.changeset/purple-cats-cheer.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`GovernorTimelockControl`: Add the Governor instance address as part of the TimelockController operation `salt` to avoid operation id collisions between governors using the same TimelockController.
|
||||
5
.changeset/quiet-trainers-kick.md
Normal file
5
.changeset/quiet-trainers-kick.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`AccessManager`: Added a new contract for managing access control of complex systems in a consolidated location.
|
||||
5
.changeset/red-dots-fold.md
Normal file
5
.changeset/red-dots-fold.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
Overrides are now used internally for a number of functions that were previously hardcoded to their default implementation in certain locations: `ERC1155Supply.totalSupply`, `ERC721.ownerOf`, `ERC721.balanceOf` and `ERC721.totalSupply` in `ERC721Enumerable`, `ERC20.totalSupply` in `ERC20FlashMint`, and `ERC1967._getImplementation` in `ERC1967Proxy`.
|
||||
5
.changeset/rotten-insects-wash.md
Normal file
5
.changeset/rotten-insects-wash.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ProxyAdmin`: Removed `getProxyAdmin` and `getProxyImplementation` getters. ([#3820](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3820))
|
||||
5
.changeset/serious-books-lie.md
Normal file
5
.changeset/serious-books-lie.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`ERC1155`: Optimize array allocation.
|
||||
5
.changeset/short-eels-enjoy.md
Normal file
5
.changeset/short-eels-enjoy.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
Bump minimum compiler version required to 0.8.20
|
||||
7
.changeset/silly-bees-beam.md
Normal file
7
.changeset/silly-bees-beam.md
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC20Votes`: Changed internal vote accounting to reusable `Votes` module previously used by `ERC721Votes`. Removed implicit `ERC20Permit` inheritance. Note that the `DOMAIN_SEPARATOR` getter was previously guaranteed to be available for `ERC20Votes` contracts, but is no longer available unless `ERC20Permit` is explicitly used; ERC-5267 support is included in `ERC20Votes` with `EIP712` and is recommended as an alternative.
|
||||
|
||||
pr: #3816
|
||||
5
.changeset/sixty-numbers-reply.md
Normal file
5
.changeset/sixty-numbers-reply.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Governor`: Add `voter` and `nonce` parameters in signed ballots, to avoid forging signatures for random addresses, prevent signature replay, and allow invalidating signatures. Add `voter` as a new parameter in the `castVoteBySig` and `castVoteWithReasonAndParamsBySig` functions.
|
||||
5
.changeset/slimy-penguins-attack.md
Normal file
5
.changeset/slimy-penguins-attack.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`TransparentUpgradeableProxy`: Removed `admin` and `implementation` getters, which were only callable by the proxy owner and thus not very useful. ([#3820](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3820))
|
||||
5
.changeset/smooth-books-wink.md
Normal file
5
.changeset/smooth-books-wink.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC1155`: Remove check for address zero in `balanceOf`.
|
||||
5
.changeset/spicy-sheep-eat.md
Normal file
5
.changeset/spicy-sheep-eat.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`access`: Move `AccessControl` extensions to a dedicated directory.
|
||||
5
.changeset/spotty-hotels-type.md
Normal file
5
.changeset/spotty-hotels-type.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`ERC721Consecutive`: Add a `_firstConsecutiveId` internal function that can be overridden to change the id of the first token minted through `_mintConsecutive`.
|
||||
5
.changeset/strong-poems-thank.md
Normal file
5
.changeset/strong-poems-thank.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`DoubleEndedQueue`: refactor internal structure to use `uint128` instead of `int128`. This has no effect on the library interface.
|
||||
5
.changeset/swift-bags-divide.md
Normal file
5
.changeset/swift-bags-divide.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`Governor`: Add a mechanism to restrict the address of the proposer using a suffix in the description.
|
||||
5
.changeset/swift-numbers-cry.md
Normal file
5
.changeset/swift-numbers-cry.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`Governor`, `Initializable`, and `UUPSUpgradeable`: Use internal functions in modifiers to optimize bytecode size.
|
||||
5
.changeset/tasty-tomatoes-turn.md
Normal file
5
.changeset/tasty-tomatoes-turn.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Strings`: Rename `toString(int256)` to `toStringSigned(int256)`.
|
||||
5
.changeset/tender-shirts-turn.md
Normal file
5
.changeset/tender-shirts-turn.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`BeaconProxy`: Reject value in initialization unless a payable function is explicitly invoked.
|
||||
5
.changeset/thin-camels-matter.md
Normal file
5
.changeset/thin-camels-matter.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`ERC1155`: Bubble errors triggered in the `onERC1155Received` and `onERC1155BatchReceived` hooks.
|
||||
5
.changeset/tough-drinks-hammer.md
Normal file
5
.changeset/tough-drinks-hammer.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`ERC1155`: Optimize array accesses by skipping bounds checking when unnecessary.
|
||||
5
.changeset/two-wasps-punch.md
Normal file
5
.changeset/two-wasps-punch.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`AccessControl`: Add a boolean return value to the internal `_grantRole` and `_revokeRole` functions indicating whether the role was granted or revoked.
|
||||
5
.changeset/unlucky-beans-obey.md
Normal file
5
.changeset/unlucky-beans-obey.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`ERC2771Context`: Return the forwarder address whenever the `msg.data` of a call originating from a trusted forwarder is not long enough to contain the request signer address (i.e. `msg.data.length` is less than 20 bytes), as specified by ERC-2771.
|
||||
5
.changeset/violet-dancers-cough.md
Normal file
5
.changeset/violet-dancers-cough.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
Remove the `override` specifier from functions that only override a single interface function.
|
||||
5
.changeset/warm-guests-rule.md
Normal file
5
.changeset/warm-guests-rule.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`ERC2771Context`: Prevent revert in `_msgData()` when a call originating from a trusted forwarder is not long enough to contain the request signer address (i.e. `msg.data.length` is less than 20 bytes). Return the full calldata in that case.
|
||||
5
.changeset/wild-beds-visit.md
Normal file
5
.changeset/wild-beds-visit.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`GovernorStorage`: Added a new governor extension that stores the proposal details in storage, with an interface that operates on `proposalId`, as well as proposal enumerability. This replaces the old `GovernorCompatibilityBravo` module.
|
||||
5
.changeset/wild-rockets-rush.md
Normal file
5
.changeset/wild-rockets-rush.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Math`: Renamed members of `Rounding` enum, and added a new rounding mode for "away from zero".
|
||||
5
.changeset/wild-windows-trade.md
Normal file
5
.changeset/wild-windows-trade.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`SafeERC20`: Refactor `safeDecreaseAllowance` and `safeIncreaseAllowance` to support USDT-like tokens.
|
||||
6
.github/workflows/checks.yml
vendored
6
.github/workflows/checks.yml
vendored
@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- next-v*
|
||||
- release-v*
|
||||
pull_request: {}
|
||||
workflow_dispatch: {}
|
||||
@ -41,6 +42,7 @@ jobs:
|
||||
run: npm run test:generation
|
||||
- name: Compare gas costs
|
||||
uses: ./.github/actions/gas-compare
|
||||
if: github.base_ref == 'master'
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
|
||||
@ -62,6 +64,7 @@ jobs:
|
||||
run: npm run test:inheritance
|
||||
- name: Check storage layout
|
||||
uses: ./.github/actions/storage-layout
|
||||
if: github.base_ref == 'master'
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
|
||||
@ -105,7 +108,8 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run CodeSpell
|
||||
uses: codespell-project/actions-codespell@v1.0
|
||||
uses: codespell-project/actions-codespell@v2.0
|
||||
with:
|
||||
check_hidden: true
|
||||
check_filenames: true
|
||||
skip: package-lock.json,*.pdf
|
||||
|
||||
2
.github/workflows/formal-verification.yml
vendored
2
.github/workflows/formal-verification.yml
vendored
@ -12,7 +12,7 @@ on:
|
||||
env:
|
||||
PIP_VERSION: '3.10'
|
||||
JAVA_VERSION: '11'
|
||||
SOLC_VERSION: '0.8.19'
|
||||
SOLC_VERSION: '0.8.20'
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
{
|
||||
"rules": {
|
||||
"no-unused-vars": "error",
|
||||
"const-name-snakecase": "error",
|
||||
"contract-name-camelcase": "error",
|
||||
"event-name-camelcase": "error",
|
||||
"func-name-mixedcase": "error",
|
||||
"func-param-name-mixedcase": "error",
|
||||
"modifier-name-mixedcase": "error",
|
||||
"private-vars-leading-underscore": "error",
|
||||
"var-name-mixedcase": "error",
|
||||
"imports-on-top": "error"
|
||||
}
|
||||
}
|
||||
64
CHANGELOG.md
64
CHANGELOG.md
@ -1,15 +1,67 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 4.9.4 (2023-12-07)
|
||||
> **Warning** Version 5.0 is under active development and should not be used. Install the releases from npm or use the version tags in the repository.
|
||||
|
||||
- `ERC2771Context` and `Context`: Introduce a `_contextPrefixLength()` getter, used to trim extra information appended to `msg.data`.
|
||||
- `Multicall`: Make aware of non-canonical context (i.e. `msg.sender` is not `_msgSender()`), allowing compatibility with `ERC2771Context`.
|
||||
### Removals
|
||||
|
||||
## 4.9.3 (2023-07-28)
|
||||
The following contracts, libraries and functions were removed:
|
||||
|
||||
- `ERC2771Context`: Return the forwarder address whenever the `msg.data` of a call originating from a trusted forwarder is not long enough to contain the request signer address (i.e. `msg.data.length` is less than 20 bytes), as specified by ERC-2771. ([#4481](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/4481))
|
||||
- `ERC2771Context`: Prevent revert in `_msgData()` when a call originating from a trusted forwarder is not long enough to contain the request signer address (i.e. `msg.data.length` is less than 20 bytes). Return the full calldata in that case. ([#4484](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/4484))
|
||||
- `Address.isContract` (because of its ambiguous nature and potential for misuse)
|
||||
- `Checkpoints.History`
|
||||
- `Counters`
|
||||
- `ERC20Snapshot`
|
||||
- `ERC20VotesComp`
|
||||
- `ERC165Storage` (in favor of inheritance based approach)
|
||||
- `ERC777`
|
||||
- `ERC1820Implementer`
|
||||
- `GovernorVotesComp`
|
||||
- `GovernorProposalThreshold` (deprecated since 4.4)
|
||||
- `PaymentSplitter`
|
||||
- `PullPayment`
|
||||
- `SafeMath`
|
||||
- `SignedSafeMath`
|
||||
- `Timers`
|
||||
- `TokenTimelock` (in favor of `VestingWallet`)
|
||||
- All escrow contracts (`Escrow`, `ConditionalEscrow` and `RefundEscrow`)
|
||||
- All cross-chain contracts, including `AccessControlCrossChain` and all the vendored bridge interfaces
|
||||
- All presets in favor of [OpenZeppelin Contracts Wizard](https://wizard.openzeppelin.com/)
|
||||
|
||||
These removals were implemented in the following PRs: [#3637](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3637), [#3880](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3880), [#3945](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3945), [#4258](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/4258), [#4276](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/4276), [#4289](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/4289)
|
||||
|
||||
### How to upgrade from 4.x
|
||||
|
||||
#### ERC20, ERC721, and ERC1155
|
||||
|
||||
These breaking changes will require modifications to ERC20, ERC721, and ERC1155 contracts, since the `_afterTokenTransfer` and `_beforeTokenTransfer` functions were removed. Any customization made through those hooks should now be done overriding the new `_update` function instead.
|
||||
|
||||
Minting and burning are implemented by `_update` and customizations should be done by overriding this function as well. `_mint` and `_burn` are no longer virtual (meaning they are not overridable) to guard against possible inconsistencies.
|
||||
|
||||
For example, a contract using `ERC20`'s `_beforeTokenTransfer` hook would have to be changed in the following way.
|
||||
|
||||
```diff
|
||||
- function _beforeTokenTransfer(
|
||||
+ function _update(
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal virtual override {
|
||||
- super._beforeTokenTransfer(from, to, amount);
|
||||
require(!condition(), "ERC20: wrong condition");
|
||||
+ super._update(from, to, amount);
|
||||
}
|
||||
```
|
||||
|
||||
#### ERC165Storage
|
||||
|
||||
Users that were registering EIP-165 interfaces with `_registerInterface` from `ERC165Storage` should instead do so so by overriding the `supportsInterface` function as seen below:
|
||||
|
||||
```solidity
|
||||
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
||||
return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
|
||||
}
|
||||
```
|
||||
|
||||
## 4.9.2 (2023-06-16)
|
||||
|
||||
|
||||
@ -114,4 +114,25 @@ In addition to the official Solidity Style Guide we have a number of other conve
|
||||
interface IERC777 {
|
||||
```
|
||||
|
||||
* Contracts not intended to be used standalone should be marked abstract
|
||||
so they are required to be inherited to other contracts.
|
||||
|
||||
```solidity
|
||||
abstract contract AccessControl is ..., {
|
||||
```
|
||||
|
||||
* Unchecked arithmetic blocks should contain comments explaining why overflow is guaranteed not to happen. If the reason is immediately apparent from the line above the unchecked block, the comment may be omitted.
|
||||
|
||||
* Custom errors should be declared following the [EIP-6093](https://eips.ethereum.org/EIPS/eip-6093) rationale whenever reasonable. Also, consider the following:
|
||||
|
||||
* The domain prefix should be picked in the following order:
|
||||
1. Use `ERC<number>` if the error is a violation of an ERC specification.
|
||||
2. Use the name of the underlying component where it belongs (eg. `Governor`, `ECDSA`, or `Timelock`).
|
||||
|
||||
* The location of custom errors should be decided in the following order:
|
||||
1. Take the errors from their underlying ERCs if they're already defined.
|
||||
2. Declare the errors in the underlying interface/library if the error makes sense in its context.
|
||||
3. Declare the error in the implementation if the underlying interface/library is not suitable to do so (eg. interface/library already specified in an ERC).
|
||||
4. Declare the error in an extension if the error only happens in such extension or child contracts.
|
||||
|
||||
* Custom error names should not be declared twice along the library to avoid duplicated identifier declarations when inheriting from multiple contracts.
|
||||
|
||||
25
README.md
25
README.md
@ -1,3 +1,6 @@
|
||||
> **Warning**
|
||||
> Version 5.0 is under active development. The code in this branch is not recommended for use.
|
||||
|
||||
# <img src="logo.svg" alt="OpenZeppelin" height="40px">
|
||||
|
||||
[](https://www.npmjs.org/package/@openzeppelin/contracts)
|
||||
@ -20,22 +23,34 @@
|
||||
|
||||
### Installation
|
||||
|
||||
#### Hardhat, Truffle (npm)
|
||||
|
||||
```
|
||||
$ npm install @openzeppelin/contracts
|
||||
```
|
||||
|
||||
OpenZeppelin Contracts features a [stable API](https://docs.openzeppelin.com/contracts/releases-stability#api-stability), which means that your contracts won't break unexpectedly when upgrading to a newer minor version.
|
||||
|
||||
An alternative to npm is to use the GitHub repository (`openzeppelin/openzeppelin-contracts`) to retrieve the contracts. When doing this, make sure to specify the tag for a release such as `v4.5.0`, instead of using the `master` branch.
|
||||
#### Foundry (git)
|
||||
|
||||
> **Warning** When installing via git, it is a common error to use the `master` branch. This is a development branch that should be avoided in favor of tagged releases. The release process involves security measures that the `master` branch does not guarantee.
|
||||
|
||||
> **Warning** Foundry installs the latest version initially, but subsequent `forge update` commands will use the `master` branch.
|
||||
|
||||
```
|
||||
$ forge install OpenZeppelin/openzeppelin-contracts
|
||||
```
|
||||
|
||||
Add `@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/` in `remappings.txt.`
|
||||
|
||||
### Usage
|
||||
|
||||
Once installed, you can use the contracts in the library by importing them:
|
||||
|
||||
```solidity
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
||||
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
||||
|
||||
contract MyCollectible is ERC721 {
|
||||
constructor() ERC721("MyCollectible", "MCO") {
|
||||
@ -67,7 +82,9 @@ Finally, you may want to take a look at the [guides on our blog](https://blog.op
|
||||
|
||||
This project is maintained by [OpenZeppelin](https://openzeppelin.com) with the goal of providing a secure and reliable library of smart contract components for the ecosystem. We address security through risk management in various areas such as engineering and open source best practices, scoping and API design, multi-layered review processes, and incident response preparedness.
|
||||
|
||||
The security policy is detailed in [`SECURITY.md`](./SECURITY.md), and specifies how you can report security vulnerabilities, which versions will receive security patches, and how to stay informed about them. We run a [bug bounty program on Immunefi](https://immunefi.com/bounty/openzeppelin) to reward the responsible disclosure of vulnerabilities.
|
||||
The [OpenZeppelin Contracts Security Center](https://contracts.openzeppelin.com/security) contains more details about the secure development process.
|
||||
|
||||
The security policy is detailed in [`SECURITY.md`](./SECURITY.md) as well, and specifies how you can report security vulnerabilities, which versions will receive security patches, and how to stay informed about them. We run a [bug bounty program on Immunefi](https://immunefi.com/bounty/openzeppelin) to reward the responsible disclosure of vulnerabilities.
|
||||
|
||||
The engineering guidelines we follow to promote project quality can be found in [`GUIDELINES.md`](./GUIDELINES.md).
|
||||
|
||||
|
||||
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/access/AccessControlDefaultAdminRules.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/access/AccessControl.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/utils/structs/DoubleEndedQueue.sol";
|
||||
|
||||
@ -29,11 +29,11 @@ contract DoubleEndedQueueHarness {
|
||||
_deque.clear();
|
||||
}
|
||||
|
||||
function begin() external view returns (int128) {
|
||||
function begin() external view returns (uint128) {
|
||||
return _deque._begin;
|
||||
}
|
||||
|
||||
function end() external view returns (int128) {
|
||||
function end() external view returns (uint128) {
|
||||
return _deque._end;
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/token/ERC20/ERC20.sol";
|
||||
import "../patched/token/ERC20/extensions/ERC20Permit.sol";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/token/ERC20/extensions/ERC20Permit.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/token/ERC20/extensions/ERC20Wrapper.sol";
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import "../patched/interfaces/IERC3156FlashBorrower.sol";
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
contract ERC3156FlashBorrowerHarness is IERC3156FlashBorrower {
|
||||
bytes32 somethingToReturn;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/token/ERC721/ERC721.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/interfaces/IERC721Receiver.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/utils/structs/EnumerableMap.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/utils/structs/EnumerableSet.sol";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.2;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/proxy/utils/Initializable.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/access/Ownable2Step.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/access/Ownable.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/security/Pausable.sol";
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../patched/governance/TimelockController.sol";
|
||||
|
||||
|
||||
@ -28,6 +28,11 @@ const argv = require('yargs')
|
||||
type: 'number',
|
||||
default: 4,
|
||||
},
|
||||
verbose: {
|
||||
alias: 'v',
|
||||
type: 'count',
|
||||
default: 0,
|
||||
},
|
||||
options: {
|
||||
alias: 'o',
|
||||
type: 'array',
|
||||
@ -65,6 +70,9 @@ for (const { spec, contract, files, options = [] } of specs) {
|
||||
// Run certora, aggregate the output and print it at the end
|
||||
async function runCertora(spec, contract, files, options = []) {
|
||||
const args = [...files, '--verify', `${contract}:certora/specs/${spec}.spec`, ...options];
|
||||
if (argv.verbose) {
|
||||
console.log('Running:', args.join(' '));
|
||||
}
|
||||
const child = proc.spawn('certoraRun', args);
|
||||
|
||||
const stream = new PassThrough();
|
||||
|
||||
@ -12,44 +12,23 @@ use rule onlyGrantCanGrant filtered {
|
||||
│ Helpers │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
definition timeSanity(env e) returns bool =
|
||||
e.block.timestamp > 0 && e.block.timestamp + defaultAdminDelay(e) < max_uint48();
|
||||
|
||||
function max_uint48() returns mathint {
|
||||
return (1 << 48) - 1;
|
||||
}
|
||||
definition delayChangeWaitSanity(env e, uint48 newDelay) returns bool =
|
||||
e.block.timestamp + delayChangeWait_(e, newDelay) < max_uint48();
|
||||
|
||||
function nonZeroAccount(address account) returns bool {
|
||||
return account != 0;
|
||||
}
|
||||
definition isSet(uint48 schedule) returns bool =
|
||||
schedule != 0;
|
||||
|
||||
function timeSanity(env e) returns bool {
|
||||
return
|
||||
e.block.timestamp > 0 && // Avoids 0 schedules
|
||||
e.block.timestamp + defaultAdminDelay(e) < max_uint48();
|
||||
}
|
||||
definition hasPassed(env e, uint48 schedule) returns bool =
|
||||
schedule < e.block.timestamp;
|
||||
|
||||
function delayChangeWaitSanity(env e, uint48 newDelay) returns bool {
|
||||
return e.block.timestamp + delayChangeWait_(e, newDelay) < max_uint48();
|
||||
}
|
||||
definition increasingDelaySchedule(env e, uint48 newDelay) returns mathint =
|
||||
e.block.timestamp + min(newDelay, defaultAdminDelayIncreaseWait());
|
||||
|
||||
function isSet(uint48 schedule) returns bool {
|
||||
return schedule != 0;
|
||||
}
|
||||
|
||||
function hasPassed(env e, uint48 schedule) returns bool {
|
||||
return schedule < e.block.timestamp;
|
||||
}
|
||||
|
||||
function min(uint48 a, uint48 b) returns mathint {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
function increasingDelaySchedule(env e, uint48 newDelay) returns mathint {
|
||||
return e.block.timestamp + min(newDelay, defaultAdminDelayIncreaseWait());
|
||||
}
|
||||
|
||||
function decreasingDelaySchedule(env e, uint48 newDelay) returns mathint {
|
||||
return e.block.timestamp + defaultAdminDelay(e) - newDelay;
|
||||
}
|
||||
definition decreasingDelaySchedule(env e, uint48 newDelay) returns mathint =
|
||||
e.block.timestamp + defaultAdminDelay(e) - newDelay;
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
@ -57,11 +36,10 @@ function decreasingDelaySchedule(env e, uint48 newDelay) returns mathint {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
invariant defaultAdminConsistency(address account)
|
||||
defaultAdmin() == account <=> hasRole(DEFAULT_ADMIN_ROLE(), account)
|
||||
(account == defaultAdmin() && account != 0) <=> hasRole(DEFAULT_ADMIN_ROLE(), account)
|
||||
{
|
||||
preserved {
|
||||
// defaultAdmin() returns the zero address when there's no default admin
|
||||
require nonZeroAccount(account);
|
||||
preserved with (env e) {
|
||||
require nonzerosender(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,10 +50,12 @@ invariant defaultAdminConsistency(address account)
|
||||
*/
|
||||
invariant singleDefaultAdmin(address account, address another)
|
||||
hasRole(DEFAULT_ADMIN_ROLE(), account) && hasRole(DEFAULT_ADMIN_ROLE(), another) => another == account
|
||||
// We filter here because we couldn't find a way to force Certora to have an initial state with
|
||||
// only one DEFAULT_ADMIN_ROLE enforced, so a counter example is a different default admin since inception
|
||||
// triggering the transfer, which is known to be impossible by definition.
|
||||
filtered { f -> f.selector != acceptDefaultAdminTransfer().selector }
|
||||
{
|
||||
preserved {
|
||||
requireInvariant defaultAdminConsistency(account);
|
||||
requireInvariant defaultAdminConsistency(another);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
@ -118,7 +98,8 @@ rule revokeRoleEffect(env e, bytes32 role) {
|
||||
"roles can only be revoked by their owner except for the default admin role";
|
||||
|
||||
// effect
|
||||
assert success => !hasRole(role, account), "role is revoked";
|
||||
assert success => !hasRole(role, account),
|
||||
"role is revoked";
|
||||
|
||||
// no side effect
|
||||
assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount),
|
||||
@ -137,35 +118,59 @@ rule renounceRoleEffect(env e, bytes32 role) {
|
||||
address account;
|
||||
address otherAccount;
|
||||
|
||||
bool hasOtherRoleBefore = hasRole(otherRole, otherAccount);
|
||||
uint48 scheduleBefore = pendingDefaultAdminSchedule_();
|
||||
bool hasOtherRoleBefore = hasRole(otherRole, otherAccount);
|
||||
address adminBefore = defaultAdmin();
|
||||
address pendingAdminBefore = pendingDefaultAdmin_();
|
||||
uint48 scheduleBefore = pendingDefaultAdminSchedule_();
|
||||
|
||||
renounceRole@withrevert(e, role, account);
|
||||
bool success = !lastReverted;
|
||||
|
||||
bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
|
||||
bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
|
||||
address adminAfter = defaultAdmin();
|
||||
address pendingAdminAfter = pendingDefaultAdmin_();
|
||||
uint48 scheduleAfter = pendingDefaultAdminSchedule_();
|
||||
|
||||
// liveness
|
||||
assert success <=> (
|
||||
account == e.msg.sender &&
|
||||
(
|
||||
role != DEFAULT_ADMIN_ROLE() ||
|
||||
account != adminBefore ||
|
||||
(
|
||||
role != DEFAULT_ADMIN_ROLE()
|
||||
) || (
|
||||
role == DEFAULT_ADMIN_ROLE() &&
|
||||
pendingAdminBefore == 0 &&
|
||||
isSet(scheduleBefore) &&
|
||||
hasPassed(e, scheduleBefore)
|
||||
)
|
||||
)
|
||||
), "an account only can renounce by itself with a delay for the default admin role";
|
||||
),
|
||||
"an account only can renounce by itself with a delay for the default admin role";
|
||||
|
||||
// effect
|
||||
assert success => !hasRole(role, account), "role is renounced";
|
||||
assert success => !hasRole(role, account),
|
||||
"role is renounced";
|
||||
|
||||
assert success => (
|
||||
(
|
||||
role == DEFAULT_ADMIN_ROLE() &&
|
||||
account == adminBefore
|
||||
) ? (
|
||||
adminAfter == 0 &&
|
||||
pendingAdminAfter == 0 &&
|
||||
scheduleAfter == 0
|
||||
) : (
|
||||
adminAfter == adminBefore &&
|
||||
pendingAdminAfter == pendingAdminBefore &&
|
||||
scheduleAfter == scheduleBefore
|
||||
)
|
||||
),
|
||||
"renouncing default admin role cleans state iff called by previous admin";
|
||||
|
||||
// no side effect
|
||||
assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount),
|
||||
assert hasOtherRoleBefore != hasOtherRoleAfter => (
|
||||
role == otherRole &&
|
||||
account == otherAccount
|
||||
),
|
||||
"no other role is affected";
|
||||
}
|
||||
|
||||
@ -175,10 +180,6 @@ rule renounceRoleEffect(env e, bytes32 role) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule noDefaultAdminChange(env e, method f, calldataarg args) {
|
||||
require nonZeroAccount(e.msg.sender);
|
||||
requireInvariant defaultAdminConsistency(defaultAdmin());
|
||||
requireInvariant singleDefaultAdmin(e.msg.sender, defaultAdmin());
|
||||
|
||||
address adminBefore = defaultAdmin();
|
||||
f(e, args);
|
||||
address adminAfter = defaultAdmin();
|
||||
@ -186,18 +187,17 @@ rule noDefaultAdminChange(env e, method f, calldataarg args) {
|
||||
assert adminBefore != adminAfter => (
|
||||
f.selector == acceptDefaultAdminTransfer().selector ||
|
||||
f.selector == 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 renoucing";
|
||||
}
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Rule: pendingDefaultAdmin is only affected by beginning, accepting or canceling an admin transfer │
|
||||
│ Rule: pendingDefaultAdmin is only affected by beginning, completing (accept or renounce), or canceling an admin │
|
||||
│ transfer │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule noPendingDefaultAdminChange(env e, method f, calldataarg args) {
|
||||
requireInvariant defaultAdminConsistency(defaultAdmin());
|
||||
requireInvariant singleDefaultAdmin(e.msg.sender, defaultAdmin());
|
||||
|
||||
address pendingAdminBefore = pendingDefaultAdmin_();
|
||||
address scheduleBefore = pendingDefaultAdminSchedule_();
|
||||
f(e, args);
|
||||
@ -210,8 +210,10 @@ rule noPendingDefaultAdminChange(env e, method f, calldataarg args) {
|
||||
) => (
|
||||
f.selector == beginDefaultAdminTransfer(address).selector ||
|
||||
f.selector == acceptDefaultAdminTransfer().selector ||
|
||||
f.selector == cancelDefaultAdminTransfer().selector
|
||||
), "pending admin and its schedule is only affected by beginning, accepting or cancelling an admin transfer";
|
||||
f.selector == cancelDefaultAdminTransfer().selector ||
|
||||
f.selector == renounceRole(bytes32,address).selector
|
||||
),
|
||||
"pending admin and its schedule is only affected by beginning, completing, or cancelling an admin transfer";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -224,7 +226,8 @@ rule noDefaultAdminDelayChange(env e, method f, calldataarg args) {
|
||||
f(e, args);
|
||||
uint48 delayAfter = defaultAdminDelay(e);
|
||||
|
||||
assert delayBefore == delayAfter, "delay can't be changed atomically by any function";
|
||||
assert delayBefore == delayAfter,
|
||||
"delay can't be changed atomically by any function";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -240,7 +243,8 @@ rule noPendingDefaultAdminDelayChange(env e, method f, calldataarg args) {
|
||||
assert pendingDelayBefore != pendingDelayAfter => (
|
||||
f.selector == changeDefaultAdminDelay(uint48).selector ||
|
||||
f.selector == rollbackDefaultAdminDelay().selector
|
||||
), "pending delay is only affected by changeDefaultAdminDelay or rollbackDefaultAdminDelay";
|
||||
),
|
||||
"pending delay is only affected by changeDefaultAdminDelay or rollbackDefaultAdminDelay";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -263,10 +267,10 @@ rule noDefaultAdminDelayIncreaseWaitChange(env e, method f, calldataarg args) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule beginDefaultAdminTransfer(env e, address newAdmin) {
|
||||
require nonpayable(e);
|
||||
require timeSanity(e);
|
||||
requireInvariant defaultAdminConsistency(defaultAdmin());
|
||||
requireInvariant singleDefaultAdmin(e.msg.sender, defaultAdmin());
|
||||
require nonpayable(e);
|
||||
require nonzerosender(e);
|
||||
requireInvariant defaultAdminConsistency(e.msg.sender);
|
||||
|
||||
beginDefaultAdminTransfer@withrevert(e, newAdmin);
|
||||
bool success = !lastReverted;
|
||||
@ -288,18 +292,24 @@ rule beginDefaultAdminTransfer(env e, address newAdmin) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule pendingDefaultAdminDelayEnforced(env e1, env e2, method f, calldataarg args, address newAdmin) {
|
||||
require e1.block.timestamp < e2.block.timestamp;
|
||||
require e1.block.timestamp <= e2.block.timestamp;
|
||||
|
||||
uint48 delayBefore = defaultAdminDelay(e1);
|
||||
address adminBefore = defaultAdmin();
|
||||
|
||||
// There might be a better way to generalize this without requiring `beginDefaultAdminTransfer`, but currently
|
||||
// it's the only way in which we can attest that only `delayBefore` has passed before a change.
|
||||
beginDefaultAdminTransfer(e1, newAdmin);
|
||||
f(e2, args);
|
||||
|
||||
address adminAfter = defaultAdmin();
|
||||
|
||||
assert adminAfter == newAdmin => ((e2.block.timestamp >= e1.block.timestamp + delayBefore) || adminBefore == newAdmin),
|
||||
"A delay can't change in less than applied schedule";
|
||||
// change can only happen towards the newAdmin, with the delay
|
||||
assert adminAfter != adminBefore => (
|
||||
adminAfter == newAdmin &&
|
||||
e2.block.timestamp >= e1.block.timestamp + delayBefore
|
||||
),
|
||||
"The admin can only change after the enforced delay and to the previously scheduled new admin";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -309,17 +319,19 @@ rule pendingDefaultAdminDelayEnforced(env e1, env e2, method f, calldataarg args
|
||||
*/
|
||||
rule acceptDefaultAdminTransfer(env e) {
|
||||
require nonpayable(e);
|
||||
requireInvariant defaultAdminConsistency(defaultAdmin());
|
||||
requireInvariant singleDefaultAdmin(e.msg.sender, defaultAdmin());
|
||||
|
||||
address pendingAdminBefore = pendingDefaultAdmin_();
|
||||
uint48 scheduleAfter = pendingDefaultAdminSchedule_();
|
||||
uint48 scheduleBefore = pendingDefaultAdminSchedule_();
|
||||
|
||||
acceptDefaultAdminTransfer@withrevert(e);
|
||||
bool success = !lastReverted;
|
||||
|
||||
// liveness
|
||||
assert success <=> e.msg.sender == pendingAdminBefore && isSet(scheduleAfter) && hasPassed(e, scheduleAfter),
|
||||
assert success <=> (
|
||||
e.msg.sender == pendingAdminBefore &&
|
||||
isSet(scheduleBefore) &&
|
||||
hasPassed(e, scheduleBefore)
|
||||
),
|
||||
"only the pending default admin can accept the role after the schedule has been set and passed";
|
||||
|
||||
// effect
|
||||
@ -338,8 +350,8 @@ rule acceptDefaultAdminTransfer(env e) {
|
||||
*/
|
||||
rule cancelDefaultAdminTransfer(env e) {
|
||||
require nonpayable(e);
|
||||
requireInvariant defaultAdminConsistency(defaultAdmin());
|
||||
requireInvariant singleDefaultAdmin(e.msg.sender, defaultAdmin());
|
||||
require nonzerosender(e);
|
||||
requireInvariant defaultAdminConsistency(e.msg.sender);
|
||||
|
||||
cancelDefaultAdminTransfer@withrevert(e);
|
||||
bool success = !lastReverted;
|
||||
@ -361,11 +373,11 @@ rule cancelDefaultAdminTransfer(env e) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule changeDefaultAdminDelay(env e, uint48 newDelay) {
|
||||
require nonpayable(e);
|
||||
require timeSanity(e);
|
||||
require nonpayable(e);
|
||||
require nonzerosender(e);
|
||||
require delayChangeWaitSanity(e, newDelay);
|
||||
requireInvariant defaultAdminConsistency(defaultAdmin());
|
||||
requireInvariant singleDefaultAdmin(e.msg.sender, defaultAdmin());
|
||||
requireInvariant defaultAdminConsistency(e.msg.sender);
|
||||
|
||||
uint48 delayBefore = defaultAdminDelay(e);
|
||||
|
||||
@ -377,7 +389,9 @@ rule changeDefaultAdminDelay(env e, uint48 newDelay) {
|
||||
"only the current default admin can begin a delay change";
|
||||
|
||||
// effect
|
||||
assert success => pendingDelay_(e) == newDelay, "pending delay is set";
|
||||
assert success => pendingDelay_(e) == newDelay,
|
||||
"pending delay is set";
|
||||
|
||||
assert success => (
|
||||
pendingDelaySchedule_(e) > e.block.timestamp ||
|
||||
delayBefore == newDelay || // Interpreted as decreasing, x - x = 0
|
||||
@ -392,17 +406,22 @@ rule changeDefaultAdminDelay(env e, uint48 newDelay) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule pendingDelayWaitEnforced(env e1, env e2, method f, calldataarg args, uint48 newDelay) {
|
||||
require e1.block.timestamp < e2.block.timestamp;
|
||||
require e1.block.timestamp <= e2.block.timestamp;
|
||||
|
||||
uint48 delayBefore = defaultAdminDelay(e1);
|
||||
|
||||
changeDefaultAdminDelay(e1, newDelay);
|
||||
f(e2, args);
|
||||
|
||||
uint48 delayAfter = defaultAdminDelay(e2);
|
||||
|
||||
mathint delayWait = newDelay > delayBefore ? increasingDelaySchedule(e1, newDelay) : decreasingDelaySchedule(e1, newDelay);
|
||||
|
||||
assert delayAfter == newDelay => (e2.block.timestamp >= delayWait || delayBefore == newDelay),
|
||||
"A delay can't change in less than applied schedule";
|
||||
assert delayAfter != delayBefore => (
|
||||
delayAfter == newDelay &&
|
||||
e2.block.timestamp >= delayWait
|
||||
),
|
||||
"A delay can only change after the applied schedule";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -427,8 +446,8 @@ rule pendingDelayWait(env e, uint48 newDelay) {
|
||||
*/
|
||||
rule rollbackDefaultAdminDelay(env e) {
|
||||
require nonpayable(e);
|
||||
requireInvariant defaultAdminConsistency(defaultAdmin());
|
||||
requireInvariant singleDefaultAdmin(e.msg.sender, defaultAdmin());
|
||||
require nonzerosender(e);
|
||||
requireInvariant defaultAdminConsistency(e.msg.sender);
|
||||
|
||||
rollbackDefaultAdminDelay@withrevert(e);
|
||||
bool success = !lastReverted;
|
||||
@ -443,58 +462,3 @@ rule rollbackDefaultAdminDelay(env e) {
|
||||
assert success => pendingDelaySchedule_(e) == 0,
|
||||
"Pending default admin delay is reset";
|
||||
}
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Rule: pending default admin and the delay can only change along with their corresponding schedules │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule pendingValueAndScheduleCoupling(env e, address newAdmin, uint48 newDelay) {
|
||||
requireInvariant defaultAdminConsistency(defaultAdmin());
|
||||
requireInvariant singleDefaultAdmin(e.msg.sender, defaultAdmin());
|
||||
|
||||
// Pending admin
|
||||
address pendingAdminBefore = pendingDefaultAdmin_();
|
||||
uint48 pendingAdminScheduleBefore = pendingDefaultAdminSchedule_();
|
||||
|
||||
beginDefaultAdminTransfer(e, newAdmin);
|
||||
|
||||
address pendingAdminAfter = pendingDefaultAdmin_();
|
||||
uint48 pendingAdminScheduleAfter = pendingDefaultAdminSchedule_();
|
||||
|
||||
assert (
|
||||
pendingAdminScheduleBefore != pendingDefaultAdminSchedule_() &&
|
||||
pendingAdminBefore == pendingAdminAfter
|
||||
) => newAdmin == pendingAdminBefore, "pending admin stays the same if the new admin set is the same";
|
||||
|
||||
assert (
|
||||
pendingAdminBefore != pendingAdminAfter &&
|
||||
pendingAdminScheduleBefore == pendingDefaultAdminSchedule_()
|
||||
) => (
|
||||
// Schedule doesn't change if:
|
||||
// - The defaultAdminDelay was reduced to a value such that added to the block.timestamp is equal to previous schedule
|
||||
e.block.timestamp + defaultAdminDelay(e) == pendingAdminScheduleBefore
|
||||
), "pending admin stays the same if a default admin transfer is begun on accepted edge cases";
|
||||
|
||||
// Pending delay
|
||||
address pendingDelayBefore = pendingDelay_(e);
|
||||
uint48 pendingDelayScheduleBefore = pendingDelaySchedule_(e);
|
||||
|
||||
changeDefaultAdminDelay(e, newDelay);
|
||||
|
||||
address pendingDelayAfter = pendingDelay_(e);
|
||||
uint48 pendingDelayScheduleAfter = pendingDelaySchedule_(e);
|
||||
|
||||
assert (
|
||||
pendingDelayScheduleBefore != pendingDelayScheduleAfter &&
|
||||
pendingDelayBefore == pendingDelayAfter
|
||||
) => newDelay == pendingDelayBefore || pendingDelayBefore == 0, "pending delay stays the same if the new delay set is the same";
|
||||
|
||||
assert (
|
||||
pendingDelayBefore != pendingDelayAfter &&
|
||||
pendingDelayScheduleBefore == pendingDelayScheduleAfter
|
||||
) => (
|
||||
increasingDelaySchedule(e, newDelay) == pendingDelayScheduleBefore ||
|
||||
decreasingDelaySchedule(e, newDelay) == pendingDelayScheduleBefore
|
||||
), "pending delay stays the same if a default admin transfer is begun on accepted edge cases";
|
||||
}
|
||||
|
||||
@ -1,64 +1,25 @@
|
||||
import "helpers/helpers.spec"
|
||||
import "helpers/helpers.spec";
|
||||
|
||||
methods {
|
||||
pushFront(bytes32) envfree
|
||||
pushBack(bytes32) envfree
|
||||
popFront() returns (bytes32) envfree
|
||||
popBack() returns (bytes32) envfree
|
||||
clear() envfree
|
||||
function pushFront(bytes32) external envfree;
|
||||
function pushBack(bytes32) external envfree;
|
||||
function popFront() external returns (bytes32) envfree;
|
||||
function popBack() external returns (bytes32) envfree;
|
||||
function clear() external envfree;
|
||||
|
||||
// exposed for FV
|
||||
begin() returns (int128) envfree
|
||||
end() returns (int128) envfree
|
||||
function begin() external returns (uint128) envfree;
|
||||
function end() external returns (uint128) envfree;
|
||||
|
||||
// view
|
||||
length() returns (uint256) envfree
|
||||
empty() returns (bool) envfree
|
||||
front() returns (bytes32) envfree
|
||||
back() returns (bytes32) envfree
|
||||
at_(uint256) returns (bytes32) envfree // at is a reserved word
|
||||
function length() external returns (uint256) envfree;
|
||||
function empty() external returns (bool) envfree;
|
||||
function front() external returns (bytes32) envfree;
|
||||
function back() external returns (bytes32) envfree;
|
||||
function at_(uint256) external returns (bytes32) envfree; // at is a reserved word
|
||||
}
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Helpers │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
|
||||
function min_int128() returns mathint {
|
||||
return -(1 << 127);
|
||||
}
|
||||
|
||||
function max_int128() returns mathint {
|
||||
return (1 << 127) - 1;
|
||||
}
|
||||
|
||||
// Could be broken in theory, but not in practice
|
||||
function boundedQueue() returns bool {
|
||||
return
|
||||
max_int128() > to_mathint(end()) &&
|
||||
min_int128() < to_mathint(begin());
|
||||
}
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Invariant: end is larger or equal than begin │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
invariant boundariesConsistency()
|
||||
end() >= begin()
|
||||
filtered { f -> !f.isView }
|
||||
{ preserved { require boundedQueue(); } }
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Invariant: length is end minus begin │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
invariant lengthConsistency()
|
||||
length() == to_mathint(end()) - to_mathint(begin())
|
||||
filtered { f -> !f.isView }
|
||||
{ preserved { require boundedQueue(); } }
|
||||
definition full() returns bool = length() == max_uint128;
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
@ -68,22 +29,19 @@ invariant lengthConsistency()
|
||||
invariant emptiness()
|
||||
empty() <=> length() == 0
|
||||
filtered { f -> !f.isView }
|
||||
{ preserved { require boundedQueue(); } }
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Invariant: front points to the first index and back points to the last one │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
invariant queueEndings()
|
||||
at_(length() - 1) == back() && at_(0) == front()
|
||||
invariant queueFront()
|
||||
at_(0) == front()
|
||||
filtered { f -> !f.isView }
|
||||
|
||||
invariant queueBack()
|
||||
at_(require_uint256(length() - 1)) == back()
|
||||
filtered { f -> !f.isView }
|
||||
{
|
||||
preserved {
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
@ -91,18 +49,18 @@ invariant queueEndings()
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule pushFront(bytes32 value) {
|
||||
require boundedQueue();
|
||||
|
||||
uint256 lengthBefore = length();
|
||||
bool fullBefore = full();
|
||||
|
||||
pushFront@withrevert(value);
|
||||
bool success = !lastReverted;
|
||||
|
||||
// liveness
|
||||
assert !lastReverted, "never reverts";
|
||||
assert success <=> !fullBefore, "never revert if not previously full";
|
||||
|
||||
// effect
|
||||
assert front() == value, "front set to value";
|
||||
assert length() == lengthBefore + 1, "queue extended";
|
||||
assert success => front() == value, "front set to value";
|
||||
assert success => to_mathint(length()) == lengthBefore + 1, "queue extended";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -111,15 +69,13 @@ rule pushFront(bytes32 value) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule pushFrontConsistency(uint256 index) {
|
||||
require boundedQueue();
|
||||
|
||||
bytes32 beforeAt = at_(index);
|
||||
|
||||
bytes32 value;
|
||||
pushFront(value);
|
||||
|
||||
// try to read value
|
||||
bytes32 afterAt = at_@withrevert(index + 1);
|
||||
bytes32 afterAt = at_@withrevert(require_uint256(index + 1));
|
||||
|
||||
assert !lastReverted, "value still there";
|
||||
assert afterAt == beforeAt, "data is preserved";
|
||||
@ -131,18 +87,18 @@ rule pushFrontConsistency(uint256 index) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule pushBack(bytes32 value) {
|
||||
require boundedQueue();
|
||||
|
||||
uint256 lengthBefore = length();
|
||||
bool fullBefore = full();
|
||||
|
||||
pushBack@withrevert(value);
|
||||
bool success = !lastReverted;
|
||||
|
||||
// liveness
|
||||
assert !lastReverted, "never reverts";
|
||||
assert success <=> !fullBefore, "never revert if not previously full";
|
||||
|
||||
// effect
|
||||
assert back() == value, "back set to value";
|
||||
assert length() == lengthBefore + 1, "queue increased";
|
||||
assert success => back() == value, "back set to value";
|
||||
assert success => to_mathint(length()) == lengthBefore + 1, "queue increased";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -151,8 +107,6 @@ rule pushBack(bytes32 value) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule pushBackConsistency(uint256 index) {
|
||||
require boundedQueue();
|
||||
|
||||
bytes32 beforeAt = at_(index);
|
||||
|
||||
bytes32 value;
|
||||
@ -171,9 +125,6 @@ rule pushBackConsistency(uint256 index) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule popFront {
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
|
||||
uint256 lengthBefore = length();
|
||||
bytes32 frontBefore = front@withrevert();
|
||||
|
||||
@ -185,7 +136,7 @@ rule popFront {
|
||||
|
||||
// effect
|
||||
assert success => frontBefore == popped, "previous front is returned";
|
||||
assert success => length() == lengthBefore - 1, "queue decreased";
|
||||
assert success => to_mathint(length()) == lengthBefore - 1, "queue decreased";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -194,9 +145,6 @@ rule popFront {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule popFrontConsistency(uint256 index) {
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
|
||||
// Read (any) value that is not the front (this asserts the value exists / the queue is long enough)
|
||||
require index > 1;
|
||||
bytes32 before = at_(index);
|
||||
@ -204,7 +152,7 @@ rule popFrontConsistency(uint256 index) {
|
||||
popFront();
|
||||
|
||||
// try to read value
|
||||
bytes32 after = at_@withrevert(index - 1);
|
||||
bytes32 after = at_@withrevert(require_uint256(index - 1));
|
||||
|
||||
assert !lastReverted, "value still exists in the queue";
|
||||
assert before == after, "values are offset and not modified";
|
||||
@ -216,9 +164,6 @@ rule popFrontConsistency(uint256 index) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule popBack {
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
|
||||
uint256 lengthBefore = length();
|
||||
bytes32 backBefore = back@withrevert();
|
||||
|
||||
@ -230,7 +175,7 @@ rule popBack {
|
||||
|
||||
// effect
|
||||
assert success => backBefore == popped, "previous back is returned";
|
||||
assert success => length() == lengthBefore - 1, "queue decreased";
|
||||
assert success => to_mathint(length()) == lengthBefore - 1, "queue decreased";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -239,11 +184,8 @@ rule popBack {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule popBackConsistency(uint256 index) {
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
|
||||
// Read (any) value that is not the back (this asserts the value exists / the queue is long enough)
|
||||
require index < length() - 1;
|
||||
require to_mathint(index) < length() - 1;
|
||||
bytes32 before = at_(index);
|
||||
|
||||
popBack();
|
||||
@ -275,24 +217,25 @@ rule clear {
|
||||
│ Rule: front/back access reverts only if the queue is empty or querying out of bounds │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule onlyEmptyRevert(env e) {
|
||||
rule onlyEmptyOrFullRevert(env e) {
|
||||
require nonpayable(e);
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
|
||||
method f;
|
||||
calldataarg args;
|
||||
|
||||
bool emptyBefore = empty();
|
||||
bool fullBefore = full();
|
||||
|
||||
f@withrevert(e, args);
|
||||
|
||||
assert lastReverted => (
|
||||
(f.selector == front().selector && emptyBefore) ||
|
||||
(f.selector == back().selector && emptyBefore) ||
|
||||
(f.selector == popFront().selector && emptyBefore) ||
|
||||
(f.selector == popBack().selector && emptyBefore) ||
|
||||
f.selector == at_(uint256).selector // revert conditions are verified in onlyOutOfBoundsRevert
|
||||
(f.selector == sig:front().selector && emptyBefore) ||
|
||||
(f.selector == sig:back().selector && emptyBefore) ||
|
||||
(f.selector == sig:popFront().selector && emptyBefore) ||
|
||||
(f.selector == sig:popBack().selector && emptyBefore) ||
|
||||
(f.selector == sig:pushFront(bytes32).selector && fullBefore ) ||
|
||||
(f.selector == sig:pushBack(bytes32).selector && fullBefore ) ||
|
||||
f.selector == sig:at_(uint256).selector // revert conditions are verified in onlyOutOfBoundsRevert
|
||||
), "only revert if empty or out of bounds";
|
||||
}
|
||||
|
||||
@ -302,9 +245,6 @@ rule onlyEmptyRevert(env e) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule onlyOutOfBoundsRevert(uint256 index) {
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
|
||||
at_@withrevert(index);
|
||||
|
||||
assert lastReverted <=> index >= length(), "only reverts if index is out of bounds";
|
||||
@ -316,9 +256,6 @@ rule onlyOutOfBoundsRevert(uint256 index) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule noLengthChange(env e) {
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
|
||||
method f;
|
||||
calldataarg args;
|
||||
|
||||
@ -327,11 +264,11 @@ rule noLengthChange(env e) {
|
||||
uint256 lengthAfter = length();
|
||||
|
||||
assert lengthAfter != lengthBefore => (
|
||||
(f.selector == pushFront(bytes32).selector && lengthAfter == lengthBefore + 1) ||
|
||||
(f.selector == pushBack(bytes32).selector && lengthAfter == lengthBefore + 1) ||
|
||||
(f.selector == popBack().selector && lengthAfter == lengthBefore - 1) ||
|
||||
(f.selector == popFront().selector && lengthAfter == lengthBefore - 1) ||
|
||||
(f.selector == clear().selector && lengthAfter == 0)
|
||||
(f.selector == sig:pushFront(bytes32).selector && to_mathint(lengthAfter) == lengthBefore + 1) ||
|
||||
(f.selector == sig:pushBack(bytes32).selector && to_mathint(lengthAfter) == lengthBefore + 1) ||
|
||||
(f.selector == sig:popBack().selector && to_mathint(lengthAfter) == lengthBefore - 1) ||
|
||||
(f.selector == sig:popFront().selector && to_mathint(lengthAfter) == lengthBefore - 1) ||
|
||||
(f.selector == sig:clear().selector && lengthAfter == 0)
|
||||
), "length is only affected by clear/pop/push operations";
|
||||
}
|
||||
|
||||
@ -341,9 +278,6 @@ rule noLengthChange(env e) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule noDataChange(env e) {
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
|
||||
method f;
|
||||
calldataarg args;
|
||||
|
||||
@ -354,13 +288,13 @@ rule noDataChange(env e) {
|
||||
bool atAfterSuccess = !lastReverted;
|
||||
|
||||
assert !atAfterSuccess <=> (
|
||||
f.selector == clear().selector ||
|
||||
(f.selector == popBack().selector && index == length()) ||
|
||||
(f.selector == popFront().selector && index == length())
|
||||
(f.selector == sig:clear().selector ) ||
|
||||
(f.selector == sig:popBack().selector && index == length()) ||
|
||||
(f.selector == sig:popFront().selector && index == length())
|
||||
), "indexes of the queue are only removed by clear or pop";
|
||||
|
||||
assert atAfterSuccess && atAfter != atBefore => (
|
||||
f.selector == popFront().selector ||
|
||||
f.selector == pushFront(bytes32).selector
|
||||
f.selector == sig:popFront().selector ||
|
||||
f.selector == sig:pushFront(bytes32).selector
|
||||
), "values of the queue are only changed by popFront or pushFront";
|
||||
}
|
||||
|
||||
@ -1 +1,7 @@
|
||||
// environment
|
||||
definition nonpayable(env e) returns bool = e.msg.value == 0;
|
||||
definition nonzerosender(env e) returns bool = e.msg.sender != 0;
|
||||
|
||||
// math
|
||||
definition min(mathint a, mathint b) returns mathint = a < b ? a : b;
|
||||
definition max(mathint a, mathint b) returns mathint = a > b ? a : b;
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "./IAccessControl.sol";
|
||||
import "../utils/Context.sol";
|
||||
import "../utils/Strings.sol";
|
||||
import "../utils/introspection/ERC165.sol";
|
||||
import {IAccessControl} from "./IAccessControl.sol";
|
||||
import {Context} from "../utils/Context.sol";
|
||||
import {ERC165} from "../utils/introspection/ERC165.sol";
|
||||
|
||||
/**
|
||||
* @dev Contract module that allows children to implement role-based access
|
||||
@ -59,13 +58,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
|
||||
/**
|
||||
* @dev Modifier that checks that an account has a specific role. Reverts
|
||||
* with a standardized message including the required role.
|
||||
*
|
||||
* The format of the revert reason is given by the following regular expression:
|
||||
*
|
||||
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
|
||||
*
|
||||
* _Available since v4.1._
|
||||
* with an {AccessControlUnauthorizedAccount} error including the required role.
|
||||
*/
|
||||
modifier onlyRole(bytes32 role) {
|
||||
_checkRole(role);
|
||||
@ -82,41 +75,25 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
/**
|
||||
* @dev Returns `true` if `account` has been granted `role`.
|
||||
*/
|
||||
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
|
||||
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
|
||||
return _roles[role].members[account];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
|
||||
* Overriding this function changes the behavior of the {onlyRole} modifier.
|
||||
*
|
||||
* Format of the revert message is described in {_checkRole}.
|
||||
*
|
||||
* _Available since v4.6._
|
||||
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
|
||||
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
|
||||
*/
|
||||
function _checkRole(bytes32 role) internal view virtual {
|
||||
_checkRole(role, _msgSender());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Revert with a standard message if `account` is missing `role`.
|
||||
*
|
||||
* The format of the revert reason is given by the following regular expression:
|
||||
*
|
||||
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
|
||||
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
|
||||
* is missing `role`.
|
||||
*/
|
||||
function _checkRole(bytes32 role, address account) internal view virtual {
|
||||
if (!hasRole(role, account)) {
|
||||
revert(
|
||||
string(
|
||||
abi.encodePacked(
|
||||
"AccessControl: account ",
|
||||
Strings.toHexString(account),
|
||||
" is missing role ",
|
||||
Strings.toHexString(uint256(role), 32)
|
||||
)
|
||||
)
|
||||
);
|
||||
revert AccessControlUnauthorizedAccount(account, role);
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,7 +103,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
*
|
||||
* To change a role's admin, use {_setRoleAdmin}.
|
||||
*/
|
||||
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
|
||||
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
|
||||
return _roles[role].adminRole;
|
||||
}
|
||||
|
||||
@ -142,7 +119,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
*
|
||||
* May emit a {RoleGranted} event.
|
||||
*/
|
||||
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
|
||||
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
|
||||
_grantRole(role, account);
|
||||
}
|
||||
|
||||
@ -157,7 +134,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
*
|
||||
* May emit a {RoleRevoked} event.
|
||||
*/
|
||||
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
|
||||
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
|
||||
_revokeRole(role, account);
|
||||
}
|
||||
|
||||
@ -173,38 +150,16 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - the caller must be `account`.
|
||||
* - the caller must be `callerConfirmation`.
|
||||
*
|
||||
* May emit a {RoleRevoked} event.
|
||||
*/
|
||||
function renounceRole(bytes32 role, address account) public virtual override {
|
||||
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
|
||||
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
|
||||
if (callerConfirmation != _msgSender()) {
|
||||
revert AccessControlBadConfirmation();
|
||||
}
|
||||
|
||||
_revokeRole(role, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Grants `role` to `account`.
|
||||
*
|
||||
* If `account` had not been already granted `role`, emits a {RoleGranted}
|
||||
* event. Note that unlike {grantRole}, this function doesn't perform any
|
||||
* checks on the calling account.
|
||||
*
|
||||
* May emit a {RoleGranted} event.
|
||||
*
|
||||
* [WARNING]
|
||||
* ====
|
||||
* This function should only be called from the constructor when setting
|
||||
* up the initial roles for the system.
|
||||
*
|
||||
* Using this function in any other way is effectively circumventing the admin
|
||||
* system imposed by {AccessControl}.
|
||||
* ====
|
||||
*
|
||||
* NOTE: This function is deprecated in favor of {_grantRole}.
|
||||
*/
|
||||
function _setupRole(bytes32 role, address account) internal virtual {
|
||||
_grantRole(role, account);
|
||||
_revokeRole(role, callerConfirmation);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -219,30 +174,36 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Grants `role` to `account`.
|
||||
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
|
||||
*
|
||||
* Internal function without access restriction.
|
||||
*
|
||||
* May emit a {RoleGranted} event.
|
||||
*/
|
||||
function _grantRole(bytes32 role, address account) internal virtual {
|
||||
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
|
||||
if (!hasRole(role, account)) {
|
||||
_roles[role].members[account] = true;
|
||||
emit RoleGranted(role, account, _msgSender());
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Revokes `role` from `account`.
|
||||
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
|
||||
*
|
||||
* Internal function without access restriction.
|
||||
*
|
||||
* May emit a {RoleRevoked} event.
|
||||
*/
|
||||
function _revokeRole(bytes32 role, address account) internal virtual {
|
||||
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
|
||||
if (hasRole(role, account)) {
|
||||
_roles[role].members[account] = false;
|
||||
emit RoleRevoked(role, account, _msgSender());
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.6.0) (access/AccessControlCrossChain.sol)
|
||||
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import "./AccessControl.sol";
|
||||
import "../crosschain/CrossChainEnabled.sol";
|
||||
|
||||
/**
|
||||
* @dev An extension to {AccessControl} with support for cross-chain access management.
|
||||
* For each role, is extension implements an equivalent "aliased" role that is used for
|
||||
* restricting calls originating from other chains.
|
||||
*
|
||||
* For example, if a function `myFunction` is protected by `onlyRole(SOME_ROLE)`, and
|
||||
* if an address `x` has role `SOME_ROLE`, it would be able to call `myFunction` directly.
|
||||
* A wallet or contract at the same address on another chain would however not be able
|
||||
* to call this function. In order to do so, it would require to have the role
|
||||
* `_crossChainRoleAlias(SOME_ROLE)`.
|
||||
*
|
||||
* This aliasing is required to protect against multiple contracts living at the same
|
||||
* address on different chains but controlled by conflicting entities.
|
||||
*
|
||||
* _Available since v4.6._
|
||||
*/
|
||||
abstract contract AccessControlCrossChain is AccessControl, CrossChainEnabled {
|
||||
bytes32 public constant CROSSCHAIN_ALIAS = keccak256("CROSSCHAIN_ALIAS");
|
||||
|
||||
/**
|
||||
* @dev See {AccessControl-_checkRole}.
|
||||
*/
|
||||
function _checkRole(bytes32 role) internal view virtual override {
|
||||
if (_isCrossChain()) {
|
||||
_checkRole(_crossChainRoleAlias(role), _crossChainSender());
|
||||
} else {
|
||||
super._checkRole(role);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the aliased role corresponding to `role`.
|
||||
*/
|
||||
function _crossChainRoleAlias(bytes32 role) internal pure virtual returns (bytes32) {
|
||||
return role ^ CROSSCHAIN_ALIAS;
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,29 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
/**
|
||||
* @dev External interface of AccessControl declared to support ERC165 detection.
|
||||
*/
|
||||
interface IAccessControl {
|
||||
/**
|
||||
* @dev The `account` is missing a role.
|
||||
*/
|
||||
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
|
||||
|
||||
/**
|
||||
* @dev The caller of a function is not the expected one.
|
||||
*
|
||||
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
|
||||
*/
|
||||
error AccessControlBadConfirmation();
|
||||
|
||||
/**
|
||||
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
|
||||
*
|
||||
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
|
||||
* {RoleAdminChanged} not being emitted signaling this.
|
||||
*
|
||||
* _Available since v3.1._
|
||||
*/
|
||||
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
|
||||
|
||||
@ -82,7 +92,7 @@ interface IAccessControl {
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - the caller must be `account`.
|
||||
* - the caller must be `callerConfirmation`.
|
||||
*/
|
||||
function renounceRole(bytes32 role, address account) external;
|
||||
function renounceRole(bytes32 role, address callerConfirmation) external;
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../utils/Context.sol";
|
||||
import {Context} from "../utils/Context.sol";
|
||||
|
||||
/**
|
||||
* @dev Contract module which provides a basic access control mechanism, where
|
||||
* there is an account (an owner) that can be granted exclusive access to
|
||||
* specific functions.
|
||||
*
|
||||
* By default, the owner account will be the one that deploys the contract. This
|
||||
* can later be changed with {transferOwnership}.
|
||||
* The initial owner is set to the address provided by the deployer. This can
|
||||
* later be changed with {transferOwnership}.
|
||||
*
|
||||
* This module is used through inheritance. It will make available the modifier
|
||||
* `onlyOwner`, which can be applied to your functions to restrict their use to
|
||||
@ -20,13 +20,23 @@ import "../utils/Context.sol";
|
||||
abstract contract Ownable is Context {
|
||||
address private _owner;
|
||||
|
||||
/**
|
||||
* @dev The caller account is not authorized to perform an operation.
|
||||
*/
|
||||
error OwnableUnauthorizedAccount(address account);
|
||||
|
||||
/**
|
||||
* @dev The owner is not a valid owner account. (eg. `address(0)`)
|
||||
*/
|
||||
error OwnableInvalidOwner(address owner);
|
||||
|
||||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
||||
|
||||
/**
|
||||
* @dev Initializes the contract setting the deployer as the initial owner.
|
||||
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
|
||||
*/
|
||||
constructor() {
|
||||
_transferOwnership(_msgSender());
|
||||
constructor(address initialOwner) {
|
||||
_transferOwnership(initialOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,7 +58,9 @@ abstract contract Ownable is Context {
|
||||
* @dev Throws if the sender is not the owner.
|
||||
*/
|
||||
function _checkOwner() internal view virtual {
|
||||
require(owner() == _msgSender(), "Ownable: caller is not the owner");
|
||||
if (owner() != _msgSender()) {
|
||||
revert OwnableUnauthorizedAccount(_msgSender());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,7 +79,9 @@ abstract contract Ownable is Context {
|
||||
* Can only be called by the current owner.
|
||||
*/
|
||||
function transferOwnership(address newOwner) public virtual onlyOwner {
|
||||
require(newOwner != address(0), "Ownable: new owner is the zero address");
|
||||
if (newOwner == address(0)) {
|
||||
revert OwnableInvalidOwner(address(0));
|
||||
}
|
||||
_transferOwnership(newOwner);
|
||||
}
|
||||
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "./Ownable.sol";
|
||||
import {Ownable} from "./Ownable.sol";
|
||||
|
||||
/**
|
||||
* @dev Contract module which provides access control mechanism, where
|
||||
* there is an account (an owner) that can be granted exclusive access to
|
||||
* specific functions.
|
||||
*
|
||||
* By default, the owner account will be the one that deploys the contract. This
|
||||
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
|
||||
* can later be changed with {transferOwnership} and {acceptOwnership}.
|
||||
*
|
||||
* This module is used through inheritance. It will make available all functions
|
||||
@ -51,7 +51,9 @@ abstract contract Ownable2Step is Ownable {
|
||||
*/
|
||||
function acceptOwnership() public virtual {
|
||||
address sender = _msgSender();
|
||||
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
|
||||
if (pendingOwner() != sender) {
|
||||
revert OwnableUnauthorizedAccount(sender);
|
||||
}
|
||||
_transferOwnership(sender);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ This directory provides ways to restrict who can access the functions of a contr
|
||||
- {AccessControl} provides a general role based access control mechanism. Multiple hierarchical roles can be created and assigned each to multiple accounts.
|
||||
- {Ownable} is a simpler mechanism with a single owner "role" that can be assigned to a single account. This simpler mechanism can be useful for quick tests but projects with production concerns are likely to outgrow it.
|
||||
|
||||
== Authorization
|
||||
== Core
|
||||
|
||||
{{Ownable}}
|
||||
|
||||
@ -18,10 +18,22 @@ This directory provides ways to restrict who can access the functions of a contr
|
||||
|
||||
{{AccessControl}}
|
||||
|
||||
{{AccessControlCrossChain}}
|
||||
== Extensions
|
||||
|
||||
{{IAccessControlEnumerable}}
|
||||
|
||||
{{AccessControlEnumerable}}
|
||||
|
||||
{{IAccessControlDefaultAdminRules}}
|
||||
|
||||
{{AccessControlDefaultAdminRules}}
|
||||
|
||||
== AccessManager
|
||||
|
||||
{{IAuthority}}
|
||||
|
||||
{{AccessManager}}
|
||||
|
||||
{{AccessManaged}}
|
||||
|
||||
{{AccessManagerAdapter}}
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControlDefaultAdminRules.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "./AccessControl.sol";
|
||||
import "./IAccessControlDefaultAdminRules.sol";
|
||||
import "../utils/math/SafeCast.sol";
|
||||
import "../interfaces/IERC5313.sol";
|
||||
import {IAccessControlDefaultAdminRules} from "./IAccessControlDefaultAdminRules.sol";
|
||||
import {AccessControl, IAccessControl} from "../AccessControl.sol";
|
||||
import {SafeCast} from "../../utils/math/SafeCast.sol";
|
||||
import {Math} from "../../utils/math/Math.sol";
|
||||
import {IERC5313} from "../../interfaces/IERC5313.sol";
|
||||
|
||||
/**
|
||||
* @dev Extension of {AccessControl} that allows specifying special rules to manage
|
||||
@ -34,8 +35,6 @@ import "../interfaces/IERC5313.sol";
|
||||
* ) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* _Available since v4.9._
|
||||
*/
|
||||
abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRules, IERC5313, AccessControl {
|
||||
// pending admin pair read/written together frequently
|
||||
@ -53,7 +52,9 @@ abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRu
|
||||
* @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address.
|
||||
*/
|
||||
constructor(uint48 initialDelay, address initialDefaultAdmin) {
|
||||
require(initialDefaultAdmin != address(0), "AccessControl: 0 default admin");
|
||||
if (initialDefaultAdmin == address(0)) {
|
||||
revert AccessControlInvalidDefaultAdmin(address(0));
|
||||
}
|
||||
_currentDelay = initialDelay;
|
||||
_grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);
|
||||
}
|
||||
@ -80,7 +81,9 @@ abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRu
|
||||
* @dev See {AccessControl-grantRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
|
||||
*/
|
||||
function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
|
||||
require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't directly grant default admin role");
|
||||
if (role == DEFAULT_ADMIN_ROLE) {
|
||||
revert AccessControlEnforcedDefaultAdminRules();
|
||||
}
|
||||
super.grantRole(role, account);
|
||||
}
|
||||
|
||||
@ -88,7 +91,9 @@ abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRu
|
||||
* @dev See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
|
||||
*/
|
||||
function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
|
||||
require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't directly revoke default admin role");
|
||||
if (role == DEFAULT_ADMIN_ROLE) {
|
||||
revert AccessControlEnforcedDefaultAdminRules();
|
||||
}
|
||||
super.revokeRole(role, account);
|
||||
}
|
||||
|
||||
@ -108,10 +113,9 @@ abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRu
|
||||
function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
|
||||
if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
|
||||
(address newDefaultAdmin, uint48 schedule) = pendingDefaultAdmin();
|
||||
require(
|
||||
newDefaultAdmin == address(0) && _isScheduleSet(schedule) && _hasSchedulePassed(schedule),
|
||||
"AccessControl: only can renounce in two delayed steps"
|
||||
);
|
||||
if (newDefaultAdmin != address(0) || !_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
|
||||
revert AccessControlEnforcedDefaultAdminDelay(schedule);
|
||||
}
|
||||
delete _pendingDefaultAdminSchedule;
|
||||
}
|
||||
super.renounceRole(role, account);
|
||||
@ -126,29 +130,33 @@ abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRu
|
||||
* NOTE: Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE`
|
||||
* assignable again. Make sure to guarantee this is the expected behavior in your implementation.
|
||||
*/
|
||||
function _grantRole(bytes32 role, address account) internal virtual override {
|
||||
function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
|
||||
if (role == DEFAULT_ADMIN_ROLE) {
|
||||
require(defaultAdmin() == address(0), "AccessControl: default admin already granted");
|
||||
if (defaultAdmin() != address(0)) {
|
||||
revert AccessControlEnforcedDefaultAdminRules();
|
||||
}
|
||||
_currentDefaultAdmin = account;
|
||||
}
|
||||
super._grantRole(role, account);
|
||||
return super._grantRole(role, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {AccessControl-_revokeRole}.
|
||||
*/
|
||||
function _revokeRole(bytes32 role, address account) internal virtual override {
|
||||
function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
|
||||
if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
|
||||
delete _currentDefaultAdmin;
|
||||
}
|
||||
super._revokeRole(role, account);
|
||||
return super._revokeRole(role, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {AccessControl-_setRoleAdmin}. Reverts for `DEFAULT_ADMIN_ROLE`.
|
||||
*/
|
||||
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override {
|
||||
require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't violate default admin rules");
|
||||
if (role == DEFAULT_ADMIN_ROLE) {
|
||||
revert AccessControlEnforcedDefaultAdminRules();
|
||||
}
|
||||
super._setRoleAdmin(role, adminRole);
|
||||
}
|
||||
|
||||
@ -236,7 +244,10 @@ abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRu
|
||||
*/
|
||||
function acceptDefaultAdminTransfer() public virtual {
|
||||
(address newDefaultAdmin, ) = pendingDefaultAdmin();
|
||||
require(_msgSender() == newDefaultAdmin, "AccessControl: pending admin must accept");
|
||||
if (_msgSender() != newDefaultAdmin) {
|
||||
// Enforce newDefaultAdmin explicit acceptance.
|
||||
revert AccessControlInvalidDefaultAdmin(_msgSender());
|
||||
}
|
||||
_acceptDefaultAdminTransfer();
|
||||
}
|
||||
|
||||
@ -247,7 +258,9 @@ abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRu
|
||||
*/
|
||||
function _acceptDefaultAdminTransfer() internal virtual {
|
||||
(address newAdmin, uint48 schedule) = pendingDefaultAdmin();
|
||||
require(_isScheduleSet(schedule) && _hasSchedulePassed(schedule), "AccessControl: transfer delay not passed");
|
||||
if (!_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
|
||||
revert AccessControlEnforcedDefaultAdminDelay(schedule);
|
||||
}
|
||||
_revokeRole(DEFAULT_ADMIN_ROLE, defaultAdmin());
|
||||
_grantRole(DEFAULT_ADMIN_ROLE, newAdmin);
|
||||
delete _pendingDefaultAdmin;
|
||||
@ -1,11 +1,11 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "./IAccessControlEnumerable.sol";
|
||||
import "./AccessControl.sol";
|
||||
import "../utils/structs/EnumerableSet.sol";
|
||||
import {IAccessControlEnumerable} from "./IAccessControlEnumerable.sol";
|
||||
import {AccessControl} from "../AccessControl.sol";
|
||||
import {EnumerableSet} from "../../utils/structs/EnumerableSet.sol";
|
||||
|
||||
/**
|
||||
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
|
||||
@ -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 virtual override returns (address) {
|
||||
function getRoleMember(bytes32 role, uint256 index) public view virtual returns (address) {
|
||||
return _roleMembers[role].at(index);
|
||||
}
|
||||
|
||||
@ -42,23 +42,29 @@ 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 virtual override returns (uint256) {
|
||||
function getRoleMemberCount(bytes32 role) public view virtual returns (uint256) {
|
||||
return _roleMembers[role].length();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Overload {_grantRole} to track enumerable memberships
|
||||
* @dev Overload {AccessControl-_grantRole} to track enumerable memberships
|
||||
*/
|
||||
function _grantRole(bytes32 role, address account) internal virtual override {
|
||||
super._grantRole(role, account);
|
||||
_roleMembers[role].add(account);
|
||||
function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
|
||||
bool granted = super._grantRole(role, account);
|
||||
if (granted) {
|
||||
_roleMembers[role].add(account);
|
||||
}
|
||||
return granted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Overload {_revokeRole} to track enumerable memberships
|
||||
* @dev Overload {AccessControl-_revokeRole} to track enumerable memberships
|
||||
*/
|
||||
function _revokeRole(bytes32 role, address account) internal virtual override {
|
||||
super._revokeRole(role, account);
|
||||
_roleMembers[role].remove(account);
|
||||
function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
|
||||
bool revoked = super._revokeRole(role, account);
|
||||
if (revoked) {
|
||||
_roleMembers[role].remove(account);
|
||||
}
|
||||
return revoked;
|
||||
}
|
||||
}
|
||||
@ -1,16 +1,36 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (access/IAccessControlDefaultAdminRules.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "./IAccessControl.sol";
|
||||
import {IAccessControl} from "../IAccessControl.sol";
|
||||
|
||||
/**
|
||||
* @dev External interface of AccessControlDefaultAdminRules declared to support ERC165 detection.
|
||||
*
|
||||
* _Available since v4.9._
|
||||
*/
|
||||
interface IAccessControlDefaultAdminRules is IAccessControl {
|
||||
/**
|
||||
* @dev The new default admin is not a valid default admin.
|
||||
*/
|
||||
error AccessControlInvalidDefaultAdmin(address defaultAdmin);
|
||||
|
||||
/**
|
||||
* @dev At least one of the following rules was violated:
|
||||
*
|
||||
* - The `DEFAULT_ADMIN_ROLE` must only be managed by itself.
|
||||
* - The `DEFAULT_ADMIN_ROLE` must only be held by one account at the time.
|
||||
* - Any `DEFAULT_ADMIN_ROLE` transfer must be in two delayed steps.
|
||||
*/
|
||||
error AccessControlEnforcedDefaultAdminRules();
|
||||
|
||||
/**
|
||||
* @dev The delay for transferring the default admin delay is enforced and
|
||||
* the operation must wait until `schedule`.
|
||||
*
|
||||
* NOTE: `schedule` can be 0 indicating there's no transfer scheduled.
|
||||
*/
|
||||
error AccessControlEnforcedDefaultAdminDelay(uint48 schedule);
|
||||
|
||||
/**
|
||||
* @dev Emitted when a {defaultAdmin} transfer is started, setting `newAdmin` as the next
|
||||
* address to become the {defaultAdmin} by calling {acceptDefaultAdminTransfer} only after `acceptSchedule`
|
||||
@ -1,9 +1,9 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "./IAccessControl.sol";
|
||||
import {IAccessControl} from "../IAccessControl.sol";
|
||||
|
||||
/**
|
||||
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
|
||||
121
contracts/access/manager/AccessManaged.sol
Normal file
121
contracts/access/manager/AccessManaged.sol
Normal file
@ -0,0 +1,121 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {IAuthority} from "./IAuthority.sol";
|
||||
import {AuthorityUtils} from "./AuthorityUtils.sol";
|
||||
import {IAccessManager} from "./IAccessManager.sol";
|
||||
import {IAccessManaged} from "./IAccessManaged.sol";
|
||||
import {Context} from "../../utils/Context.sol";
|
||||
|
||||
/**
|
||||
* @dev This contract module makes available a {restricted} modifier. Functions decorated with this modifier will be
|
||||
* permissioned according to an "authority": a contract like {AccessManager} that follows the {IAuthority} interface,
|
||||
* implementing a policy that allows certain callers to access certain functions.
|
||||
*
|
||||
* IMPORTANT: The `restricted` modifier should never be used on `internal` functions, judiciously used in `public`
|
||||
* functions, and ideally only used in `external` functions. See {restricted}.
|
||||
*/
|
||||
abstract contract AccessManaged is Context, IAccessManaged {
|
||||
address private _authority;
|
||||
|
||||
bool private _consumingSchedule;
|
||||
|
||||
/**
|
||||
* @dev Initializes the contract connected to an initial authority.
|
||||
*/
|
||||
constructor(address initialAuthority) {
|
||||
_setAuthority(initialAuthority);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Restricts access to a function as defined by the connected Authority for this contract and the
|
||||
* caller and selector of the function that entered the contract.
|
||||
*
|
||||
* [IMPORTANT]
|
||||
* ====
|
||||
* In general, this modifier should only be used on `external` functions. It is okay to use it on `public`
|
||||
* functions that are used as external entry points and are not called internally. Unless you know what you're
|
||||
* doing, it should never be used on `internal` functions. Failure to follow these rules can have critical security
|
||||
* implications! This is because the permissions are determined by the function that entered the contract, i.e. the
|
||||
* function at the bottom of the call stack, and not the function where the modifier is visible in the source code.
|
||||
* ====
|
||||
*
|
||||
* [NOTE]
|
||||
* ====
|
||||
* Selector collisions are mitigated by scoping permissions per contract, but some edge cases must be considered:
|
||||
*
|
||||
* * If the https://docs.soliditylang.org/en/v0.8.20/contracts.html#receive-ether-function[`receive()`] function
|
||||
* is restricted, any other function with a `0x00000000` selector will share permissions with `receive()`.
|
||||
* * Similarly, if there's no `receive()` function but a `fallback()` instead, the fallback might be called with
|
||||
* empty `calldata`, sharing the `0x00000000` selector permissions as well.
|
||||
* * For any other selector, if the restricted function is set on an upgradeable contract, an upgrade may remove
|
||||
* the restricted function and replace it with a new method whose selector replaces the last one, keeping the
|
||||
* previous permissions.
|
||||
* ====
|
||||
*/
|
||||
modifier restricted() {
|
||||
_checkCanCall(_msgSender(), _msgData());
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the current authority.
|
||||
*/
|
||||
function authority() public view virtual returns (address) {
|
||||
return _authority;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfers control to a new authority. The caller must be the current authority.
|
||||
*/
|
||||
function setAuthority(address newAuthority) public virtual {
|
||||
address caller = _msgSender();
|
||||
if (caller != authority()) {
|
||||
revert AccessManagedUnauthorized(caller);
|
||||
}
|
||||
if (newAuthority.code.length == 0) {
|
||||
revert AccessManagedInvalidAuthority(newAuthority);
|
||||
}
|
||||
_setAuthority(newAuthority);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns true only in the context of a delayed restricted call, at the moment that the scheduled operation is
|
||||
* being consumed. Prevents denial of service for delayed restricted calls in the case that the contract performs
|
||||
* attacker controlled calls.
|
||||
*/
|
||||
function isConsumingScheduledOp() public view returns (bool) {
|
||||
return _consumingSchedule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfers control to a new authority. Internal function with no access restriction. Allows bypassing the
|
||||
* permissions set by the current authority.
|
||||
*/
|
||||
function _setAuthority(address newAuthority) internal virtual {
|
||||
_authority = newAuthority;
|
||||
emit AuthorityUpdated(newAuthority);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Reverts if the caller is not allowed to call the function identified by a selector.
|
||||
*/
|
||||
function _checkCanCall(address caller, bytes calldata data) internal virtual {
|
||||
(bool allowed, uint32 delay) = AuthorityUtils.canCallWithDelay(
|
||||
authority(),
|
||||
caller,
|
||||
address(this),
|
||||
bytes4(data)
|
||||
);
|
||||
if (!allowed) {
|
||||
if (delay > 0) {
|
||||
_consumingSchedule = true;
|
||||
IAccessManager(authority()).consumeScheduledOp(caller, data);
|
||||
_consumingSchedule = false;
|
||||
} else {
|
||||
revert AccessManagedUnauthorized(caller);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user