Compare commits
40 Commits
audit/2023
...
v4.9.5
| Author | SHA1 | Date | |
|---|---|---|---|
| bd325d56b4 | |||
| ad6a5b6893 | |||
| 88ac712e06 | |||
| a83918df14 | |||
| 0d5f54e69b | |||
| ccfffe13e8 | |||
| 9329cfacd4 | |||
| e1b3d8c7ee | |||
| 98c7a4cf95 | |||
| 1014ac2924 | |||
| 0ed435b7b1 | |||
| 17c1a3a458 | |||
| fd81a96f01 | |||
| 70dea74ac7 | |||
| e4435eed75 | |||
| 7ec712baa5 | |||
| d26025b410 | |||
| a54f6398e5 | |||
| e50c24f583 | |||
| 4d2383e171 | |||
| f03420b5c7 | |||
| ded8c9eedb | |||
| 281550b71c | |||
| 33ff9b086d | |||
| fa3a30a580 | |||
| 4e6deb3c56 | |||
| 819820517d | |||
| a6e2671690 | |||
| 54b3f14346 | |||
| 813cc2b79d | |||
| 4f7047ceec | |||
| a43069e841 | |||
| e0fe936729 | |||
| 652ae921e1 | |||
| 9673c56eba | |||
| 46d5a87c00 | |||
| f214e476e6 | |||
| 17cf519425 | |||
| 43eb8d1265 | |||
| 2d2c5f348d |
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC1155Receiver`: Removed in favor of `ERC1155Holder`.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'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))
|
||||
@ -1,4 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
Use `abi.encodeCall` in place of `abi.encodeWithSelector` and `abi.encodeWithSignature` for improved type-checking of parameters
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC2771Forwarder`: Added `deadline` for expiring transactions, batching, and more secure handling of `msg.value`.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`Math`: Make `ceilDiv` to revert on 0 division even if the numerator is 0
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC20`, `ERC721`, `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), [#4377](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/4377))
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'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 .
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Ownable`: Add an `initialOwner` parameter to the constructor, making the ownership initialization explicit.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`Proxy`: Removed redundant `receive` function.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
Optimize `Strings.equal`
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC721`: `_approve` no longer allows approving the owner of the tokenId. `_setApprovalForAll` no longer allows setting address(0) as an operator.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'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.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
Replace some uses of `abi.encodePacked` with clearer alternatives (e.g. `bytes.concat`, `string.concat`).
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`Arrays`: Optimize `findUpperBound` by removing redundant SLOAD.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`ECDSA`: Use unchecked arithmetic for the `tryRecover` function that receives the `r` and `vs` short-signature fields separately.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Checkpoints`: library moved from `utils` to `utils/structs`
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Governor`: Optimized use of storage for proposal data
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC1967Utils`: Refactor the `ERC1967Upgrade` abstract contract as a library.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'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`.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'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.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
Move the logic to validate ERC-1822 during an upgrade from `ERC1967Utils` to `UUPSUpgradeable`.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`Arrays`: Add `unsafeMemoryAccess` helpers to read from a memory array without checking the length.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'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.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`GovernorTimelockControl`: Clean up timelock id on execution for gas refund.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`EIP712`: Add internal getters for the name and version strings
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`TimelockController`: Add a state getter that returns an `OperationState` enum.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
Replace revert strings and require statements with custom errors.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'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))
|
||||
@ -1,6 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`Governor`: Add validation in ERC1155 and ERC721 receiver hooks to ensure Governor is the executor.
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
Switched to using explicit Solidity import statements. Some previously available symbols may now have to be separately imported.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'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.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'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.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'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.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'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`.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ProxyAdmin`: Removed `getProxyAdmin` and `getProxyImplementation` getters. ([#3820](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3820))
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`ERC1155`: Optimize array allocation.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
Bump minimum compiler version required to 0.8.19
|
||||
@ -1,7 +0,0 @@
|
||||
---
|
||||
'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
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'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.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'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))
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`ERC1155`: Remove check for address zero in `balanceOf`.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`access`: Move `AccessControl` extensions to a dedicated directory.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`ERC721Consecutive`: Add a `_firstConsecutiveId` internal function that can be overridden to change the id of the first token minted through `_mintConsecutive`.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`DoubleEndedQueue`: refactor internal structure to use `uint128` instead of `int128`. This has no effect on the library interface.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`Governor`: Add a mechanism to restrict the address of the proposer using a suffix in the description.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Strings`: Rename `toString(int256)` to `toStringSigned(int256)`.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`BeaconProxy`: Reject value in initialization unless a payable function is explicitly invoked.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`ERC1155`: Bubble errors triggered in the `onERC1155Received` and `onERC1155BatchReceived` hooks.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': patch
|
||||
---
|
||||
|
||||
`ERC1155`: Optimize array accesses by skipping bounds checking when unnecessary.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
Remove the `override` specifier from functions that only override a single interface function.
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`Math`: Renamed members of `Rounding` enum, and added a new rounding mode for "away from zero".
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
'openzeppelin-solidity': major
|
||||
---
|
||||
|
||||
`SafeERC20`: Refactor `safeDecreaseAllowance` and `safeIncreaseAllowance` to support USDT-like tokens.
|
||||
2
.github/actions/setup/action.yml
vendored
2
.github/actions/setup/action.yml
vendored
@ -5,7 +5,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 16.x
|
||||
- uses: actions/cache@v3
|
||||
id: cache
|
||||
with:
|
||||
|
||||
4
.github/workflows/checks.yml
vendored
4
.github/workflows/checks.yml
vendored
@ -4,7 +4,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- next-v*
|
||||
- release-v*
|
||||
pull_request: {}
|
||||
workflow_dispatch: {}
|
||||
@ -106,8 +105,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run CodeSpell
|
||||
uses: codespell-project/actions-codespell@v2.0
|
||||
uses: codespell-project/actions-codespell@v1.0
|
||||
with:
|
||||
check_hidden: true
|
||||
check_filenames: true
|
||||
skip: package-lock.json,*.pdf
|
||||
|
||||
14
.solhint.json
Normal file
14
.solhint.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
76
CHANGELOG.md
76
CHANGELOG.md
@ -1,79 +1,19 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
> **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.
|
||||
## 4.9.5 (2023-12-08)
|
||||
|
||||
### Removals
|
||||
- `Multicall`: Patch duplicated `Address.functionDelegateCall`.
|
||||
|
||||
The following contracts, libraries and functions were removed:
|
||||
## 4.9.4 (2023-12-07)
|
||||
|
||||
- `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/)
|
||||
- `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`.
|
||||
|
||||
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)
|
||||
## 4.9.3 (2023-07-28)
|
||||
|
||||
### 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. `_transfer`, `_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);
|
||||
}
|
||||
```
|
||||
|
||||
### More about ERC721
|
||||
|
||||
In the case of `ERC721`, the `_update` function does not include a `from` parameter, as the sender is implicitly the previous owner of the `tokenId`. The address of
|
||||
this previous owner is returned by the `_update` function, so it can be used for a posteriori checks. In addition to `to` and `tokenId`, a third parameter (`auth`) is
|
||||
present in this function. This parameter enabled an optional check that the caller/spender is approved to do the transfer. This check cannot be performed after the transfer (because the transfer resets the approval), and doing it before `_update` would require a duplicate call to `_ownerOf`.
|
||||
|
||||
In this logic of removing hidden SLOADs, the `_isApprovedOrOwner` function was removed in favor of a new `_isAuthorized` function. Overrides that used to target the
|
||||
`_isApprovedOrOwner` should now be performed on the `_isAuthorized` function. Calls to `_isApprovedOrOwner` that preceded a call to `_transfer`, `_burn` or `_approve`
|
||||
should be removed in favor of using the `auth` argument in `_update` and `_approve`. This is showcased in `ERC721Burnable.burn` and in `ERC721Wrapper.withdrawTo`.
|
||||
|
||||
The `_exists` function was removed. Calls to this function can be replaced by `_ownerOf(tokenId) != address(0)`.
|
||||
|
||||
#### 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);
|
||||
}
|
||||
```
|
||||
- `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))
|
||||
|
||||
## 4.9.2 (2023-06-16)
|
||||
|
||||
|
||||
@ -114,25 +114,4 @@ 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,6 +1,3 @@
|
||||
> **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)
|
||||
@ -23,34 +20,22 @@
|
||||
|
||||
### 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.
|
||||
|
||||
#### 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.`
|
||||
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.
|
||||
|
||||
### Usage
|
||||
|
||||
Once installed, you can use the contracts in the library by importing them:
|
||||
|
||||
```solidity
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
||||
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
||||
|
||||
contract MyCollectible is ERC721 {
|
||||
constructor() ERC721("MyCollectible", "MCO") {
|
||||
@ -82,9 +67,7 @@ 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 [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 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 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.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../patched/access/AccessControlDefaultAdminRules.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../patched/access/AccessControl.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../patched/utils/structs/DoubleEndedQueue.sol";
|
||||
|
||||
@ -29,11 +29,11 @@ contract DoubleEndedQueueHarness {
|
||||
_deque.clear();
|
||||
}
|
||||
|
||||
function begin() external view returns (uint128) {
|
||||
function begin() external view returns (int128) {
|
||||
return _deque._begin;
|
||||
}
|
||||
|
||||
function end() external view returns (uint128) {
|
||||
function end() external view returns (int128) {
|
||||
return _deque._end;
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
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.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../patched/token/ERC20/extensions/ERC20Permit.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../patched/token/ERC20/extensions/ERC20Wrapper.sol";
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import "../patched/interfaces/IERC3156FlashBorrower.sol";
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract ERC3156FlashBorrowerHarness is IERC3156FlashBorrower {
|
||||
bytes32 somethingToReturn;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../patched/token/ERC721/ERC721.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../patched/interfaces/IERC721Receiver.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../patched/utils/structs/EnumerableMap.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../patched/utils/structs/EnumerableSet.sol";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.2;
|
||||
|
||||
import "../patched/proxy/utils/Initializable.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../patched/access/Ownable2Step.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../patched/access/Ownable.sol";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../patched/security/Pausable.sol";
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../patched/governance/TimelockController.sol";
|
||||
|
||||
|
||||
@ -28,11 +28,6 @@ const argv = require('yargs')
|
||||
type: 'number',
|
||||
default: 4,
|
||||
},
|
||||
verbose: {
|
||||
alias: 'v',
|
||||
type: 'count',
|
||||
default: 0,
|
||||
},
|
||||
options: {
|
||||
alias: 'o',
|
||||
type: 'array',
|
||||
@ -70,9 +65,6 @@ 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,23 +12,44 @@ use rule onlyGrantCanGrant filtered {
|
||||
│ Helpers │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
definition timeSanity(env e) returns bool =
|
||||
e.block.timestamp > 0 && e.block.timestamp + defaultAdminDelay(e) < max_uint48();
|
||||
|
||||
definition delayChangeWaitSanity(env e, uint48 newDelay) returns bool =
|
||||
e.block.timestamp + delayChangeWait_(e, newDelay) < max_uint48();
|
||||
function max_uint48() returns mathint {
|
||||
return (1 << 48) - 1;
|
||||
}
|
||||
|
||||
definition isSet(uint48 schedule) returns bool =
|
||||
schedule != 0;
|
||||
function nonZeroAccount(address account) returns bool {
|
||||
return account != 0;
|
||||
}
|
||||
|
||||
definition hasPassed(env e, uint48 schedule) returns bool =
|
||||
schedule < e.block.timestamp;
|
||||
function timeSanity(env e) returns bool {
|
||||
return
|
||||
e.block.timestamp > 0 && // Avoids 0 schedules
|
||||
e.block.timestamp + defaultAdminDelay(e) < max_uint48();
|
||||
}
|
||||
|
||||
definition increasingDelaySchedule(env e, uint48 newDelay) returns mathint =
|
||||
e.block.timestamp + min(newDelay, defaultAdminDelayIncreaseWait());
|
||||
function delayChangeWaitSanity(env e, uint48 newDelay) returns bool {
|
||||
return e.block.timestamp + delayChangeWait_(e, newDelay) < max_uint48();
|
||||
}
|
||||
|
||||
definition decreasingDelaySchedule(env e, uint48 newDelay) returns mathint =
|
||||
e.block.timestamp + defaultAdminDelay(e) - newDelay;
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
@ -36,10 +57,11 @@ definition decreasingDelaySchedule(env e, uint48 newDelay) returns mathint =
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
invariant defaultAdminConsistency(address account)
|
||||
(account == defaultAdmin() && account != 0) <=> hasRole(DEFAULT_ADMIN_ROLE(), account)
|
||||
defaultAdmin() == account <=> hasRole(DEFAULT_ADMIN_ROLE(), account)
|
||||
{
|
||||
preserved with (env e) {
|
||||
require nonzerosender(e);
|
||||
preserved {
|
||||
// defaultAdmin() returns the zero address when there's no default admin
|
||||
require nonZeroAccount(account);
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,12 +72,10 @@ invariant defaultAdminConsistency(address account)
|
||||
*/
|
||||
invariant singleDefaultAdmin(address account, address another)
|
||||
hasRole(DEFAULT_ADMIN_ROLE(), account) && hasRole(DEFAULT_ADMIN_ROLE(), another) => another == account
|
||||
{
|
||||
preserved {
|
||||
requireInvariant defaultAdminConsistency(account);
|
||||
requireInvariant defaultAdminConsistency(another);
|
||||
}
|
||||
}
|
||||
// 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 }
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
@ -98,8 +118,7 @@ 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),
|
||||
@ -118,59 +137,35 @@ rule renounceRoleEffect(env e, bytes32 role) {
|
||||
address account;
|
||||
address otherAccount;
|
||||
|
||||
bool hasOtherRoleBefore = hasRole(otherRole, otherAccount);
|
||||
address adminBefore = defaultAdmin();
|
||||
bool hasOtherRoleBefore = hasRole(otherRole, otherAccount);
|
||||
uint48 scheduleBefore = pendingDefaultAdminSchedule_();
|
||||
address pendingAdminBefore = pendingDefaultAdmin_();
|
||||
uint48 scheduleBefore = pendingDefaultAdminSchedule_();
|
||||
|
||||
renounceRole@withrevert(e, role, account);
|
||||
bool success = !lastReverted;
|
||||
|
||||
bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
|
||||
address adminAfter = defaultAdmin();
|
||||
address pendingAdminAfter = pendingDefaultAdmin_();
|
||||
uint48 scheduleAfter = pendingDefaultAdminSchedule_();
|
||||
bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
|
||||
|
||||
// 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 => (
|
||||
(
|
||||
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";
|
||||
assert success => !hasRole(role, account), "role is renounced";
|
||||
|
||||
// no side effect
|
||||
assert hasOtherRoleBefore != hasOtherRoleAfter => (
|
||||
role == otherRole &&
|
||||
account == otherAccount
|
||||
),
|
||||
assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount),
|
||||
"no other role is affected";
|
||||
}
|
||||
|
||||
@ -180,6 +175,10 @@ 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();
|
||||
@ -187,17 +186,18 @@ 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, completing (accept or renounce), or canceling an admin │
|
||||
│ transfer │
|
||||
│ Rule: pendingDefaultAdmin is only affected by beginning, accepting 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,10 +210,8 @@ rule noPendingDefaultAdminChange(env e, method f, calldataarg args) {
|
||||
) => (
|
||||
f.selector == beginDefaultAdminTransfer(address).selector ||
|
||||
f.selector == acceptDefaultAdminTransfer().selector ||
|
||||
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";
|
||||
f.selector == cancelDefaultAdminTransfer().selector
|
||||
), "pending admin and its schedule is only affected by beginning, accepting or cancelling an admin transfer";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -226,8 +224,7 @@ 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";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -243,8 +240,7 @@ 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";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -267,10 +263,10 @@ rule noDefaultAdminDelayIncreaseWaitChange(env e, method f, calldataarg args) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule beginDefaultAdminTransfer(env e, address newAdmin) {
|
||||
require timeSanity(e);
|
||||
require nonpayable(e);
|
||||
require nonzerosender(e);
|
||||
requireInvariant defaultAdminConsistency(e.msg.sender);
|
||||
require timeSanity(e);
|
||||
requireInvariant defaultAdminConsistency(defaultAdmin());
|
||||
requireInvariant singleDefaultAdmin(e.msg.sender, defaultAdmin());
|
||||
|
||||
beginDefaultAdminTransfer@withrevert(e, newAdmin);
|
||||
bool success = !lastReverted;
|
||||
@ -292,24 +288,18 @@ 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();
|
||||
|
||||
// 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";
|
||||
assert adminAfter == newAdmin => ((e2.block.timestamp >= e1.block.timestamp + delayBefore) || adminBefore == newAdmin),
|
||||
"A delay can't change in less than applied schedule";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -319,19 +309,17 @@ 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 scheduleBefore = pendingDefaultAdminSchedule_();
|
||||
uint48 scheduleAfter = pendingDefaultAdminSchedule_();
|
||||
|
||||
acceptDefaultAdminTransfer@withrevert(e);
|
||||
bool success = !lastReverted;
|
||||
|
||||
// liveness
|
||||
assert success <=> (
|
||||
e.msg.sender == pendingAdminBefore &&
|
||||
isSet(scheduleBefore) &&
|
||||
hasPassed(e, scheduleBefore)
|
||||
),
|
||||
assert success <=> e.msg.sender == pendingAdminBefore && isSet(scheduleAfter) && hasPassed(e, scheduleAfter),
|
||||
"only the pending default admin can accept the role after the schedule has been set and passed";
|
||||
|
||||
// effect
|
||||
@ -350,8 +338,8 @@ rule acceptDefaultAdminTransfer(env e) {
|
||||
*/
|
||||
rule cancelDefaultAdminTransfer(env e) {
|
||||
require nonpayable(e);
|
||||
require nonzerosender(e);
|
||||
requireInvariant defaultAdminConsistency(e.msg.sender);
|
||||
requireInvariant defaultAdminConsistency(defaultAdmin());
|
||||
requireInvariant singleDefaultAdmin(e.msg.sender, defaultAdmin());
|
||||
|
||||
cancelDefaultAdminTransfer@withrevert(e);
|
||||
bool success = !lastReverted;
|
||||
@ -373,11 +361,11 @@ rule cancelDefaultAdminTransfer(env e) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule changeDefaultAdminDelay(env e, uint48 newDelay) {
|
||||
require timeSanity(e);
|
||||
require nonpayable(e);
|
||||
require nonzerosender(e);
|
||||
require timeSanity(e);
|
||||
require delayChangeWaitSanity(e, newDelay);
|
||||
requireInvariant defaultAdminConsistency(e.msg.sender);
|
||||
requireInvariant defaultAdminConsistency(defaultAdmin());
|
||||
requireInvariant singleDefaultAdmin(e.msg.sender, defaultAdmin());
|
||||
|
||||
uint48 delayBefore = defaultAdminDelay(e);
|
||||
|
||||
@ -389,9 +377,7 @@ 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
|
||||
@ -406,22 +392,17 @@ 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 != delayBefore => (
|
||||
delayAfter == newDelay &&
|
||||
e2.block.timestamp >= delayWait
|
||||
),
|
||||
"A delay can only change after the applied schedule";
|
||||
assert delayAfter == newDelay => (e2.block.timestamp >= delayWait || delayBefore == newDelay),
|
||||
"A delay can't change in less than applied schedule";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -446,8 +427,8 @@ rule pendingDelayWait(env e, uint48 newDelay) {
|
||||
*/
|
||||
rule rollbackDefaultAdminDelay(env e) {
|
||||
require nonpayable(e);
|
||||
require nonzerosender(e);
|
||||
requireInvariant defaultAdminConsistency(e.msg.sender);
|
||||
requireInvariant defaultAdminConsistency(defaultAdmin());
|
||||
requireInvariant singleDefaultAdmin(e.msg.sender, defaultAdmin());
|
||||
|
||||
rollbackDefaultAdminDelay@withrevert(e);
|
||||
bool success = !lastReverted;
|
||||
@ -462,3 +443,58 @@ 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,25 +1,64 @@
|
||||
import "helpers/helpers.spec";
|
||||
import "helpers/helpers.spec"
|
||||
|
||||
methods {
|
||||
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;
|
||||
pushFront(bytes32) envfree
|
||||
pushBack(bytes32) envfree
|
||||
popFront() returns (bytes32) envfree
|
||||
popBack() returns (bytes32) envfree
|
||||
clear() envfree
|
||||
|
||||
// exposed for FV
|
||||
function begin() external returns (uint128) envfree;
|
||||
function end() external returns (uint128) envfree;
|
||||
begin() returns (int128) envfree
|
||||
end() returns (int128) envfree
|
||||
|
||||
// view
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
definition full() returns bool = length() == max_uint128;
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ 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(); } }
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
@ -29,19 +68,22 @@ definition full() returns bool = length() == max_uint128;
|
||||
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 queueFront()
|
||||
at_(0) == front()
|
||||
filtered { f -> !f.isView }
|
||||
|
||||
invariant queueBack()
|
||||
at_(require_uint256(length() - 1)) == back()
|
||||
invariant queueEndings()
|
||||
at_(length() - 1) == back() && at_(0) == front()
|
||||
filtered { f -> !f.isView }
|
||||
{
|
||||
preserved {
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
@ -49,18 +91,18 @@ invariant queueBack()
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule pushFront(bytes32 value) {
|
||||
require boundedQueue();
|
||||
|
||||
uint256 lengthBefore = length();
|
||||
bool fullBefore = full();
|
||||
|
||||
pushFront@withrevert(value);
|
||||
bool success = !lastReverted;
|
||||
|
||||
// liveness
|
||||
assert success <=> !fullBefore, "never revert if not previously full";
|
||||
assert !lastReverted, "never reverts";
|
||||
|
||||
// effect
|
||||
assert success => front() == value, "front set to value";
|
||||
assert success => to_mathint(length()) == lengthBefore + 1, "queue extended";
|
||||
assert front() == value, "front set to value";
|
||||
assert length() == lengthBefore + 1, "queue extended";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -69,13 +111,15 @@ 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(require_uint256(index + 1));
|
||||
bytes32 afterAt = at_@withrevert(index + 1);
|
||||
|
||||
assert !lastReverted, "value still there";
|
||||
assert afterAt == beforeAt, "data is preserved";
|
||||
@ -87,18 +131,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 success <=> !fullBefore, "never revert if not previously full";
|
||||
assert !lastReverted, "never reverts";
|
||||
|
||||
// effect
|
||||
assert success => back() == value, "back set to value";
|
||||
assert success => to_mathint(length()) == lengthBefore + 1, "queue increased";
|
||||
assert back() == value, "back set to value";
|
||||
assert length() == lengthBefore + 1, "queue increased";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -107,6 +151,8 @@ rule pushBack(bytes32 value) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule pushBackConsistency(uint256 index) {
|
||||
require boundedQueue();
|
||||
|
||||
bytes32 beforeAt = at_(index);
|
||||
|
||||
bytes32 value;
|
||||
@ -125,6 +171,9 @@ rule pushBackConsistency(uint256 index) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule popFront {
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
|
||||
uint256 lengthBefore = length();
|
||||
bytes32 frontBefore = front@withrevert();
|
||||
|
||||
@ -136,7 +185,7 @@ rule popFront {
|
||||
|
||||
// effect
|
||||
assert success => frontBefore == popped, "previous front is returned";
|
||||
assert success => to_mathint(length()) == lengthBefore - 1, "queue decreased";
|
||||
assert success => length() == lengthBefore - 1, "queue decreased";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -145,6 +194,9 @@ 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);
|
||||
@ -152,7 +204,7 @@ rule popFrontConsistency(uint256 index) {
|
||||
popFront();
|
||||
|
||||
// try to read value
|
||||
bytes32 after = at_@withrevert(require_uint256(index - 1));
|
||||
bytes32 after = at_@withrevert(index - 1);
|
||||
|
||||
assert !lastReverted, "value still exists in the queue";
|
||||
assert before == after, "values are offset and not modified";
|
||||
@ -164,6 +216,9 @@ rule popFrontConsistency(uint256 index) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule popBack {
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
|
||||
uint256 lengthBefore = length();
|
||||
bytes32 backBefore = back@withrevert();
|
||||
|
||||
@ -175,7 +230,7 @@ rule popBack {
|
||||
|
||||
// effect
|
||||
assert success => backBefore == popped, "previous back is returned";
|
||||
assert success => to_mathint(length()) == lengthBefore - 1, "queue decreased";
|
||||
assert success => length() == lengthBefore - 1, "queue decreased";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -184,8 +239,11 @@ 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 to_mathint(index) < length() - 1;
|
||||
require index < length() - 1;
|
||||
bytes32 before = at_(index);
|
||||
|
||||
popBack();
|
||||
@ -217,25 +275,24 @@ rule clear {
|
||||
│ Rule: front/back access reverts only if the queue is empty or querying out of bounds │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule onlyEmptyOrFullRevert(env e) {
|
||||
rule onlyEmptyRevert(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 == 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
|
||||
(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
|
||||
), "only revert if empty or out of bounds";
|
||||
}
|
||||
|
||||
@ -245,6 +302,9 @@ rule onlyEmptyOrFullRevert(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";
|
||||
@ -256,6 +316,9 @@ rule onlyOutOfBoundsRevert(uint256 index) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule noLengthChange(env e) {
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
|
||||
method f;
|
||||
calldataarg args;
|
||||
|
||||
@ -264,11 +327,11 @@ rule noLengthChange(env e) {
|
||||
uint256 lengthAfter = length();
|
||||
|
||||
assert lengthAfter != lengthBefore => (
|
||||
(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)
|
||||
(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)
|
||||
), "length is only affected by clear/pop/push operations";
|
||||
}
|
||||
|
||||
@ -278,6 +341,9 @@ rule noLengthChange(env e) {
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
rule noDataChange(env e) {
|
||||
requireInvariant boundariesConsistency();
|
||||
require boundedQueue();
|
||||
|
||||
method f;
|
||||
calldataarg args;
|
||||
|
||||
@ -288,13 +354,13 @@ rule noDataChange(env e) {
|
||||
bool atAfterSuccess = !lastReverted;
|
||||
|
||||
assert !atAfterSuccess <=> (
|
||||
(f.selector == sig:clear().selector ) ||
|
||||
(f.selector == sig:popBack().selector && index == length()) ||
|
||||
(f.selector == sig:popFront().selector && index == length())
|
||||
f.selector == clear().selector ||
|
||||
(f.selector == popBack().selector && index == length()) ||
|
||||
(f.selector == popFront().selector && index == length())
|
||||
), "indexes of the queue are only removed by clear or pop";
|
||||
|
||||
assert atAfterSuccess && atAfter != atBefore => (
|
||||
f.selector == sig:popFront().selector ||
|
||||
f.selector == sig:pushFront(bytes32).selector
|
||||
f.selector == popFront().selector ||
|
||||
f.selector == pushFront(bytes32).selector
|
||||
), "values of the queue are only changed by popFront or pushFront";
|
||||
}
|
||||
|
||||
@ -1,7 +1 @@
|
||||
// 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,11 +1,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {IAccessControl} from "./IAccessControl.sol";
|
||||
import {Context} from "../utils/Context.sol";
|
||||
import {ERC165} from "../utils/introspection/ERC165.sol";
|
||||
import "./IAccessControl.sol";
|
||||
import "../utils/Context.sol";
|
||||
import "../utils/Strings.sol";
|
||||
import "../utils/introspection/ERC165.sol";
|
||||
|
||||
/**
|
||||
* @dev Contract module that allows children to implement role-based access
|
||||
@ -63,6 +64,8 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
* 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._
|
||||
*/
|
||||
modifier onlyRole(bytes32 role) {
|
||||
_checkRole(role);
|
||||
@ -79,7 +82,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
/**
|
||||
* @dev Returns `true` if `account` has been granted `role`.
|
||||
*/
|
||||
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
|
||||
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
|
||||
return _roles[role].members[account];
|
||||
}
|
||||
|
||||
@ -88,6 +91,8 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
* Overriding this function changes the behavior of the {onlyRole} modifier.
|
||||
*
|
||||
* Format of the revert message is described in {_checkRole}.
|
||||
*
|
||||
* _Available since v4.6._
|
||||
*/
|
||||
function _checkRole(bytes32 role) internal view virtual {
|
||||
_checkRole(role, _msgSender());
|
||||
@ -102,7 +107,16 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
*/
|
||||
function _checkRole(bytes32 role, address account) internal view virtual {
|
||||
if (!hasRole(role, account)) {
|
||||
revert AccessControlUnauthorizedAccount(account, role);
|
||||
revert(
|
||||
string(
|
||||
abi.encodePacked(
|
||||
"AccessControl: account ",
|
||||
Strings.toHexString(account),
|
||||
" is missing role ",
|
||||
Strings.toHexString(uint256(role), 32)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,7 +126,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
*
|
||||
* To change a role's admin, use {_setRoleAdmin}.
|
||||
*/
|
||||
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
|
||||
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
|
||||
return _roles[role].adminRole;
|
||||
}
|
||||
|
||||
@ -128,7 +142,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
*
|
||||
* May emit a {RoleGranted} event.
|
||||
*/
|
||||
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
|
||||
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
|
||||
_grantRole(role, account);
|
||||
}
|
||||
|
||||
@ -143,7 +157,7 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
*
|
||||
* May emit a {RoleRevoked} event.
|
||||
*/
|
||||
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
|
||||
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
|
||||
_revokeRole(role, account);
|
||||
}
|
||||
|
||||
@ -159,16 +173,38 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - the caller must be `callerConfirmation`.
|
||||
* - the caller must be `account`.
|
||||
*
|
||||
* May emit a {RoleRevoked} event.
|
||||
*/
|
||||
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
|
||||
if (callerConfirmation != _msgSender()) {
|
||||
revert AccessControlBadConfirmation();
|
||||
}
|
||||
function renounceRole(bytes32 role, address account) public virtual override {
|
||||
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
|
||||
|
||||
_revokeRole(role, callerConfirmation);
|
||||
_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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
45
contracts/access/AccessControlCrossChain.sol
Normal file
45
contracts/access/AccessControlCrossChain.sol
Normal file
@ -0,0 +1,45 @@
|
||||
// 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,13 +1,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControlDefaultAdminRules.sol)
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
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";
|
||||
import "./AccessControl.sol";
|
||||
import "./IAccessControlDefaultAdminRules.sol";
|
||||
import "../utils/math/SafeCast.sol";
|
||||
import "../interfaces/IERC5313.sol";
|
||||
|
||||
/**
|
||||
* @dev Extension of {AccessControl} that allows specifying special rules to manage
|
||||
@ -35,6 +34,8 @@ import {IERC5313} from "../../interfaces/IERC5313.sol";
|
||||
* ) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* _Available since v4.9._
|
||||
*/
|
||||
abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRules, IERC5313, AccessControl {
|
||||
// pending admin pair read/written together frequently
|
||||
@ -52,9 +53,7 @@ abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRu
|
||||
* @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address.
|
||||
*/
|
||||
constructor(uint48 initialDelay, address initialDefaultAdmin) {
|
||||
if (initialDefaultAdmin == address(0)) {
|
||||
revert AccessControlInvalidDefaultAdmin(address(0));
|
||||
}
|
||||
require(initialDefaultAdmin != address(0), "AccessControl: 0 default admin");
|
||||
_currentDelay = initialDelay;
|
||||
_grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);
|
||||
}
|
||||
@ -81,9 +80,7 @@ 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) {
|
||||
if (role == DEFAULT_ADMIN_ROLE) {
|
||||
revert AccessControlEnforcedDefaultAdminRules();
|
||||
}
|
||||
require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't directly grant default admin role");
|
||||
super.grantRole(role, account);
|
||||
}
|
||||
|
||||
@ -91,9 +88,7 @@ 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) {
|
||||
if (role == DEFAULT_ADMIN_ROLE) {
|
||||
revert AccessControlEnforcedDefaultAdminRules();
|
||||
}
|
||||
require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't directly revoke default admin role");
|
||||
super.revokeRole(role, account);
|
||||
}
|
||||
|
||||
@ -113,9 +108,10 @@ 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();
|
||||
if (newDefaultAdmin != address(0) || !_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
|
||||
revert AccessControlEnforcedDefaultAdminDelay(schedule);
|
||||
}
|
||||
require(
|
||||
newDefaultAdmin == address(0) && _isScheduleSet(schedule) && _hasSchedulePassed(schedule),
|
||||
"AccessControl: only can renounce in two delayed steps"
|
||||
);
|
||||
delete _pendingDefaultAdminSchedule;
|
||||
}
|
||||
super.renounceRole(role, account);
|
||||
@ -132,9 +128,7 @@ abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRu
|
||||
*/
|
||||
function _grantRole(bytes32 role, address account) internal virtual override {
|
||||
if (role == DEFAULT_ADMIN_ROLE) {
|
||||
if (defaultAdmin() != address(0)) {
|
||||
revert AccessControlEnforcedDefaultAdminRules();
|
||||
}
|
||||
require(defaultAdmin() == address(0), "AccessControl: default admin already granted");
|
||||
_currentDefaultAdmin = account;
|
||||
}
|
||||
super._grantRole(role, account);
|
||||
@ -154,9 +148,7 @@ abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRu
|
||||
* @dev See {AccessControl-_setRoleAdmin}. Reverts for `DEFAULT_ADMIN_ROLE`.
|
||||
*/
|
||||
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override {
|
||||
if (role == DEFAULT_ADMIN_ROLE) {
|
||||
revert AccessControlEnforcedDefaultAdminRules();
|
||||
}
|
||||
require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't violate default admin rules");
|
||||
super._setRoleAdmin(role, adminRole);
|
||||
}
|
||||
|
||||
@ -244,10 +236,7 @@ abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRu
|
||||
*/
|
||||
function acceptDefaultAdminTransfer() public virtual {
|
||||
(address newDefaultAdmin, ) = pendingDefaultAdmin();
|
||||
if (_msgSender() != newDefaultAdmin) {
|
||||
// Enforce newDefaultAdmin explicit acceptance.
|
||||
revert AccessControlInvalidDefaultAdmin(_msgSender());
|
||||
}
|
||||
require(_msgSender() == newDefaultAdmin, "AccessControl: pending admin must accept");
|
||||
_acceptDefaultAdminTransfer();
|
||||
}
|
||||
|
||||
@ -258,9 +247,7 @@ abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRu
|
||||
*/
|
||||
function _acceptDefaultAdminTransfer() internal virtual {
|
||||
(address newAdmin, uint48 schedule) = pendingDefaultAdmin();
|
||||
if (!_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
|
||||
revert AccessControlEnforcedDefaultAdminDelay(schedule);
|
||||
}
|
||||
require(_isScheduleSet(schedule) && _hasSchedulePassed(schedule), "AccessControl: transfer delay not passed");
|
||||
_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.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {IAccessControlEnumerable} from "./IAccessControlEnumerable.sol";
|
||||
import {AccessControl} from "../AccessControl.sol";
|
||||
import {EnumerableSet} from "../../utils/structs/EnumerableSet.sol";
|
||||
import "./IAccessControlEnumerable.sol";
|
||||
import "./AccessControl.sol";
|
||||
import "../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 returns (address) {
|
||||
function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
|
||||
return _roleMembers[role].at(index);
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessCon
|
||||
* @dev Returns the number of accounts that have `role`. Can be used
|
||||
* together with {getRoleMember} to enumerate all bearers of a role.
|
||||
*/
|
||||
function getRoleMemberCount(bytes32 role) public view virtual returns (uint256) {
|
||||
function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
|
||||
return _roleMembers[role].length();
|
||||
}
|
||||
|
||||
@ -1,29 +1,19 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
@ -92,7 +82,7 @@ interface IAccessControl {
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - the caller must be `callerConfirmation`.
|
||||
* - the caller must be `account`.
|
||||
*/
|
||||
function renounceRole(bytes32 role, address callerConfirmation) external;
|
||||
function renounceRole(bytes32 role, address account) external;
|
||||
}
|
||||
|
||||
@ -1,36 +1,16 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (access/IAccessControlDefaultAdminRules.sol)
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {IAccessControl} from "../IAccessControl.sol";
|
||||
import "./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.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {IAccessControl} from "../IAccessControl.sol";
|
||||
import "./IAccessControl.sol";
|
||||
|
||||
/**
|
||||
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
|
||||
@ -1,17 +1,17 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {Context} from "../utils/Context.sol";
|
||||
import "../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.
|
||||
*
|
||||
* The initial owner is set to the address provided by the deployer. This can
|
||||
* later be changed with {transferOwnership}.
|
||||
* By default, the owner account will be the one that deploys the contract. 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,23 +20,13 @@ import {Context} from "../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 address provided by the deployer as the initial owner.
|
||||
* @dev Initializes the contract setting the deployer as the initial owner.
|
||||
*/
|
||||
constructor(address initialOwner) {
|
||||
_transferOwnership(initialOwner);
|
||||
constructor() {
|
||||
_transferOwnership(_msgSender());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -58,9 +48,7 @@ abstract contract Ownable is Context {
|
||||
* @dev Throws if the sender is not the owner.
|
||||
*/
|
||||
function _checkOwner() internal view virtual {
|
||||
if (owner() != _msgSender()) {
|
||||
revert OwnableUnauthorizedAccount(_msgSender());
|
||||
}
|
||||
require(owner() == _msgSender(), "Ownable: caller is not the owner");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,9 +67,7 @@ abstract contract Ownable is Context {
|
||||
* Can only be called by the current owner.
|
||||
*/
|
||||
function transferOwnership(address newOwner) public virtual onlyOwner {
|
||||
if (newOwner == address(0)) {
|
||||
revert OwnableInvalidOwner(address(0));
|
||||
}
|
||||
require(newOwner != address(0), "Ownable: new owner is the zero address");
|
||||
_transferOwnership(newOwner);
|
||||
}
|
||||
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {Ownable} from "./Ownable.sol";
|
||||
import "./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.
|
||||
*
|
||||
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
|
||||
* By default, the owner account will be the one that deploys the contract. This
|
||||
* can later be changed with {transferOwnership} and {acceptOwnership}.
|
||||
*
|
||||
* This module is used through inheritance. It will make available all functions
|
||||
@ -51,9 +51,7 @@ abstract contract Ownable2Step is Ownable {
|
||||
*/
|
||||
function acceptOwnership() public virtual {
|
||||
address sender = _msgSender();
|
||||
if (pendingOwner() != sender) {
|
||||
revert OwnableUnauthorizedAccount(sender);
|
||||
}
|
||||
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
|
||||
_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.
|
||||
|
||||
== Core
|
||||
== Authorization
|
||||
|
||||
{{Ownable}}
|
||||
|
||||
@ -18,12 +18,10 @@ This directory provides ways to restrict who can access the functions of a contr
|
||||
|
||||
{{AccessControl}}
|
||||
|
||||
== Extensions
|
||||
{{AccessControlCrossChain}}
|
||||
|
||||
{{IAccessControlEnumerable}}
|
||||
|
||||
{{AccessControlEnumerable}}
|
||||
|
||||
{{IAccessControlDefaultAdminRules}}
|
||||
|
||||
{{AccessControlDefaultAdminRules}}
|
||||
|
||||
54
contracts/crosschain/CrossChainEnabled.sol
Normal file
54
contracts/crosschain/CrossChainEnabled.sol
Normal file
@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.6.0) (crosschain/CrossChainEnabled.sol)
|
||||
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import "./errors.sol";
|
||||
|
||||
/**
|
||||
* @dev Provides information for building cross-chain aware contracts. This
|
||||
* abstract contract provides accessors and modifiers to control the execution
|
||||
* flow when receiving cross-chain messages.
|
||||
*
|
||||
* Actual implementations of cross-chain aware contracts, which are based on
|
||||
* this abstraction, will have to inherit from a bridge-specific
|
||||
* specialization. Such specializations are provided under
|
||||
* `crosschain/<chain>/CrossChainEnabled<chain>.sol`.
|
||||
*
|
||||
* _Available since v4.6._
|
||||
*/
|
||||
abstract contract CrossChainEnabled {
|
||||
/**
|
||||
* @dev Throws if the current function call is not the result of a
|
||||
* cross-chain execution.
|
||||
*/
|
||||
modifier onlyCrossChain() {
|
||||
if (!_isCrossChain()) revert NotCrossChainCall();
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Throws if the current function call is not the result of a
|
||||
* cross-chain execution initiated by `account`.
|
||||
*/
|
||||
modifier onlyCrossChainSender(address expected) {
|
||||
address actual = _crossChainSender();
|
||||
if (expected != actual) revert InvalidCrossChainSender(actual, expected);
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns whether the current function call is the result of a
|
||||
* cross-chain message.
|
||||
*/
|
||||
function _isCrossChain() internal view virtual returns (bool);
|
||||
|
||||
/**
|
||||
* @dev Returns the address of the sender of the cross-chain message that
|
||||
* triggered the current function call.
|
||||
*
|
||||
* IMPORTANT: Should revert with `NotCrossChainCall` if the current function
|
||||
* call is not the result of a cross-chain message.
|
||||
*/
|
||||
function _crossChainSender() internal view virtual returns (address);
|
||||
}
|
||||
34
contracts/crosschain/README.adoc
Normal file
34
contracts/crosschain/README.adoc
Normal file
@ -0,0 +1,34 @@
|
||||
= Cross Chain Awareness
|
||||
|
||||
[.readme-notice]
|
||||
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/crosschain
|
||||
|
||||
This directory provides building blocks to improve cross-chain awareness of smart contracts.
|
||||
|
||||
- {CrossChainEnabled} is an abstraction that contains accessors and modifiers to control the execution flow when receiving cross-chain messages.
|
||||
|
||||
== CrossChainEnabled specializations
|
||||
|
||||
The following specializations of {CrossChainEnabled} provide implementations of the {CrossChainEnabled} abstraction for specific bridges. This can be used to complex cross-chain aware components such as {AccessControlCrossChain}.
|
||||
|
||||
{{CrossChainEnabledAMB}}
|
||||
|
||||
{{CrossChainEnabledArbitrumL1}}
|
||||
|
||||
{{CrossChainEnabledArbitrumL2}}
|
||||
|
||||
{{CrossChainEnabledOptimism}}
|
||||
|
||||
{{CrossChainEnabledPolygonChild}}
|
||||
|
||||
== Libraries for cross-chain
|
||||
|
||||
In addition to the {CrossChainEnabled} abstraction, cross-chain awareness is also available through libraries. These libraries can be used to build complex designs such as contracts with the ability to interact with multiple bridges.
|
||||
|
||||
{{LibAMB}}
|
||||
|
||||
{{LibArbitrumL1}}
|
||||
|
||||
{{LibArbitrumL2}}
|
||||
|
||||
{{LibOptimism}}
|
||||
49
contracts/crosschain/amb/CrossChainEnabledAMB.sol
Normal file
49
contracts/crosschain/amb/CrossChainEnabledAMB.sol
Normal file
@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/amb/CrossChainEnabledAMB.sol)
|
||||
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import "../CrossChainEnabled.sol";
|
||||
import "./LibAMB.sol";
|
||||
|
||||
/**
|
||||
* @dev https://docs.tokenbridge.net/amb-bridge/about-amb-bridge[AMB]
|
||||
* specialization or the {CrossChainEnabled} abstraction.
|
||||
*
|
||||
* As of february 2020, AMB bridges are available between the following chains:
|
||||
*
|
||||
* - https://docs.tokenbridge.net/eth-xdai-amb-bridge/about-the-eth-xdai-amb[ETH ⇌ xDai]
|
||||
* - https://docs.tokenbridge.net/eth-qdai-bridge/about-the-eth-qdai-amb[ETH ⇌ qDai]
|
||||
* - https://docs.tokenbridge.net/eth-etc-amb-bridge/about-the-eth-etc-amb[ETH ⇌ ETC]
|
||||
* - https://docs.tokenbridge.net/eth-bsc-amb/about-the-eth-bsc-amb[ETH ⇌ BSC]
|
||||
* - https://docs.tokenbridge.net/eth-poa-amb-bridge/about-the-eth-poa-amb[ETH ⇌ POA]
|
||||
* - https://docs.tokenbridge.net/bsc-xdai-amb/about-the-bsc-xdai-amb[BSC ⇌ xDai]
|
||||
* - https://docs.tokenbridge.net/poa-xdai-amb/about-the-poa-xdai-amb[POA ⇌ xDai]
|
||||
* - https://docs.tokenbridge.net/rinkeby-xdai-amb-bridge/about-the-rinkeby-xdai-amb[Rinkeby ⇌ xDai]
|
||||
* - https://docs.tokenbridge.net/kovan-sokol-amb-bridge/about-the-kovan-sokol-amb[Kovan ⇌ Sokol]
|
||||
*
|
||||
* _Available since v4.6._
|
||||
*/
|
||||
contract CrossChainEnabledAMB is CrossChainEnabled {
|
||||
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
|
||||
address private immutable _bridge;
|
||||
|
||||
/// @custom:oz-upgrades-unsafe-allow constructor
|
||||
constructor(address bridge) {
|
||||
_bridge = bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev see {CrossChainEnabled-_isCrossChain}
|
||||
*/
|
||||
function _isCrossChain() internal view virtual override returns (bool) {
|
||||
return LibAMB.isCrossChain(_bridge);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev see {CrossChainEnabled-_crossChainSender}
|
||||
*/
|
||||
function _crossChainSender() internal view virtual override onlyCrossChain returns (address) {
|
||||
return LibAMB.crossChainSender(_bridge);
|
||||
}
|
||||
}
|
||||
35
contracts/crosschain/amb/LibAMB.sol
Normal file
35
contracts/crosschain/amb/LibAMB.sol
Normal file
@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/amb/LibAMB.sol)
|
||||
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import {IAMB as AMB_Bridge} from "../../vendor/amb/IAMB.sol";
|
||||
import "../errors.sol";
|
||||
|
||||
/**
|
||||
* @dev Primitives for cross-chain aware contracts using the
|
||||
* https://docs.tokenbridge.net/amb-bridge/about-amb-bridge[AMB]
|
||||
* family of bridges.
|
||||
*/
|
||||
library LibAMB {
|
||||
/**
|
||||
* @dev Returns whether the current function call is the result of a
|
||||
* cross-chain message relayed by `bridge`.
|
||||
*/
|
||||
function isCrossChain(address bridge) internal view returns (bool) {
|
||||
return msg.sender == bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the address of the sender that triggered the current
|
||||
* cross-chain message through `bridge`.
|
||||
*
|
||||
* NOTE: {isCrossChain} should be checked before trying to recover the
|
||||
* sender, as it will revert with `NotCrossChainCall` if the current
|
||||
* function call is not the result of a cross-chain message.
|
||||
*/
|
||||
function crossChainSender(address bridge) internal view returns (address) {
|
||||
if (!isCrossChain(bridge)) revert NotCrossChainCall();
|
||||
return AMB_Bridge(bridge).messageSender();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol)
|
||||
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import "../CrossChainEnabled.sol";
|
||||
import "./LibArbitrumL1.sol";
|
||||
|
||||
/**
|
||||
* @dev https://arbitrum.io/[Arbitrum] specialization or the
|
||||
* {CrossChainEnabled} abstraction the L1 side (mainnet).
|
||||
*
|
||||
* This version should only be deployed on L1 to process cross-chain messages
|
||||
* originating from L2. For the other side, use {CrossChainEnabledArbitrumL2}.
|
||||
*
|
||||
* The bridge contract is provided and maintained by the arbitrum team. You can
|
||||
* find the address of this contract on the rinkeby testnet in
|
||||
* https://developer.offchainlabs.com/docs/useful_addresses[Arbitrum's developer documentation].
|
||||
*
|
||||
* _Available since v4.6._
|
||||
*/
|
||||
abstract contract CrossChainEnabledArbitrumL1 is CrossChainEnabled {
|
||||
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
|
||||
address private immutable _bridge;
|
||||
|
||||
/// @custom:oz-upgrades-unsafe-allow constructor
|
||||
constructor(address bridge) {
|
||||
_bridge = bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev see {CrossChainEnabled-_isCrossChain}
|
||||
*/
|
||||
function _isCrossChain() internal view virtual override returns (bool) {
|
||||
return LibArbitrumL1.isCrossChain(_bridge);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev see {CrossChainEnabled-_crossChainSender}
|
||||
*/
|
||||
function _crossChainSender() internal view virtual override onlyCrossChain returns (address) {
|
||||
return LibArbitrumL1.crossChainSender(_bridge);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.8.0) (crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol)
|
||||
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import "../CrossChainEnabled.sol";
|
||||
import "./LibArbitrumL2.sol";
|
||||
|
||||
/**
|
||||
* @dev https://arbitrum.io/[Arbitrum] specialization or the
|
||||
* {CrossChainEnabled} abstraction the L2 side (arbitrum).
|
||||
*
|
||||
* This version should only be deployed on L2 to process cross-chain messages
|
||||
* originating from L1. For the other side, use {CrossChainEnabledArbitrumL1}.
|
||||
*
|
||||
* Arbitrum L2 includes the `ArbSys` contract at a fixed address. Therefore,
|
||||
* this specialization of {CrossChainEnabled} does not include a constructor.
|
||||
*
|
||||
* _Available since v4.6._
|
||||
*
|
||||
* WARNING: There is currently a bug in Arbitrum that causes this contract to
|
||||
* fail to detect cross-chain calls when deployed behind a proxy. This will be
|
||||
* fixed when the network is upgraded to Arbitrum Nitro, currently scheduled for
|
||||
* August 31st 2022.
|
||||
*/
|
||||
abstract contract CrossChainEnabledArbitrumL2 is CrossChainEnabled {
|
||||
/**
|
||||
* @dev see {CrossChainEnabled-_isCrossChain}
|
||||
*/
|
||||
function _isCrossChain() internal view virtual override returns (bool) {
|
||||
return LibArbitrumL2.isCrossChain(LibArbitrumL2.ARBSYS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev see {CrossChainEnabled-_crossChainSender}
|
||||
*/
|
||||
function _crossChainSender() internal view virtual override onlyCrossChain returns (address) {
|
||||
return LibArbitrumL2.crossChainSender(LibArbitrumL2.ARBSYS);
|
||||
}
|
||||
}
|
||||
42
contracts/crosschain/arbitrum/LibArbitrumL1.sol
Normal file
42
contracts/crosschain/arbitrum/LibArbitrumL1.sol
Normal file
@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.8.0) (crosschain/arbitrum/LibArbitrumL1.sol)
|
||||
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import {IBridge as ArbitrumL1_Bridge} from "../../vendor/arbitrum/IBridge.sol";
|
||||
import {IOutbox as ArbitrumL1_Outbox} from "../../vendor/arbitrum/IOutbox.sol";
|
||||
import "../errors.sol";
|
||||
|
||||
/**
|
||||
* @dev Primitives for cross-chain aware contracts for
|
||||
* https://arbitrum.io/[Arbitrum].
|
||||
*
|
||||
* This version should only be used on L1 to process cross-chain messages
|
||||
* originating from L2. For the other side, use {LibArbitrumL2}.
|
||||
*/
|
||||
library LibArbitrumL1 {
|
||||
/**
|
||||
* @dev Returns whether the current function call is the result of a
|
||||
* cross-chain message relayed by the `bridge`.
|
||||
*/
|
||||
function isCrossChain(address bridge) internal view returns (bool) {
|
||||
return msg.sender == bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the address of the sender that triggered the current
|
||||
* cross-chain message through the `bridge`.
|
||||
*
|
||||
* NOTE: {isCrossChain} should be checked before trying to recover the
|
||||
* sender, as it will revert with `NotCrossChainCall` if the current
|
||||
* function call is not the result of a cross-chain message.
|
||||
*/
|
||||
function crossChainSender(address bridge) internal view returns (address) {
|
||||
if (!isCrossChain(bridge)) revert NotCrossChainCall();
|
||||
|
||||
address sender = ArbitrumL1_Outbox(ArbitrumL1_Bridge(bridge).activeOutbox()).l2ToL1Sender();
|
||||
require(sender != address(0), "LibArbitrumL1: system messages without sender");
|
||||
|
||||
return sender;
|
||||
}
|
||||
}
|
||||
45
contracts/crosschain/arbitrum/LibArbitrumL2.sol
Normal file
45
contracts/crosschain/arbitrum/LibArbitrumL2.sol
Normal file
@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.8.0) (crosschain/arbitrum/LibArbitrumL2.sol)
|
||||
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import {IArbSys as ArbitrumL2_Bridge} from "../../vendor/arbitrum/IArbSys.sol";
|
||||
import "../errors.sol";
|
||||
|
||||
/**
|
||||
* @dev Primitives for cross-chain aware contracts for
|
||||
* https://arbitrum.io/[Arbitrum].
|
||||
*
|
||||
* This version should only be used on L2 to process cross-chain messages
|
||||
* originating from L1. For the other side, use {LibArbitrumL1}.
|
||||
*
|
||||
* WARNING: There is currently a bug in Arbitrum that causes this contract to
|
||||
* fail to detect cross-chain calls when deployed behind a proxy. This will be
|
||||
* fixed when the network is upgraded to Arbitrum Nitro, currently scheduled for
|
||||
* August 31st 2022.
|
||||
*/
|
||||
library LibArbitrumL2 {
|
||||
/**
|
||||
* @dev Returns whether the current function call is the result of a
|
||||
* cross-chain message relayed by `arbsys`.
|
||||
*/
|
||||
address public constant ARBSYS = 0x0000000000000000000000000000000000000064;
|
||||
|
||||
function isCrossChain(address arbsys) internal view returns (bool) {
|
||||
return ArbitrumL2_Bridge(arbsys).wasMyCallersAddressAliased();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the address of the sender that triggered the current
|
||||
* cross-chain message through `arbsys`.
|
||||
*
|
||||
* NOTE: {isCrossChain} should be checked before trying to recover the
|
||||
* sender, as it will revert with `NotCrossChainCall` if the current
|
||||
* function call is not the result of a cross-chain message.
|
||||
*/
|
||||
function crossChainSender(address arbsys) internal view returns (address) {
|
||||
if (!isCrossChain(arbsys)) revert NotCrossChainCall();
|
||||
|
||||
return ArbitrumL2_Bridge(arbsys).myCallersAddressWithoutAliasing();
|
||||
}
|
||||
}
|
||||
7
contracts/crosschain/errors.sol
Normal file
7
contracts/crosschain/errors.sol
Normal file
@ -0,0 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.6.0) (crosschain/errors.sol)
|
||||
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
error NotCrossChainCall();
|
||||
error InvalidCrossChainSender(address actual, address expected);
|
||||
41
contracts/crosschain/optimism/CrossChainEnabledOptimism.sol
Normal file
41
contracts/crosschain/optimism/CrossChainEnabledOptimism.sol
Normal file
@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/optimism/CrossChainEnabledOptimism.sol)
|
||||
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import "../CrossChainEnabled.sol";
|
||||
import "./LibOptimism.sol";
|
||||
|
||||
/**
|
||||
* @dev https://www.optimism.io/[Optimism] specialization or the
|
||||
* {CrossChainEnabled} abstraction.
|
||||
*
|
||||
* The messenger (`CrossDomainMessenger`) contract is provided and maintained by
|
||||
* the optimism team. You can find the address of this contract on mainnet and
|
||||
* kovan in the https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts/deployments[deployments section of Optimism monorepo].
|
||||
*
|
||||
* _Available since v4.6._
|
||||
*/
|
||||
abstract contract CrossChainEnabledOptimism is CrossChainEnabled {
|
||||
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
|
||||
address private immutable _messenger;
|
||||
|
||||
/// @custom:oz-upgrades-unsafe-allow constructor
|
||||
constructor(address messenger) {
|
||||
_messenger = messenger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev see {CrossChainEnabled-_isCrossChain}
|
||||
*/
|
||||
function _isCrossChain() internal view virtual override returns (bool) {
|
||||
return LibOptimism.isCrossChain(_messenger);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev see {CrossChainEnabled-_crossChainSender}
|
||||
*/
|
||||
function _crossChainSender() internal view virtual override onlyCrossChain returns (address) {
|
||||
return LibOptimism.crossChainSender(_messenger);
|
||||
}
|
||||
}
|
||||
36
contracts/crosschain/optimism/LibOptimism.sol
Normal file
36
contracts/crosschain/optimism/LibOptimism.sol
Normal file
@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/optimism/LibOptimism.sol)
|
||||
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import {ICrossDomainMessenger as Optimism_Bridge} from "../../vendor/optimism/ICrossDomainMessenger.sol";
|
||||
import "../errors.sol";
|
||||
|
||||
/**
|
||||
* @dev Primitives for cross-chain aware contracts for https://www.optimism.io/[Optimism].
|
||||
* See the https://community.optimism.io/docs/developers/bridge/messaging/#accessing-msg-sender[documentation]
|
||||
* for the functionality used here.
|
||||
*/
|
||||
library LibOptimism {
|
||||
/**
|
||||
* @dev Returns whether the current function call is the result of a
|
||||
* cross-chain message relayed by `messenger`.
|
||||
*/
|
||||
function isCrossChain(address messenger) internal view returns (bool) {
|
||||
return msg.sender == messenger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the address of the sender that triggered the current
|
||||
* cross-chain message through `messenger`.
|
||||
*
|
||||
* NOTE: {isCrossChain} should be checked before trying to recover the
|
||||
* sender, as it will revert with `NotCrossChainCall` if the current
|
||||
* function call is not the result of a cross-chain message.
|
||||
*/
|
||||
function crossChainSender(address messenger) internal view returns (address) {
|
||||
if (!isCrossChain(messenger)) revert NotCrossChainCall();
|
||||
|
||||
return Optimism_Bridge(messenger).xDomainMessageSender();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user