From 0f10efe2326dfb79442967b82bde4ff1b28c69cd Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 19 May 2023 22:48:05 +0200 Subject: [PATCH] Remove code in preparation for v5.0 (#4258) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ernesto García Co-authored-by: Francisco --- .changeset/beige-ducks-flow.md | 5 + .changeset/fluffy-gifts-build.md | 5 + .changeset/friendly-suits-camp.md | 5 + .changeset/selfish-queens-rest.md | 5 + .changeset/spicy-ducks-cough.md | 5 + .changeset/swift-berries-sort.md | 5 + .changeset/tame-geckos-search.md | 5 + .changeset/three-weeks-double.md | 5 + contracts/access/AccessControl.sol | 24 - contracts/access/AccessControlCrossChain.sol | 45 -- contracts/access/README.adoc | 2 - contracts/crosschain/CrossChainEnabled.sol | 54 -- contracts/crosschain/README.adoc | 34 - .../crosschain/amb/CrossChainEnabledAMB.sol | 49 -- contracts/crosschain/amb/LibAMB.sol | 35 - .../arbitrum/CrossChainEnabledArbitrumL1.sol | 44 -- .../arbitrum/CrossChainEnabledArbitrumL2.sol | 40 -- .../crosschain/arbitrum/LibArbitrumL1.sol | 42 -- .../crosschain/arbitrum/LibArbitrumL2.sol | 45 -- contracts/crosschain/errors.sol | 7 - .../optimism/CrossChainEnabledOptimism.sol | 41 -- contracts/crosschain/optimism/LibOptimism.sol | 36 -- .../polygon/CrossChainEnabledPolygonChild.sol | 72 --- contracts/governance/README.adoc | 4 - contracts/governance/TimelockController.sol | 6 +- .../GovernorCompatibilityBravo.sol | 11 +- .../extensions/GovernorProposalThreshold.sol | 23 - .../GovernorVotesQuorumFraction.sol | 16 +- contracts/interfaces/IERC1820Implementer.sol | 18 +- contracts/interfaces/IERC1820Registry.sol | 110 +++- contracts/interfaces/IERC777.sol | 197 +++++- contracts/interfaces/IERC777Recipient.sol | 32 +- contracts/interfaces/IERC777Sender.sol | 32 +- contracts/interfaces/draft-IERC2612.sol | 7 - .../mocks/AccessControlCrossChainMock.sol | 8 - contracts/mocks/ConditionalEscrowMock.sol | 18 - contracts/mocks/PullPaymentMock.sol | 15 - contracts/mocks/SafeMathMemoryCheck.sol | 72 --- contracts/mocks/TimersBlockNumberImpl.sol | 39 -- contracts/mocks/TimersTimestampImpl.sol | 39 -- contracts/mocks/crosschain/bridges.sol | 94 --- contracts/mocks/crosschain/receivers.sol | 54 -- contracts/mocks/governance/GovernorMock.sol | 17 +- .../mocks/token/ERC20PermitNoRevertMock.sol | 2 +- .../mocks/token/ERC721ConsecutiveMock.sol | 2 +- contracts/mocks/token/ERC777Mock.sol | 13 - .../mocks/token/ERC777SenderRecipientMock.sol | 152 ----- contracts/mocks/wizard/MyGovernor1.sol | 79 --- contracts/mocks/wizard/MyGovernor2.sol | 85 --- contracts/mocks/wizard/MyGovernor3.sol | 94 --- contracts/proxy/beacon/BeaconProxy.sol | 21 - .../TransparentUpgradeableProxy.sol | 27 +- contracts/security/PullPayment.sol | 74 --- contracts/security/README.adoc | 3 - .../token/ERC20/extensions/ERC20Permit.sol | 8 - .../ERC20/extensions/draft-ERC20Permit.sol | 8 - .../ERC20/extensions/draft-IERC20Permit.sol | 7 - contracts/token/ERC20/utils/SafeERC20.sol | 18 - .../ERC721/extensions/draft-ERC721Votes.sol | 9 - contracts/token/ERC777/ERC777.sol | 517 --------------- contracts/token/ERC777/IERC777.sol | 200 ------ contracts/token/ERC777/IERC777Recipient.sol | 35 - contracts/token/ERC777/IERC777Sender.sol | 35 - contracts/token/ERC777/README.adoc | 26 - contracts/utils/Checkpoints.sol | 196 ------ contracts/utils/README.adoc | 25 - contracts/utils/Timers.sol | 75 --- contracts/utils/cryptography/ECDSA.sol | 3 +- contracts/utils/cryptography/draft-EIP712.sol | 8 - contracts/utils/escrow/ConditionalEscrow.sol | 25 - contracts/utils/escrow/Escrow.sol | 67 -- contracts/utils/escrow/RefundEscrow.sol | 100 --- .../introspection/ERC1820Implementer.sol | 43 -- .../introspection/IERC1820Implementer.sol | 20 - .../utils/introspection/IERC1820Registry.sol | 112 ---- contracts/utils/math/Math.sol | 66 ++ contracts/utils/math/SafeCast.sol | 3 - contracts/utils/math/SafeMath.sol | 215 ------- contracts/utils/math/SignedSafeMath.sol | 68 -- contracts/utils/structs/EnumerableMap.sol | 68 -- contracts/vendor/amb/IAMB.sol | 41 -- contracts/vendor/arbitrum/IArbSys.sol | 134 ---- contracts/vendor/arbitrum/IBridge.sol | 102 --- .../arbitrum/IDelayedMessageProvider.sol | 16 - contracts/vendor/arbitrum/IInbox.sol | 152 ----- contracts/vendor/arbitrum/IOutbox.sol | 117 ---- .../vendor/optimism/ICrossDomainMessenger.sol | 34 - contracts/vendor/optimism/LICENSE | 22 - .../vendor/polygon/IFxMessageProcessor.sol | 7 - docs/modules/ROOT/nav.adoc | 3 - docs/modules/ROOT/pages/crosschain.adoc | 210 ------ docs/modules/ROOT/pages/erc1155.adoc | 8 +- docs/modules/ROOT/pages/erc777.adoc | 75 --- docs/modules/ROOT/pages/tokens.adoc | 1 - docs/modules/ROOT/pages/utilities.adoc | 10 +- hardhat.config.js | 6 +- scripts/generate/templates/Checkpoints.js | 86 +-- .../generate/templates/Checkpoints.opts.js | 7 +- scripts/generate/templates/Checkpoints.t.js | 118 +--- scripts/generate/templates/EnumerableMap.js | 30 - scripts/generate/templates/SafeCast.js | 3 - scripts/upgradeable/upgradeable.patch | 13 - test/access/AccessControlCrossChain.test.js | 49 -- test/crosschain/CrossChainEnabled.test.js | 78 --- test/helpers/crosschain.js | 61 -- test/security/PullPayment.test.js | 51 -- test/token/ERC20/utils/SafeERC20.test.js | 37 -- test/token/ERC777/ERC777.behavior.js | 597 ------------------ test/token/ERC777/ERC777.test.js | 556 ---------------- test/utils/Checkpoints.t.sol | 123 ---- test/utils/Checkpoints.test.js | 190 ++---- test/utils/Create2.test.js | 13 +- test/utils/TimersBlockNumberImpl.test.js | 55 -- test/utils/TimersTimestamp.test.js | 55 -- test/utils/escrow/ConditionalEscrow.test.js | 37 -- test/utils/escrow/Escrow.behavior.js | 90 --- test/utils/escrow/Escrow.test.js | 14 - test/utils/escrow/RefundEscrow.test.js | 143 ----- .../introspection/ERC1820Implementer.test.js | 71 --- test/utils/math/Math.t.sol | 3 +- test/utils/math/Math.test.js | 139 ++++ test/utils/math/SafeMath.test.js | 433 ------------- test/utils/math/SignedSafeMath.test.js | 152 ----- test/utils/structs/EnumerableMap.behavior.js | 11 - test/utils/structs/EnumerableMap.test.js | 5 - 125 files changed, 700 insertions(+), 7389 deletions(-) create mode 100644 .changeset/beige-ducks-flow.md create mode 100644 .changeset/fluffy-gifts-build.md create mode 100644 .changeset/friendly-suits-camp.md create mode 100644 .changeset/selfish-queens-rest.md create mode 100644 .changeset/spicy-ducks-cough.md create mode 100644 .changeset/swift-berries-sort.md create mode 100644 .changeset/tame-geckos-search.md create mode 100644 .changeset/three-weeks-double.md delete mode 100644 contracts/access/AccessControlCrossChain.sol delete mode 100644 contracts/crosschain/CrossChainEnabled.sol delete mode 100644 contracts/crosschain/README.adoc delete mode 100644 contracts/crosschain/amb/CrossChainEnabledAMB.sol delete mode 100644 contracts/crosschain/amb/LibAMB.sol delete mode 100644 contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol delete mode 100644 contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol delete mode 100644 contracts/crosschain/arbitrum/LibArbitrumL1.sol delete mode 100644 contracts/crosschain/arbitrum/LibArbitrumL2.sol delete mode 100644 contracts/crosschain/errors.sol delete mode 100644 contracts/crosschain/optimism/CrossChainEnabledOptimism.sol delete mode 100644 contracts/crosschain/optimism/LibOptimism.sol delete mode 100644 contracts/crosschain/polygon/CrossChainEnabledPolygonChild.sol delete mode 100644 contracts/governance/extensions/GovernorProposalThreshold.sol delete mode 100644 contracts/interfaces/draft-IERC2612.sol delete mode 100644 contracts/mocks/AccessControlCrossChainMock.sol delete mode 100644 contracts/mocks/ConditionalEscrowMock.sol delete mode 100644 contracts/mocks/PullPaymentMock.sol delete mode 100644 contracts/mocks/SafeMathMemoryCheck.sol delete mode 100644 contracts/mocks/TimersBlockNumberImpl.sol delete mode 100644 contracts/mocks/TimersTimestampImpl.sol delete mode 100644 contracts/mocks/crosschain/bridges.sol delete mode 100644 contracts/mocks/crosschain/receivers.sol delete mode 100644 contracts/mocks/token/ERC777Mock.sol delete mode 100644 contracts/mocks/token/ERC777SenderRecipientMock.sol delete mode 100644 contracts/mocks/wizard/MyGovernor1.sol delete mode 100644 contracts/mocks/wizard/MyGovernor2.sol delete mode 100644 contracts/mocks/wizard/MyGovernor3.sol delete mode 100644 contracts/security/PullPayment.sol delete mode 100644 contracts/token/ERC20/extensions/draft-ERC20Permit.sol delete mode 100644 contracts/token/ERC20/extensions/draft-IERC20Permit.sol delete mode 100644 contracts/token/ERC721/extensions/draft-ERC721Votes.sol delete mode 100644 contracts/token/ERC777/ERC777.sol delete mode 100644 contracts/token/ERC777/IERC777.sol delete mode 100644 contracts/token/ERC777/IERC777Recipient.sol delete mode 100644 contracts/token/ERC777/IERC777Sender.sol delete mode 100644 contracts/token/ERC777/README.adoc delete mode 100644 contracts/utils/Timers.sol delete mode 100644 contracts/utils/cryptography/draft-EIP712.sol delete mode 100644 contracts/utils/escrow/ConditionalEscrow.sol delete mode 100644 contracts/utils/escrow/Escrow.sol delete mode 100644 contracts/utils/escrow/RefundEscrow.sol delete mode 100644 contracts/utils/introspection/ERC1820Implementer.sol delete mode 100644 contracts/utils/introspection/IERC1820Implementer.sol delete mode 100644 contracts/utils/introspection/IERC1820Registry.sol delete mode 100644 contracts/utils/math/SafeMath.sol delete mode 100644 contracts/utils/math/SignedSafeMath.sol delete mode 100644 contracts/vendor/amb/IAMB.sol delete mode 100644 contracts/vendor/arbitrum/IArbSys.sol delete mode 100644 contracts/vendor/arbitrum/IBridge.sol delete mode 100644 contracts/vendor/arbitrum/IDelayedMessageProvider.sol delete mode 100644 contracts/vendor/arbitrum/IInbox.sol delete mode 100644 contracts/vendor/arbitrum/IOutbox.sol delete mode 100644 contracts/vendor/optimism/ICrossDomainMessenger.sol delete mode 100644 contracts/vendor/optimism/LICENSE delete mode 100644 contracts/vendor/polygon/IFxMessageProcessor.sol delete mode 100644 docs/modules/ROOT/pages/crosschain.adoc delete mode 100644 docs/modules/ROOT/pages/erc777.adoc delete mode 100644 test/access/AccessControlCrossChain.test.js delete mode 100644 test/crosschain/CrossChainEnabled.test.js delete mode 100644 test/helpers/crosschain.js delete mode 100644 test/security/PullPayment.test.js delete mode 100644 test/token/ERC777/ERC777.behavior.js delete mode 100644 test/token/ERC777/ERC777.test.js delete mode 100644 test/utils/TimersBlockNumberImpl.test.js delete mode 100644 test/utils/TimersTimestamp.test.js delete mode 100644 test/utils/escrow/ConditionalEscrow.test.js delete mode 100644 test/utils/escrow/Escrow.behavior.js delete mode 100644 test/utils/escrow/Escrow.test.js delete mode 100644 test/utils/escrow/RefundEscrow.test.js delete mode 100644 test/utils/introspection/ERC1820Implementer.test.js delete mode 100644 test/utils/math/SafeMath.test.js delete mode 100644 test/utils/math/SignedSafeMath.test.js diff --git a/.changeset/beige-ducks-flow.md b/.changeset/beige-ducks-flow.md new file mode 100644 index 00000000000..4edc870b519 --- /dev/null +++ b/.changeset/beige-ducks-flow.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': major +--- + +Remove deprecated GovernorProposalThreshold module. diff --git a/.changeset/fluffy-gifts-build.md b/.changeset/fluffy-gifts-build.md new file mode 100644 index 00000000000..a1ffe8be196 --- /dev/null +++ b/.changeset/fluffy-gifts-build.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': major +--- + +Remove ERC1820Implementer. diff --git a/.changeset/friendly-suits-camp.md b/.changeset/friendly-suits-camp.md new file mode 100644 index 00000000000..bcf1d7ee4cd --- /dev/null +++ b/.changeset/friendly-suits-camp.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': major +--- + +Remove Checkpoints.History. diff --git a/.changeset/selfish-queens-rest.md b/.changeset/selfish-queens-rest.md new file mode 100644 index 00000000000..739fa135601 --- /dev/null +++ b/.changeset/selfish-queens-rest.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': major +--- + +Remove PullPayment and Escrow contracts (Escrow, ConditionalEscrow, RefundEscrow). diff --git a/.changeset/spicy-ducks-cough.md b/.changeset/spicy-ducks-cough.md new file mode 100644 index 00000000000..bf7296e4e0c --- /dev/null +++ b/.changeset/spicy-ducks-cough.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': major +--- + +Remove the Timers library. diff --git a/.changeset/swift-berries-sort.md b/.changeset/swift-berries-sort.md new file mode 100644 index 00000000000..9af6cba6442 --- /dev/null +++ b/.changeset/swift-berries-sort.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': major +--- + +Remove ERC777 implementation. diff --git a/.changeset/tame-geckos-search.md b/.changeset/tame-geckos-search.md new file mode 100644 index 00000000000..6b6ae86d0df --- /dev/null +++ b/.changeset/tame-geckos-search.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': major +--- + +Remove SafeMath and SignedSafeMath libraries. diff --git a/.changeset/three-weeks-double.md b/.changeset/three-weeks-double.md new file mode 100644 index 00000000000..d267e72c4fc --- /dev/null +++ b/.changeset/three-weeks-double.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': major +--- + +Remove CrossChain contracts, including AccessControlCrossChain and all the vendored bridge interfaces. diff --git a/contracts/access/AccessControl.sol b/contracts/access/AccessControl.sol index 3a73de78b55..24fe0c00c5e 100644 --- a/contracts/access/AccessControl.sol +++ b/contracts/access/AccessControl.sol @@ -183,30 +183,6 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 { _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); - } - /** * @dev Sets `adminRole` as ``role``'s admin role. * diff --git a/contracts/access/AccessControlCrossChain.sol b/contracts/access/AccessControlCrossChain.sol deleted file mode 100644 index 95be5091c5d..00000000000 --- a/contracts/access/AccessControlCrossChain.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (access/AccessControlCrossChain.sol) - -pragma solidity ^0.8.4; - -import "./AccessControl.sol"; -import "../crosschain/CrossChainEnabled.sol"; - -/** - * @dev An extension to {AccessControl} with support for cross-chain access management. - * For each role, is extension implements an equivalent "aliased" role that is used for - * restricting calls originating from other chains. - * - * For example, if a function `myFunction` is protected by `onlyRole(SOME_ROLE)`, and - * if an address `x` has role `SOME_ROLE`, it would be able to call `myFunction` directly. - * A wallet or contract at the same address on another chain would however not be able - * to call this function. In order to do so, it would require to have the role - * `_crossChainRoleAlias(SOME_ROLE)`. - * - * This aliasing is required to protect against multiple contracts living at the same - * address on different chains but controlled by conflicting entities. - * - * _Available since v4.6._ - */ -abstract contract AccessControlCrossChain is AccessControl, CrossChainEnabled { - bytes32 public constant CROSSCHAIN_ALIAS = keccak256("CROSSCHAIN_ALIAS"); - - /** - * @dev See {AccessControl-_checkRole}. - */ - function _checkRole(bytes32 role) internal view virtual override { - if (_isCrossChain()) { - _checkRole(_crossChainRoleAlias(role), _crossChainSender()); - } else { - super._checkRole(role); - } - } - - /** - * @dev Returns the aliased role corresponding to `role`. - */ - function _crossChainRoleAlias(bytes32 role) internal pure virtual returns (bytes32) { - return role ^ CROSSCHAIN_ALIAS; - } -} diff --git a/contracts/access/README.adoc b/contracts/access/README.adoc index 80ca0020f4b..117cd7c9735 100644 --- a/contracts/access/README.adoc +++ b/contracts/access/README.adoc @@ -18,8 +18,6 @@ This directory provides ways to restrict who can access the functions of a contr {{AccessControl}} -{{AccessControlCrossChain}} - {{IAccessControlEnumerable}} {{AccessControlEnumerable}} diff --git a/contracts/crosschain/CrossChainEnabled.sol b/contracts/crosschain/CrossChainEnabled.sol deleted file mode 100644 index 4c9b9e5c543..00000000000 --- a/contracts/crosschain/CrossChainEnabled.sol +++ /dev/null @@ -1,54 +0,0 @@ -// 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//CrossChainEnabled.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); -} diff --git a/contracts/crosschain/README.adoc b/contracts/crosschain/README.adoc deleted file mode 100644 index 266b1530fb4..00000000000 --- a/contracts/crosschain/README.adoc +++ /dev/null @@ -1,34 +0,0 @@ -= 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}} diff --git a/contracts/crosschain/amb/CrossChainEnabledAMB.sol b/contracts/crosschain/amb/CrossChainEnabledAMB.sol deleted file mode 100644 index e69355d6266..00000000000 --- a/contracts/crosschain/amb/CrossChainEnabledAMB.sol +++ /dev/null @@ -1,49 +0,0 @@ -// 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); - } -} diff --git a/contracts/crosschain/amb/LibAMB.sol b/contracts/crosschain/amb/LibAMB.sol deleted file mode 100644 index aef9c43eeaa..00000000000 --- a/contracts/crosschain/amb/LibAMB.sol +++ /dev/null @@ -1,35 +0,0 @@ -// 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(); - } -} diff --git a/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol b/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol deleted file mode 100644 index 5068da39574..00000000000 --- a/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol +++ /dev/null @@ -1,44 +0,0 @@ -// 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); - } -} diff --git a/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol b/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol deleted file mode 100644 index e85993dbbac..00000000000 --- a/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol +++ /dev/null @@ -1,40 +0,0 @@ -// 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); - } -} diff --git a/contracts/crosschain/arbitrum/LibArbitrumL1.sol b/contracts/crosschain/arbitrum/LibArbitrumL1.sol deleted file mode 100644 index be7236b2479..00000000000 --- a/contracts/crosschain/arbitrum/LibArbitrumL1.sol +++ /dev/null @@ -1,42 +0,0 @@ -// 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; - } -} diff --git a/contracts/crosschain/arbitrum/LibArbitrumL2.sol b/contracts/crosschain/arbitrum/LibArbitrumL2.sol deleted file mode 100644 index 715a3878f49..00000000000 --- a/contracts/crosschain/arbitrum/LibArbitrumL2.sol +++ /dev/null @@ -1,45 +0,0 @@ -// 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(); - } -} diff --git a/contracts/crosschain/errors.sol b/contracts/crosschain/errors.sol deleted file mode 100644 index 004460e970b..00000000000 --- a/contracts/crosschain/errors.sol +++ /dev/null @@ -1,7 +0,0 @@ -// 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); diff --git a/contracts/crosschain/optimism/CrossChainEnabledOptimism.sol b/contracts/crosschain/optimism/CrossChainEnabledOptimism.sol deleted file mode 100644 index 1005864ae9d..00000000000 --- a/contracts/crosschain/optimism/CrossChainEnabledOptimism.sol +++ /dev/null @@ -1,41 +0,0 @@ -// 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); - } -} diff --git a/contracts/crosschain/optimism/LibOptimism.sol b/contracts/crosschain/optimism/LibOptimism.sol deleted file mode 100644 index d963ade2aa3..00000000000 --- a/contracts/crosschain/optimism/LibOptimism.sol +++ /dev/null @@ -1,36 +0,0 @@ -// 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(); - } -} diff --git a/contracts/crosschain/polygon/CrossChainEnabledPolygonChild.sol b/contracts/crosschain/polygon/CrossChainEnabledPolygonChild.sol deleted file mode 100644 index fa099483499..00000000000 --- a/contracts/crosschain/polygon/CrossChainEnabledPolygonChild.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/polygon/CrossChainEnabledPolygonChild.sol) - -pragma solidity ^0.8.4; - -import "../CrossChainEnabled.sol"; -import "../../security/ReentrancyGuard.sol"; -import "../../utils/Address.sol"; -import "../../vendor/polygon/IFxMessageProcessor.sol"; - -address constant DEFAULT_SENDER = 0x000000000000000000000000000000000000dEaD; - -/** - * @dev https://polygon.technology/[Polygon] specialization or the - * {CrossChainEnabled} abstraction the child side (polygon/mumbai). - * - * This version should only be deployed on child chain to process cross-chain - * messages originating from the parent chain. - * - * The fxChild contract is provided and maintained by the polygon team. You can - * find the address of this contract polygon and mumbai in - * https://docs.polygon.technology/docs/develop/l1-l2-communication/fx-portal/#contract-addresses[Polygon's Fx-Portal documentation]. - * - * _Available since v4.6._ - */ -abstract contract CrossChainEnabledPolygonChild is IFxMessageProcessor, CrossChainEnabled, ReentrancyGuard { - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - address private immutable _fxChild; - address private _sender = DEFAULT_SENDER; - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(address fxChild) { - _fxChild = fxChild; - } - - /** - * @dev see {CrossChainEnabled-_isCrossChain} - */ - function _isCrossChain() internal view virtual override returns (bool) { - return msg.sender == _fxChild; - } - - /** - * @dev see {CrossChainEnabled-_crossChainSender} - */ - function _crossChainSender() internal view virtual override onlyCrossChain returns (address) { - return _sender; - } - - /** - * @dev External entry point to receive and relay messages originating - * from the fxChild. - * - * Non-reentrancy is crucial to avoid a cross-chain call being able - * to impersonate anyone by just looping through this with user-defined - * arguments. - * - * Note: if _fxChild calls any other function that does a delegate-call, - * then security could be compromised. - */ - function processMessageFromRoot( - uint256 /* stateId */, - address rootMessageSender, - bytes calldata data - ) external override nonReentrant { - if (!_isCrossChain()) revert NotCrossChainCall(); - - _sender = rootMessageSender; - Address.functionDelegateCall(address(this), data, "cross-chain execution failed"); - _sender = DEFAULT_SENDER; - } -} diff --git a/contracts/governance/README.adoc b/contracts/governance/README.adoc index d47f59a774a..171283662dc 100644 --- a/contracts/governance/README.adoc +++ b/contracts/governance/README.adoc @@ -80,10 +80,6 @@ NOTE: Functions of the `Governor` contract do not include access control. If you {{GovernorCompatibilityBravo}} -=== Deprecated - -{{GovernorProposalThreshold}} - == Utils {{Votes}} diff --git a/contracts/governance/TimelockController.sol b/contracts/governance/TimelockController.sol index a8906a5ca99..d2c2ae93a56 100644 --- a/contracts/governance/TimelockController.sol +++ b/contracts/governance/TimelockController.sol @@ -88,13 +88,13 @@ contract TimelockController is AccessControl, IERC721Receiver, IERC1155Receiver // register proposers and cancellers for (uint256 i = 0; i < proposers.length; ++i) { - _setupRole(PROPOSER_ROLE, proposers[i]); - _setupRole(CANCELLER_ROLE, proposers[i]); + _grantRole(PROPOSER_ROLE, proposers[i]); + _grantRole(CANCELLER_ROLE, proposers[i]); } // register executors for (uint256 i = 0; i < executors.length; ++i) { - _setupRole(EXECUTOR_ROLE, executors[i]); + _grantRole(EXECUTOR_ROLE, executors[i]); } _minDelay = minDelay; diff --git a/contracts/governance/compatibility/GovernorCompatibilityBravo.sol b/contracts/governance/compatibility/GovernorCompatibilityBravo.sol index 1332ac79d82..db7301ef4ce 100644 --- a/contracts/governance/compatibility/GovernorCompatibilityBravo.sol +++ b/contracts/governance/compatibility/GovernorCompatibilityBravo.sol @@ -26,7 +26,6 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp } struct ProposalDetails { - address proposer; address[] targets; uint256[] values; string[] signatures; @@ -56,7 +55,7 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp string memory description ) public virtual override(IGovernor, Governor) returns (uint256) { // Stores the proposal details (if not already present) and executes the propose logic from the core. - _storeProposal(_msgSender(), targets, values, new string[](calldatas.length), calldatas, description); + _storeProposal(targets, values, new string[](calldatas.length), calldatas, description); return super.propose(targets, values, calldatas, description); } @@ -75,7 +74,7 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp // after the full proposal is stored, so the store operation included in the fallback will be skipped. Here we // call `propose` and not `super.propose` to make sure if a child contract override `propose`, whatever code // is added there is also executed when calling this alternative interface. - _storeProposal(_msgSender(), targets, values, signatures, calldatas, description); + _storeProposal(targets, values, signatures, calldatas, description); return propose(targets, values, _encodeCalldata(signatures, calldatas), description); } @@ -132,7 +131,7 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp bytes32 descriptionHash ) public virtual override(IGovernor, Governor) returns (uint256) { uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); - address proposer = _proposalDetails[proposalId].proposer; + address proposer = proposalProposer(proposalId); require( _msgSender() == proposer || getVotes(proposer, clock() - 1) < proposalThreshold(), @@ -182,7 +181,6 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp * @dev Store proposal metadata (if not already present) for later lookup. */ function _storeProposal( - address proposer, address[] memory targets, uint256[] memory values, string[] memory signatures, @@ -194,7 +192,6 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp ProposalDetails storage details = _proposalDetails[proposalId]; if (details.descriptionHash == bytes32(0)) { - details.proposer = proposer; details.targets = targets; details.values = values; details.signatures = signatures; @@ -228,12 +225,12 @@ abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorComp ) { id = proposalId; + proposer = proposalProposer(proposalId); eta = proposalEta(proposalId); startBlock = proposalSnapshot(proposalId); endBlock = proposalDeadline(proposalId); ProposalDetails storage details = _proposalDetails[proposalId]; - proposer = details.proposer; forVotes = details.forVotes; againstVotes = details.againstVotes; abstainVotes = details.abstainVotes; diff --git a/contracts/governance/extensions/GovernorProposalThreshold.sol b/contracts/governance/extensions/GovernorProposalThreshold.sol deleted file mode 100644 index 3feebace070..00000000000 --- a/contracts/governance/extensions/GovernorProposalThreshold.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (governance/extensions/GovernorProposalThreshold.sol) - -pragma solidity ^0.8.0; - -import "../Governor.sol"; - -/** - * @dev Extension of {Governor} for proposal restriction to token holders with a minimum balance. - * - * _Available since v4.3._ - * _Deprecated since v4.4._ - */ -abstract contract GovernorProposalThreshold is Governor { - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public virtual override returns (uint256) { - return super.propose(targets, values, calldatas, description); - } -} diff --git a/contracts/governance/extensions/GovernorVotesQuorumFraction.sol b/contracts/governance/extensions/GovernorVotesQuorumFraction.sol index 4035702606f..e103ea0bfac 100644 --- a/contracts/governance/extensions/GovernorVotesQuorumFraction.sol +++ b/contracts/governance/extensions/GovernorVotesQuorumFraction.sol @@ -16,8 +16,6 @@ import "../../utils/math/SafeCast.sol"; abstract contract GovernorVotesQuorumFraction is GovernorVotes { using Checkpoints for Checkpoints.Trace224; - uint256 private _quorumNumerator; // DEPRECATED in favor of _quorumNumeratorHistory - /// @custom:oz-retyped-from Checkpoints.History Checkpoints.Trace224 private _quorumNumeratorHistory; @@ -38,7 +36,7 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes { * @dev Returns the current quorum numerator. See {quorumDenominator}. */ function quorumNumerator() public view virtual returns (uint256) { - return _quorumNumeratorHistory._checkpoints.length == 0 ? _quorumNumerator : _quorumNumeratorHistory.latest(); + return _quorumNumeratorHistory.latest(); } /** @@ -47,9 +45,6 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes { function quorumNumerator(uint256 timepoint) public view virtual returns (uint256) { // If history is empty, fallback to old storage uint256 length = _quorumNumeratorHistory._checkpoints.length; - if (length == 0) { - return _quorumNumerator; - } // Optimistic search, check the latest checkpoint Checkpoints.Checkpoint224 memory latest = _quorumNumeratorHistory._checkpoints[length - 1]; @@ -105,15 +100,6 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes { ); uint256 oldQuorumNumerator = quorumNumerator(); - - // Make sure we keep track of the original numerator in contracts upgraded from a version without checkpoints. - if (oldQuorumNumerator != 0 && _quorumNumeratorHistory._checkpoints.length == 0) { - _quorumNumeratorHistory._checkpoints.push( - Checkpoints.Checkpoint224({_key: 0, _value: SafeCast.toUint224(oldQuorumNumerator)}) - ); - } - - // Set new quorum for future proposals _quorumNumeratorHistory.push(SafeCast.toUint32(clock()), SafeCast.toUint224(newQuorumNumerator)); emit QuorumNumeratorUpdated(oldQuorumNumerator, newQuorumNumerator); diff --git a/contracts/interfaces/IERC1820Implementer.sol b/contracts/interfaces/IERC1820Implementer.sol index a83a7a3049b..c4d0b30289a 100644 --- a/contracts/interfaces/IERC1820Implementer.sol +++ b/contracts/interfaces/IERC1820Implementer.sol @@ -1,6 +1,20 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1820Implementer.sol) +// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC1820Implementer.sol) pragma solidity ^0.8.0; -import "../utils/introspection/IERC1820Implementer.sol"; +/** + * @dev Interface for an ERC1820 implementer, as defined in the + * https://eips.ethereum.org/EIPS/eip-1820#interface-implementation-erc1820implementerinterface[EIP]. + * Used by contracts that will be registered as implementers in the + * {IERC1820Registry}. + */ +interface IERC1820Implementer { + /** + * @dev Returns a special value (`ERC1820_ACCEPT_MAGIC`) if this contract + * implements `interfaceHash` for `account`. + * + * See {IERC1820Registry-setInterfaceImplementer}. + */ + function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) external view returns (bytes32); +} diff --git a/contracts/interfaces/IERC1820Registry.sol b/contracts/interfaces/IERC1820Registry.sol index 1b1ba9fcff3..a146bc2a68b 100644 --- a/contracts/interfaces/IERC1820Registry.sol +++ b/contracts/interfaces/IERC1820Registry.sol @@ -1,6 +1,112 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1820Registry.sol) +// OpenZeppelin Contracts (last updated v4.8.0) (utils/introspection/IERC1820Registry.sol) pragma solidity ^0.8.0; -import "../utils/introspection/IERC1820Registry.sol"; +/** + * @dev Interface of the global ERC1820 Registry, as defined in the + * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register + * implementers for interfaces in this registry, as well as query support. + * + * Implementers may be shared by multiple accounts, and can also implement more + * than a single interface for each account. Contracts can implement interfaces + * for themselves, but externally-owned accounts (EOA) must delegate this to a + * contract. + * + * {IERC165} interfaces can also be queried via the registry. + * + * For an in-depth explanation and source code analysis, see the EIP text. + */ +interface IERC1820Registry { + event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer); + + event ManagerChanged(address indexed account, address indexed newManager); + + /** + * @dev Sets `newManager` as the manager for `account`. A manager of an + * account is able to set interface implementers for it. + * + * By default, each account is its own manager. Passing a value of `0x0` in + * `newManager` will reset the manager to this initial state. + * + * Emits a {ManagerChanged} event. + * + * Requirements: + * + * - the caller must be the current manager for `account`. + */ + function setManager(address account, address newManager) external; + + /** + * @dev Returns the manager for `account`. + * + * See {setManager}. + */ + function getManager(address account) external view returns (address); + + /** + * @dev Sets the `implementer` contract as ``account``'s implementer for + * `interfaceHash`. + * + * `account` being the zero address is an alias for the caller's address. + * The zero address can also be used in `implementer` to remove an old one. + * + * See {interfaceHash} to learn how these are created. + * + * Emits an {InterfaceImplementerSet} event. + * + * Requirements: + * + * - the caller must be the current manager for `account`. + * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not + * end in 28 zeroes). + * - `implementer` must implement {IERC1820Implementer} and return true when + * queried for support, unless `implementer` is the caller. See + * {IERC1820Implementer-canImplementInterfaceForAddress}. + */ + function setInterfaceImplementer(address account, bytes32 _interfaceHash, address implementer) external; + + /** + * @dev Returns the implementer of `interfaceHash` for `account`. If no such + * implementer is registered, returns the zero address. + * + * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28 + * zeroes), `account` will be queried for support of it. + * + * `account` being the zero address is an alias for the caller's address. + */ + function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address); + + /** + * @dev Returns the interface hash for an `interfaceName`, as defined in the + * corresponding + * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP]. + */ + function interfaceHash(string calldata interfaceName) external pure returns (bytes32); + + /** + * @notice Updates the cache with whether the contract implements an ERC165 interface or not. + * @param account Address of the contract for which to update the cache. + * @param interfaceId ERC165 interface for which to update the cache. + */ + function updateERC165Cache(address account, bytes4 interfaceId) external; + + /** + * @notice Checks whether a contract implements an ERC165 interface or not. + * If the result is not cached a direct lookup on the contract address is performed. + * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling + * {updateERC165Cache} with the contract address. + * @param account Address of the contract to check. + * @param interfaceId ERC165 interface to check. + * @return True if `account` implements `interfaceId`, false otherwise. + */ + function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool); + + /** + * @notice Checks whether a contract implements an ERC165 interface or not without using or updating the cache. + * @param account Address of the contract to check. + * @param interfaceId ERC165 interface to check. + * @return True if `account` implements `interfaceId`, false otherwise. + */ + function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool); +} diff --git a/contracts/interfaces/IERC777.sol b/contracts/interfaces/IERC777.sol index b97ba7b801c..4d36da52cba 100644 --- a/contracts/interfaces/IERC777.sol +++ b/contracts/interfaces/IERC777.sol @@ -1,6 +1,199 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (interfaces/IERC777.sol) pragma solidity ^0.8.0; -import "../token/ERC777/IERC777.sol"; +/** + * @dev Interface of the ERC777Token standard as defined in the EIP. + * + * This contract uses the + * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let + * token holders and recipients react to token movements by using setting implementers + * for the associated interfaces in said registry. See {IERC1820Registry} and + * {IERC1820Implementer}. + */ +interface IERC777 { + /** + * @dev Emitted when `amount` tokens are created by `operator` and assigned to `to`. + * + * Note that some additional user `data` and `operatorData` can be logged in the event. + */ + event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData); + + /** + * @dev Emitted when `operator` destroys `amount` tokens from `account`. + * + * Note that some additional user `data` and `operatorData` can be logged in the event. + */ + event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData); + + /** + * @dev Emitted when `operator` is made operator for `tokenHolder`. + */ + event AuthorizedOperator(address indexed operator, address indexed tokenHolder); + + /** + * @dev Emitted when `operator` is revoked its operator status for `tokenHolder`. + */ + event RevokedOperator(address indexed operator, address indexed tokenHolder); + + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the smallest part of the token that is not divisible. This + * means all token operations (creation, movement and destruction) must have + * amounts that are a multiple of this number. + * + * For most token contracts, this value will equal 1. + */ + function granularity() external view returns (uint256); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by an account (`owner`). + */ + function balanceOf(address owner) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * If send or receive hooks are registered for the caller and `recipient`, + * the corresponding functions will be called with `data` and empty + * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. + * + * Emits a {Sent} event. + * + * Requirements + * + * - the caller must have at least `amount` tokens. + * - `recipient` cannot be the zero address. + * - if `recipient` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function send(address recipient, uint256 amount, bytes calldata data) external; + + /** + * @dev Destroys `amount` tokens from the caller's account, reducing the + * total supply. + * + * If a send hook is registered for the caller, the corresponding function + * will be called with `data` and empty `operatorData`. See {IERC777Sender}. + * + * Emits a {Burned} event. + * + * Requirements + * + * - the caller must have at least `amount` tokens. + */ + function burn(uint256 amount, bytes calldata data) external; + + /** + * @dev Returns true if an account is an operator of `tokenHolder`. + * Operators can send and burn tokens on behalf of their owners. All + * accounts are their own operator. + * + * See {operatorSend} and {operatorBurn}. + */ + function isOperatorFor(address operator, address tokenHolder) external view returns (bool); + + /** + * @dev Make an account an operator of the caller. + * + * See {isOperatorFor}. + * + * Emits an {AuthorizedOperator} event. + * + * Requirements + * + * - `operator` cannot be calling address. + */ + function authorizeOperator(address operator) external; + + /** + * @dev Revoke an account's operator status for the caller. + * + * See {isOperatorFor} and {defaultOperators}. + * + * Emits a {RevokedOperator} event. + * + * Requirements + * + * - `operator` cannot be calling address. + */ + function revokeOperator(address operator) external; + + /** + * @dev Returns the list of default operators. These accounts are operators + * for all token holders, even if {authorizeOperator} was never called on + * them. + * + * This list is immutable, but individual holders may revoke these via + * {revokeOperator}, in which case {isOperatorFor} will return false. + */ + function defaultOperators() external view returns (address[] memory); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must + * be an operator of `sender`. + * + * If send or receive hooks are registered for `sender` and `recipient`, + * the corresponding functions will be called with `data` and + * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. + * + * Emits a {Sent} event. + * + * Requirements + * + * - `sender` cannot be the zero address. + * - `sender` must have at least `amount` tokens. + * - the caller must be an operator for `sender`. + * - `recipient` cannot be the zero address. + * - if `recipient` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function operatorSend( + address sender, + address recipient, + uint256 amount, + bytes calldata data, + bytes calldata operatorData + ) external; + + /** + * @dev Destroys `amount` tokens from `account`, reducing the total supply. + * The caller must be an operator of `account`. + * + * If a send hook is registered for `account`, the corresponding function + * will be called with `data` and `operatorData`. See {IERC777Sender}. + * + * Emits a {Burned} event. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + * - the caller must be an operator for `account`. + */ + function operatorBurn(address account, uint256 amount, bytes calldata data, bytes calldata operatorData) external; + + event Sent( + address indexed operator, + address indexed from, + address indexed to, + uint256 amount, + bytes data, + bytes operatorData + ); +} diff --git a/contracts/interfaces/IERC777Recipient.sol b/contracts/interfaces/IERC777Recipient.sol index 0ce2704a875..069904855a4 100644 --- a/contracts/interfaces/IERC777Recipient.sol +++ b/contracts/interfaces/IERC777Recipient.sol @@ -1,6 +1,34 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (interfaces/IERC777Recipient.sol) pragma solidity ^0.8.0; -import "../token/ERC777/IERC777Recipient.sol"; +/** + * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. + * + * Accounts can be notified of {IERC777} tokens being sent to them by having a + * contract implement this interface (contract holders can be their own + * implementer) and registering it on the + * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. + * + * See {IERC1820Registry} and {IERC1820Implementer}. + */ +interface IERC777Recipient { + /** + * @dev Called by an {IERC777} token contract whenever tokens are being + * moved or created into a registered account (`to`). The type of operation + * is conveyed by `from` being the zero address or not. + * + * This call occurs _after_ the token contract's state is updated, so + * {IERC777-balanceOf}, etc., can be used to query the post-operation state. + * + * This function may revert to prevent the operation from being executed. + */ + function tokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external; +} diff --git a/contracts/interfaces/IERC777Sender.sol b/contracts/interfaces/IERC777Sender.sol index f1f17a22e92..c45477fcc29 100644 --- a/contracts/interfaces/IERC777Sender.sol +++ b/contracts/interfaces/IERC777Sender.sol @@ -1,6 +1,34 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (interfaces/IERC777Sender.sol) pragma solidity ^0.8.0; -import "../token/ERC777/IERC777Sender.sol"; +/** + * @dev Interface of the ERC777TokensSender standard as defined in the EIP. + * + * {IERC777} Token holders can be notified of operations performed on their + * tokens by having a contract implement this interface (contract holders can be + * their own implementer) and registering it on the + * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. + * + * See {IERC1820Registry} and {IERC1820Implementer}. + */ +interface IERC777Sender { + /** + * @dev Called by an {IERC777} token contract whenever a registered holder's + * (`from`) tokens are about to be moved or destroyed. The type of operation + * is conveyed by `to` being the zero address or not. + * + * This call occurs _before_ the token contract's state is updated, so + * {IERC777-balanceOf}, etc., can be used to query the pre-operation state. + * + * This function may revert to prevent the operation from being executed. + */ + function tokensToSend( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external; +} diff --git a/contracts/interfaces/draft-IERC2612.sol b/contracts/interfaces/draft-IERC2612.sol deleted file mode 100644 index 1ea7bf1c022..00000000000 --- a/contracts/interfaces/draft-IERC2612.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -// EIP-2612 is Final as of 2022-11-01. This file is deprecated. - -import "./IERC2612.sol"; diff --git a/contracts/mocks/AccessControlCrossChainMock.sol b/contracts/mocks/AccessControlCrossChainMock.sol deleted file mode 100644 index cd4c3a5d92c..00000000000 --- a/contracts/mocks/AccessControlCrossChainMock.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import "../access/AccessControlCrossChain.sol"; -import "../crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol"; - -contract AccessControlCrossChainMock is AccessControlCrossChain, CrossChainEnabledArbitrumL2 {} diff --git a/contracts/mocks/ConditionalEscrowMock.sol b/contracts/mocks/ConditionalEscrowMock.sol deleted file mode 100644 index ececf0521d5..00000000000 --- a/contracts/mocks/ConditionalEscrowMock.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/escrow/ConditionalEscrow.sol"; - -// mock class using ConditionalEscrow -contract ConditionalEscrowMock is ConditionalEscrow { - mapping(address => bool) private _allowed; - - function setAllowed(address payee, bool allowed) public { - _allowed[payee] = allowed; - } - - function withdrawalAllowed(address payee) public view override returns (bool) { - return _allowed[payee]; - } -} diff --git a/contracts/mocks/PullPaymentMock.sol b/contracts/mocks/PullPaymentMock.sol deleted file mode 100644 index 8a708e30ce1..00000000000 --- a/contracts/mocks/PullPaymentMock.sol +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../security/PullPayment.sol"; - -// mock class using PullPayment -contract PullPaymentMock is PullPayment { - constructor() payable {} - - // test helper function to call asyncTransfer - function callTransfer(address dest, uint256 amount) public { - _asyncTransfer(dest, amount); - } -} diff --git a/contracts/mocks/SafeMathMemoryCheck.sol b/contracts/mocks/SafeMathMemoryCheck.sol deleted file mode 100644 index 96946881ad7..00000000000 --- a/contracts/mocks/SafeMathMemoryCheck.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/math/SafeMath.sol"; - -library SafeMathMemoryCheck { - function addMemoryCheck() internal pure returns (uint256 mem) { - uint256 length = 32; - assembly { - mem := mload(0x40) - } - for (uint256 i = 0; i < length; ++i) { - SafeMath.add(1, 1); - } - assembly { - mem := sub(mload(0x40), mem) - } - } - - function subMemoryCheck() internal pure returns (uint256 mem) { - uint256 length = 32; - assembly { - mem := mload(0x40) - } - for (uint256 i = 0; i < length; ++i) { - SafeMath.sub(1, 1); - } - assembly { - mem := sub(mload(0x40), mem) - } - } - - function mulMemoryCheck() internal pure returns (uint256 mem) { - uint256 length = 32; - assembly { - mem := mload(0x40) - } - for (uint256 i = 0; i < length; ++i) { - SafeMath.mul(1, 1); - } - assembly { - mem := sub(mload(0x40), mem) - } - } - - function divMemoryCheck() internal pure returns (uint256 mem) { - uint256 length = 32; - assembly { - mem := mload(0x40) - } - for (uint256 i = 0; i < length; ++i) { - SafeMath.div(1, 1); - } - assembly { - mem := sub(mload(0x40), mem) - } - } - - function modMemoryCheck() internal pure returns (uint256 mem) { - uint256 length = 32; - assembly { - mem := mload(0x40) - } - for (uint256 i = 0; i < length; ++i) { - SafeMath.mod(1, 1); - } - assembly { - mem := sub(mload(0x40), mem) - } - } -} diff --git a/contracts/mocks/TimersBlockNumberImpl.sol b/contracts/mocks/TimersBlockNumberImpl.sol deleted file mode 100644 index 84633e6f830..00000000000 --- a/contracts/mocks/TimersBlockNumberImpl.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Timers.sol"; - -contract TimersBlockNumberImpl { - using Timers for Timers.BlockNumber; - - Timers.BlockNumber private _timer; - - function getDeadline() public view returns (uint64) { - return _timer.getDeadline(); - } - - function setDeadline(uint64 timestamp) public { - _timer.setDeadline(timestamp); - } - - function reset() public { - _timer.reset(); - } - - function isUnset() public view returns (bool) { - return _timer.isUnset(); - } - - function isStarted() public view returns (bool) { - return _timer.isStarted(); - } - - function isPending() public view returns (bool) { - return _timer.isPending(); - } - - function isExpired() public view returns (bool) { - return _timer.isExpired(); - } -} diff --git a/contracts/mocks/TimersTimestampImpl.sol b/contracts/mocks/TimersTimestampImpl.sol deleted file mode 100644 index 07f9a1b3f20..00000000000 --- a/contracts/mocks/TimersTimestampImpl.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../utils/Timers.sol"; - -contract TimersTimestampImpl { - using Timers for Timers.Timestamp; - - Timers.Timestamp private _timer; - - function getDeadline() public view returns (uint64) { - return _timer.getDeadline(); - } - - function setDeadline(uint64 timestamp) public { - _timer.setDeadline(timestamp); - } - - function reset() public { - _timer.reset(); - } - - function isUnset() public view returns (bool) { - return _timer.isUnset(); - } - - function isStarted() public view returns (bool) { - return _timer.isStarted(); - } - - function isPending() public view returns (bool) { - return _timer.isPending(); - } - - function isExpired() public view returns (bool) { - return _timer.isExpired(); - } -} diff --git a/contracts/mocks/crosschain/bridges.sol b/contracts/mocks/crosschain/bridges.sol deleted file mode 100644 index 41baffed805..00000000000 --- a/contracts/mocks/crosschain/bridges.sol +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../../utils/Address.sol"; -import "../../vendor/polygon/IFxMessageProcessor.sol"; - -abstract contract BaseRelayMock { - // needed to parse custom errors - error NotCrossChainCall(); - error InvalidCrossChainSender(address sender, address expected); - - address internal _currentSender; - - function relayAs(address target, bytes calldata data, address sender) external virtual { - address previousSender = _currentSender; - - _currentSender = sender; - - (bool success, bytes memory returndata) = target.call(data); - Address.verifyCallResultFromTarget(target, success, returndata, "low-level call reverted"); - - _currentSender = previousSender; - } -} - -/** - * AMB - */ -contract BridgeAMBMock is BaseRelayMock { - function messageSender() public view returns (address) { - return _currentSender; - } -} - -/** - * Arbitrum - */ -contract BridgeArbitrumL1Mock is BaseRelayMock { - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment - address public immutable inbox = address(new BridgeArbitrumL1Inbox()); - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment - address public immutable outbox = address(new BridgeArbitrumL1Outbox()); - - function activeOutbox() public view returns (address) { - return outbox; - } - - function currentSender() public view returns (address) { - return _currentSender; - } -} - -contract BridgeArbitrumL1Inbox { - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment - address public immutable bridge = msg.sender; -} - -contract BridgeArbitrumL1Outbox { - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment - address public immutable bridge = msg.sender; - - function l2ToL1Sender() public view returns (address) { - return BridgeArbitrumL1Mock(bridge).currentSender(); - } -} - -contract BridgeArbitrumL2Mock is BaseRelayMock { - function wasMyCallersAddressAliased() public view returns (bool) { - return _currentSender != address(0); - } - - function myCallersAddressWithoutAliasing() public view returns (address) { - return _currentSender; - } -} - -/** - * Optimism - */ -contract BridgeOptimismMock is BaseRelayMock { - function xDomainMessageSender() public view returns (address) { - return _currentSender; - } -} - -/** - * Polygon - */ -contract BridgePolygonChildMock is BaseRelayMock { - function relayAs(address target, bytes calldata data, address sender) external override { - IFxMessageProcessor(target).processMessageFromRoot(0, sender, data); - } -} diff --git a/contracts/mocks/crosschain/receivers.sol b/contracts/mocks/crosschain/receivers.sol deleted file mode 100644 index 601a2ac105f..00000000000 --- a/contracts/mocks/crosschain/receivers.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import "../../access/Ownable.sol"; -import "../../crosschain/amb/CrossChainEnabledAMB.sol"; -import "../../crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol"; -import "../../crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol"; -import "../../crosschain/optimism/CrossChainEnabledOptimism.sol"; -import "../../crosschain/polygon/CrossChainEnabledPolygonChild.sol"; - -abstract contract Receiver is CrossChainEnabled { - // we don't use Ownable because it messes up testing for the upgradeable contracts - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment - address public immutable owner = msg.sender; - - function crossChainRestricted() external onlyCrossChain {} - - function crossChainOwnerRestricted() external onlyCrossChainSender(owner) {} -} - -/** - * AMB - */ -contract CrossChainEnabledAMBMock is Receiver, CrossChainEnabledAMB { - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(address bridge) CrossChainEnabledAMB(bridge) {} -} - -/** - * Arbitrum - */ -contract CrossChainEnabledArbitrumL1Mock is Receiver, CrossChainEnabledArbitrumL1 { - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(address bridge) CrossChainEnabledArbitrumL1(bridge) {} -} - -contract CrossChainEnabledArbitrumL2Mock is Receiver, CrossChainEnabledArbitrumL2 {} - -/** - * Optimism - */ -contract CrossChainEnabledOptimismMock is Receiver, CrossChainEnabledOptimism { - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(address bridge) CrossChainEnabledOptimism(bridge) {} -} - -/** - * Polygon - */ -contract CrossChainEnabledPolygonChildMock is Receiver, CrossChainEnabledPolygonChild { - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(address bridge) CrossChainEnabledPolygonChild(bridge) {} -} diff --git a/contracts/mocks/governance/GovernorMock.sol b/contracts/mocks/governance/GovernorMock.sol index 8a1db47041d..9284a24fef3 100644 --- a/contracts/mocks/governance/GovernorMock.sol +++ b/contracts/mocks/governance/GovernorMock.sol @@ -2,27 +2,12 @@ pragma solidity ^0.8.0; -import "../../governance/extensions/GovernorProposalThreshold.sol"; import "../../governance/extensions/GovernorSettings.sol"; import "../../governance/extensions/GovernorCountingSimple.sol"; import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; -abstract contract GovernorMock is - GovernorProposalThreshold, - GovernorSettings, - GovernorVotesQuorumFraction, - GovernorCountingSimple -{ +abstract contract GovernorMock is GovernorSettings, GovernorVotesQuorumFraction, GovernorCountingSimple { function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { return super.proposalThreshold(); } - - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public override(Governor, GovernorProposalThreshold) returns (uint256) { - return super.propose(targets, values, calldatas, description); - } } diff --git a/contracts/mocks/token/ERC20PermitNoRevertMock.sol b/contracts/mocks/token/ERC20PermitNoRevertMock.sol index 2176c51aef3..f2562d3ccff 100644 --- a/contracts/mocks/token/ERC20PermitNoRevertMock.sol +++ b/contracts/mocks/token/ERC20PermitNoRevertMock.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import "../../token/ERC20/ERC20.sol"; -import "../../token/ERC20/extensions/draft-ERC20Permit.sol"; +import "../../token/ERC20/extensions/ERC20Permit.sol"; abstract contract ERC20PermitNoRevertMock is ERC20Permit { function permitThatMayRevert( diff --git a/contracts/mocks/token/ERC721ConsecutiveMock.sol b/contracts/mocks/token/ERC721ConsecutiveMock.sol index 427f44a19c7..8bfa0cb9e0d 100644 --- a/contracts/mocks/token/ERC721ConsecutiveMock.sol +++ b/contracts/mocks/token/ERC721ConsecutiveMock.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.0; import "../../token/ERC721/extensions/ERC721Consecutive.sol"; import "../../token/ERC721/extensions/ERC721Enumerable.sol"; import "../../token/ERC721/extensions/ERC721Pausable.sol"; -import "../../token/ERC721/extensions/draft-ERC721Votes.sol"; +import "../../token/ERC721/extensions/ERC721Votes.sol"; /** * @title ERC721ConsecutiveMock diff --git a/contracts/mocks/token/ERC777Mock.sol b/contracts/mocks/token/ERC777Mock.sol deleted file mode 100644 index 685277e8bb8..00000000000 --- a/contracts/mocks/token/ERC777Mock.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../../token/ERC777/ERC777.sol"; - -abstract contract ERC777Mock is ERC777 { - event BeforeTokenTransfer(); - - function _beforeTokenTransfer(address, address, address, uint256) internal override { - emit BeforeTokenTransfer(); - } -} diff --git a/contracts/mocks/token/ERC777SenderRecipientMock.sol b/contracts/mocks/token/ERC777SenderRecipientMock.sol deleted file mode 100644 index 3bec6d94bdb..00000000000 --- a/contracts/mocks/token/ERC777SenderRecipientMock.sol +++ /dev/null @@ -1,152 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../../token/ERC777/IERC777.sol"; -import "../../token/ERC777/IERC777Sender.sol"; -import "../../token/ERC777/IERC777Recipient.sol"; -import "../../utils/Context.sol"; -import "../../utils/introspection/IERC1820Registry.sol"; -import "../../utils/introspection/ERC1820Implementer.sol"; - -contract ERC777SenderRecipientMock is Context, IERC777Sender, IERC777Recipient, ERC1820Implementer { - event TokensToSendCalled( - address operator, - address from, - address to, - uint256 amount, - bytes data, - bytes operatorData, - address token, - uint256 fromBalance, - uint256 toBalance - ); - - event TokensReceivedCalled( - address operator, - address from, - address to, - uint256 amount, - bytes data, - bytes operatorData, - address token, - uint256 fromBalance, - uint256 toBalance - ); - - // Emitted in ERC777Mock. Here for easier decoding - event BeforeTokenTransfer(); - - bool private _shouldRevertSend; - bool private _shouldRevertReceive; - - IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); - - bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender"); - bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); - - function tokensToSend( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external override { - if (_shouldRevertSend) { - revert(); - } - - IERC777 token = IERC777(_msgSender()); - - uint256 fromBalance = token.balanceOf(from); - // when called due to burn, to will be the zero address, which will have a balance of 0 - uint256 toBalance = token.balanceOf(to); - - emit TokensToSendCalled( - operator, - from, - to, - amount, - userData, - operatorData, - address(token), - fromBalance, - toBalance - ); - } - - function tokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external override { - if (_shouldRevertReceive) { - revert(); - } - - IERC777 token = IERC777(_msgSender()); - - uint256 fromBalance = token.balanceOf(from); - // when called due to burn, to will be the zero address, which will have a balance of 0 - uint256 toBalance = token.balanceOf(to); - - emit TokensReceivedCalled( - operator, - from, - to, - amount, - userData, - operatorData, - address(token), - fromBalance, - toBalance - ); - } - - function senderFor(address account) public { - _registerInterfaceForAddress(_TOKENS_SENDER_INTERFACE_HASH, account); - - address self = address(this); - if (account == self) { - registerSender(self); - } - } - - function registerSender(address sender) public { - _erc1820.setInterfaceImplementer(address(this), _TOKENS_SENDER_INTERFACE_HASH, sender); - } - - function recipientFor(address account) public { - _registerInterfaceForAddress(_TOKENS_RECIPIENT_INTERFACE_HASH, account); - - address self = address(this); - if (account == self) { - registerRecipient(self); - } - } - - function registerRecipient(address recipient) public { - _erc1820.setInterfaceImplementer(address(this), _TOKENS_RECIPIENT_INTERFACE_HASH, recipient); - } - - function setShouldRevertSend(bool shouldRevert) public { - _shouldRevertSend = shouldRevert; - } - - function setShouldRevertReceive(bool shouldRevert) public { - _shouldRevertReceive = shouldRevert; - } - - function send(IERC777 token, address to, uint256 amount, bytes memory data) public { - // This is 777's send function, not the Solidity send function - token.send(to, amount, data); // solhint-disable-line check-send-result - } - - function burn(IERC777 token, uint256 amount, bytes memory data) public { - token.burn(amount, data); - } -} diff --git a/contracts/mocks/wizard/MyGovernor1.sol b/contracts/mocks/wizard/MyGovernor1.sol deleted file mode 100644 index 37ecfd57d8b..00000000000 --- a/contracts/mocks/wizard/MyGovernor1.sol +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.2; - -import "../../governance/Governor.sol"; -import "../../governance/extensions/GovernorCountingSimple.sol"; -import "../../governance/extensions/GovernorVotes.sol"; -import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; -import "../../governance/extensions/GovernorTimelockControl.sol"; - -contract MyGovernor1 is - Governor, - GovernorTimelockControl, - GovernorVotes, - GovernorVotesQuorumFraction, - GovernorCountingSimple -{ - constructor( - IVotes _token, - TimelockController _timelock - ) Governor("MyGovernor") GovernorVotes(_token) GovernorVotesQuorumFraction(4) GovernorTimelockControl(_timelock) {} - - function votingDelay() public pure override returns (uint256) { - return 1; // 1 block - } - - function votingPeriod() public pure override returns (uint256) { - return 45818; // 1 week - } - - // The following functions are overrides required by Solidity. - - function quorum( - uint256 blockNumber - ) public view override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { - return super.quorum(blockNumber); - } - - function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { - return super.state(proposalId); - } - - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public override(Governor, IGovernor) returns (uint256) { - return super.propose(targets, values, calldatas, description); - } - - function _execute( - uint256 proposalId, - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) { - super._execute(proposalId, targets, values, calldatas, descriptionHash); - } - - function _cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) returns (uint256) { - return super._cancel(targets, values, calldatas, descriptionHash); - } - - function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { - return super._executor(); - } - - function supportsInterface( - bytes4 interfaceId - ) public view override(Governor, GovernorTimelockControl) returns (bool) { - return super.supportsInterface(interfaceId); - } -} diff --git a/contracts/mocks/wizard/MyGovernor2.sol b/contracts/mocks/wizard/MyGovernor2.sol deleted file mode 100644 index 1472b67d588..00000000000 --- a/contracts/mocks/wizard/MyGovernor2.sol +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.2; - -import "../../governance/Governor.sol"; -import "../../governance/extensions/GovernorProposalThreshold.sol"; -import "../../governance/extensions/GovernorCountingSimple.sol"; -import "../../governance/extensions/GovernorVotes.sol"; -import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; -import "../../governance/extensions/GovernorTimelockControl.sol"; - -contract MyGovernor2 is - Governor, - GovernorTimelockControl, - GovernorProposalThreshold, - GovernorVotes, - GovernorVotesQuorumFraction, - GovernorCountingSimple -{ - constructor( - IVotes _token, - TimelockController _timelock - ) Governor("MyGovernor") GovernorVotes(_token) GovernorVotesQuorumFraction(4) GovernorTimelockControl(_timelock) {} - - function votingDelay() public pure override returns (uint256) { - return 1; // 1 block - } - - function votingPeriod() public pure override returns (uint256) { - return 45818; // 1 week - } - - function proposalThreshold() public pure override returns (uint256) { - return 1000e18; - } - - // The following functions are overrides required by Solidity. - - function quorum( - uint256 blockNumber - ) public view override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { - return super.quorum(blockNumber); - } - - function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { - return super.state(proposalId); - } - - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public override(Governor, GovernorProposalThreshold, IGovernor) returns (uint256) { - return super.propose(targets, values, calldatas, description); - } - - function _execute( - uint256 proposalId, - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) { - super._execute(proposalId, targets, values, calldatas, descriptionHash); - } - - function _cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) returns (uint256) { - return super._cancel(targets, values, calldatas, descriptionHash); - } - - function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { - return super._executor(); - } - - function supportsInterface( - bytes4 interfaceId - ) public view override(Governor, GovernorTimelockControl) returns (bool) { - return super.supportsInterface(interfaceId); - } -} diff --git a/contracts/mocks/wizard/MyGovernor3.sol b/contracts/mocks/wizard/MyGovernor3.sol deleted file mode 100644 index f4d29515627..00000000000 --- a/contracts/mocks/wizard/MyGovernor3.sol +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.2; - -import "../../governance/Governor.sol"; -import "../../governance/compatibility/GovernorCompatibilityBravo.sol"; -import "../../governance/extensions/GovernorVotes.sol"; -import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; -import "../../governance/extensions/GovernorTimelockControl.sol"; - -contract MyGovernor is - Governor, - GovernorTimelockControl, - GovernorCompatibilityBravo, - GovernorVotes, - GovernorVotesQuorumFraction -{ - constructor( - IVotes _token, - TimelockController _timelock - ) Governor("MyGovernor") GovernorVotes(_token) GovernorVotesQuorumFraction(4) GovernorTimelockControl(_timelock) {} - - function votingDelay() public pure override returns (uint256) { - return 1; // 1 block - } - - function votingPeriod() public pure override returns (uint256) { - return 45818; // 1 week - } - - function proposalThreshold() public pure override returns (uint256) { - return 1000e18; - } - - // The following functions are overrides required by Solidity. - - function quorum( - uint256 blockNumber - ) public view override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { - return super.quorum(blockNumber); - } - - function state( - uint256 proposalId - ) public view override(Governor, IGovernor, GovernorTimelockControl) returns (ProposalState) { - return super.state(proposalId); - } - - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public override(Governor, GovernorCompatibilityBravo, IGovernor) returns (uint256) { - return super.propose(targets, values, calldatas, description); - } - - function cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) public override(Governor, GovernorCompatibilityBravo, IGovernor) returns (uint256) { - return super.cancel(targets, values, calldatas, descriptionHash); - } - - function _execute( - uint256 proposalId, - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) { - super._execute(proposalId, targets, values, calldatas, descriptionHash); - } - - function _cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) returns (uint256) { - return super._cancel(targets, values, calldatas, descriptionHash); - } - - function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { - return super._executor(); - } - - function supportsInterface( - bytes4 interfaceId - ) public view override(Governor, IERC165, GovernorTimelockControl) returns (bool) { - return super.supportsInterface(interfaceId); - } -} diff --git a/contracts/proxy/beacon/BeaconProxy.sol b/contracts/proxy/beacon/BeaconProxy.sol index d217b15eae2..a278884f438 100644 --- a/contracts/proxy/beacon/BeaconProxy.sol +++ b/contracts/proxy/beacon/BeaconProxy.sol @@ -31,31 +31,10 @@ contract BeaconProxy is Proxy, ERC1967Upgrade { _upgradeBeaconToAndCall(beacon, data, false); } - /** - * @dev Returns the current beacon address. - */ - function _beacon() internal view virtual returns (address) { - return _getBeacon(); - } - /** * @dev Returns the current implementation address of the associated beacon. */ function _implementation() internal view virtual override returns (address) { return IBeacon(_getBeacon()).implementation(); } - - /** - * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}. - * - * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. - * - * Requirements: - * - * - `beacon` must be a contract. - * - The implementation returned by `beacon` must be a contract. - */ - function _setBeacon(address beacon, bytes memory data) internal virtual { - _upgradeBeaconToAndCall(beacon, data, false); - } } diff --git a/contracts/proxy/transparent/TransparentUpgradeableProxy.sol b/contracts/proxy/transparent/TransparentUpgradeableProxy.sol index fbef40619ac..7bdef7c28be 100644 --- a/contracts/proxy/transparent/TransparentUpgradeableProxy.sol +++ b/contracts/proxy/transparent/TransparentUpgradeableProxy.sol @@ -60,20 +60,6 @@ contract TransparentUpgradeableProxy is ERC1967Proxy { _changeAdmin(admin_); } - /** - * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin. - * - * CAUTION: This modifier is deprecated, as it could cause issues if the modified function has arguments, and the - * implementation provides a function with the same selector. - */ - modifier ifAdmin() { - if (msg.sender == _getAdmin()) { - _; - } else { - _fallback(); - } - } - /** * @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior */ @@ -137,17 +123,8 @@ contract TransparentUpgradeableProxy is ERC1967Proxy { } /** - * @dev Returns the current admin. - * - * CAUTION: This function is deprecated. Use {ERC1967Upgrade-_getAdmin} instead. - */ - function _admin() internal view virtual returns (address) { - return _getAdmin(); - } - - /** - * @dev To keep this contract fully transparent, all `ifAdmin` functions must be payable. This helper is here to - * emulate some proxy functions being non-payable while still allowing value to pass through. + * @dev To keep this contract fully transparent, the fallback is payable. This helper is here to enforce + * non-payability of function implemented through dispatchers while still allowing value to pass through. */ function _requireZeroValue() private { require(msg.value == 0); diff --git a/contracts/security/PullPayment.sol b/contracts/security/PullPayment.sol deleted file mode 100644 index 65b4980f650..00000000000 --- a/contracts/security/PullPayment.sol +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (security/PullPayment.sol) - -pragma solidity ^0.8.0; - -import "../utils/escrow/Escrow.sol"; - -/** - * @dev Simple implementation of a - * https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/external-calls/#favor-pull-over-push-for-external-calls[pull-payment] - * strategy, where the paying contract doesn't interact directly with the - * receiver account, which must withdraw its payments itself. - * - * Pull-payments are often considered the best practice when it comes to sending - * Ether, security-wise. It prevents recipients from blocking execution, and - * eliminates reentrancy concerns. - * - * TIP: If you would like to learn more about reentrancy and alternative ways - * to protect against it, check out our blog post - * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. - * - * To use, derive from the `PullPayment` contract, and use {_asyncTransfer} - * instead of Solidity's `transfer` function. Payees can query their due - * payments with {payments}, and retrieve them with {withdrawPayments}. - */ -abstract contract PullPayment { - Escrow private immutable _escrow; - - constructor() { - _escrow = new Escrow(); - } - - /** - * @dev Withdraw accumulated payments, forwarding all gas to the recipient. - * - * Note that _any_ account can call this function, not just the `payee`. - * This means that contracts unaware of the `PullPayment` protocol can still - * receive funds this way, by having a separate account call - * {withdrawPayments}. - * - * WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities. - * Make sure you trust the recipient, or are either following the - * checks-effects-interactions pattern or using {ReentrancyGuard}. - * - * @param payee Whose payments will be withdrawn. - * - * Causes the `escrow` to emit a {Withdrawn} event. - */ - function withdrawPayments(address payable payee) public virtual { - _escrow.withdraw(payee); - } - - /** - * @dev Returns the payments owed to an address. - * @param dest The creditor's address. - */ - function payments(address dest) public view returns (uint256) { - return _escrow.depositsOf(dest); - } - - /** - * @dev Called by the payer to store the sent amount as credit to be pulled. - * Funds sent in this way are stored in an intermediate {Escrow} contract, so - * there is no danger of them being spent before withdrawal. - * - * @param dest The destination address of the funds. - * @param amount The amount to transfer. - * - * Causes the `escrow` to emit a {Deposited} event. - */ - function _asyncTransfer(address dest, uint256 amount) internal virtual { - _escrow.deposit{value: amount}(dest); - } -} diff --git a/contracts/security/README.adoc b/contracts/security/README.adoc index 66f398fece5..7f4799eb81e 100644 --- a/contracts/security/README.adoc +++ b/contracts/security/README.adoc @@ -5,7 +5,6 @@ NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/ These contracts aim to cover common security practices. -* {PullPayment}: A pattern that can be used to avoid reentrancy attacks. * {ReentrancyGuard}: A modifier that can prevent reentrancy during certain functions. * {Pausable}: A common emergency response mechanism that can pause functionality while a remediation is pending. @@ -13,8 +12,6 @@ TIP: For an overview on reentrancy and the possible mechanisms to prevent it, re == Contracts -{{PullPayment}} - {{ReentrancyGuard}} {{Pausable}} diff --git a/contracts/token/ERC20/extensions/ERC20Permit.sol b/contracts/token/ERC20/extensions/ERC20Permit.sol index 70b48a8d402..8f476b6628b 100644 --- a/contracts/token/ERC20/extensions/ERC20Permit.sol +++ b/contracts/token/ERC20/extensions/ERC20Permit.sol @@ -23,14 +23,6 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712, Nonces { // solhint-disable-next-line var-name-mixedcase bytes32 private constant _PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); - /** - * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`. - * However, to ensure consistency with the upgradeable transpiler, we will continue - * to reserve a slot. - * @custom:oz-renamed-from _PERMIT_TYPEHASH - */ - // solhint-disable-next-line var-name-mixedcase - bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT; /** * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. diff --git a/contracts/token/ERC20/extensions/draft-ERC20Permit.sol b/contracts/token/ERC20/extensions/draft-ERC20Permit.sol deleted file mode 100644 index 6579ef33f9b..00000000000 --- a/contracts/token/ERC20/extensions/draft-ERC20Permit.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol) - -pragma solidity ^0.8.0; - -// EIP-2612 is Final as of 2022-11-01. This file is deprecated. - -import "./ERC20Permit.sol"; diff --git a/contracts/token/ERC20/extensions/draft-IERC20Permit.sol b/contracts/token/ERC20/extensions/draft-IERC20Permit.sol deleted file mode 100644 index 1df6c537d02..00000000000 --- a/contracts/token/ERC20/extensions/draft-IERC20Permit.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -// EIP-2612 is Final as of 2022-11-01. This file is deprecated. - -import "./IERC20Permit.sol"; diff --git a/contracts/token/ERC20/utils/SafeERC20.sol b/contracts/token/ERC20/utils/SafeERC20.sol index 805a1565f14..5eb25932e09 100644 --- a/contracts/token/ERC20/utils/SafeERC20.sol +++ b/contracts/token/ERC20/utils/SafeERC20.sol @@ -35,24 +35,6 @@ library SafeERC20 { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } - /** - * @dev Deprecated. This function has issues similar to the ones found in - * {IERC20-approve}, and its usage is discouraged. - * - * Whenever possible, use {safeIncreaseAllowance} and - * {safeDecreaseAllowance} instead. - */ - function safeApprove(IERC20 token, address spender, uint256 value) internal { - // safeApprove should only be called when setting an initial allowance, - // or when resetting it to zero. To increase and decrease it, use - // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' - require( - (value == 0) || (token.allowance(address(this), spender) == 0), - "SafeERC20: approve from non-zero to non-zero allowance" - ); - _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); - } - /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. diff --git a/contracts/token/ERC721/extensions/draft-ERC721Votes.sol b/contracts/token/ERC721/extensions/draft-ERC721Votes.sol deleted file mode 100644 index c6aa7c56457..00000000000 --- a/contracts/token/ERC721/extensions/draft-ERC721Votes.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/draft-ERC721Votes.sol) - -pragma solidity ^0.8.0; - -// ERC721Votes was marked as draft due to the EIP-712 dependency. -// EIP-712 is Final as of 2022-08-11. This file is deprecated. - -import "./ERC721Votes.sol"; diff --git a/contracts/token/ERC777/ERC777.sol b/contracts/token/ERC777/ERC777.sol deleted file mode 100644 index 063337d7ec4..00000000000 --- a/contracts/token/ERC777/ERC777.sol +++ /dev/null @@ -1,517 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC777/ERC777.sol) - -pragma solidity ^0.8.1; - -import "./IERC777.sol"; -import "./IERC777Recipient.sol"; -import "./IERC777Sender.sol"; -import "../ERC20/IERC20.sol"; -import "../../utils/Address.sol"; -import "../../utils/Context.sol"; -import "../../utils/introspection/IERC1820Registry.sol"; - -/** - * @dev Implementation of the {IERC777} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * - * Support for ERC20 is included in this contract, as specified by the EIP: both - * the ERC777 and ERC20 interfaces can be safely used when interacting with it. - * Both {IERC777-Sent} and {IERC20-Transfer} events are emitted on token - * movements. - * - * Additionally, the {IERC777-granularity} value is hard-coded to `1`, meaning that there - * are no special restrictions in the amount of tokens that created, moved, or - * destroyed. This makes integration with ERC20 applications seamless. - * - * CAUTION: This file is deprecated as of v4.9 and will be removed in the next major release. - */ -contract ERC777 is Context, IERC777, IERC20 { - using Address for address; - - IERC1820Registry internal constant _ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); - - mapping(address => uint256) private _balances; - - uint256 private _totalSupply; - - string private _name; - string private _symbol; - - bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender"); - bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); - - // This isn't ever read from - it's only used to respond to the defaultOperators query. - address[] private _defaultOperatorsArray; - - // Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators). - mapping(address => bool) private _defaultOperators; - - // For each account, a mapping of its operators and revoked default operators. - mapping(address => mapping(address => bool)) private _operators; - mapping(address => mapping(address => bool)) private _revokedDefaultOperators; - - // ERC20-allowances - mapping(address => mapping(address => uint256)) private _allowances; - - /** - * @dev `defaultOperators` may be an empty array. - */ - constructor(string memory name_, string memory symbol_, address[] memory defaultOperators_) { - _name = name_; - _symbol = symbol_; - - _defaultOperatorsArray = defaultOperators_; - for (uint256 i = 0; i < defaultOperators_.length; i++) { - _defaultOperators[defaultOperators_[i]] = true; - } - - // register interfaces - _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this)); - _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC20Token"), address(this)); - } - - /** - * @dev See {IERC777-name}. - */ - function name() public view virtual override returns (string memory) { - return _name; - } - - /** - * @dev See {IERC777-symbol}. - */ - function symbol() public view virtual override returns (string memory) { - return _symbol; - } - - /** - * @dev See {ERC20-decimals}. - * - * Always returns 18, as per the - * [ERC777 EIP](https://eips.ethereum.org/EIPS/eip-777#backward-compatibility). - */ - function decimals() public pure virtual returns (uint8) { - return 18; - } - - /** - * @dev See {IERC777-granularity}. - * - * This implementation always returns `1`. - */ - function granularity() public view virtual override returns (uint256) { - return 1; - } - - /** - * @dev See {IERC777-totalSupply}. - */ - function totalSupply() public view virtual override(IERC20, IERC777) returns (uint256) { - return _totalSupply; - } - - /** - * @dev Returns the amount of tokens owned by an account (`tokenHolder`). - */ - function balanceOf(address tokenHolder) public view virtual override(IERC20, IERC777) returns (uint256) { - return _balances[tokenHolder]; - } - - /** - * @dev See {IERC777-send}. - * - * Also emits a {IERC20-Transfer} event for ERC20 compatibility. - */ - function send(address recipient, uint256 amount, bytes memory data) public virtual override { - _send(_msgSender(), recipient, amount, data, "", true); - } - - /** - * @dev See {IERC20-transfer}. - * - * Unlike `send`, `recipient` is _not_ required to implement the {IERC777Recipient} - * interface if it is a contract. - * - * Also emits a {Sent} event. - */ - function transfer(address recipient, uint256 amount) public virtual override returns (bool) { - _send(_msgSender(), recipient, amount, "", "", false); - return true; - } - - /** - * @dev See {IERC777-burn}. - * - * Also emits a {IERC20-Transfer} event for ERC20 compatibility. - */ - function burn(uint256 amount, bytes memory data) public virtual override { - _burn(_msgSender(), amount, data, ""); - } - - /** - * @dev See {IERC777-isOperatorFor}. - */ - function isOperatorFor(address operator, address tokenHolder) public view virtual override returns (bool) { - return - operator == tokenHolder || - (_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) || - _operators[tokenHolder][operator]; - } - - /** - * @dev See {IERC777-authorizeOperator}. - */ - function authorizeOperator(address operator) public virtual override { - require(_msgSender() != operator, "ERC777: authorizing self as operator"); - - if (_defaultOperators[operator]) { - delete _revokedDefaultOperators[_msgSender()][operator]; - } else { - _operators[_msgSender()][operator] = true; - } - - emit AuthorizedOperator(operator, _msgSender()); - } - - /** - * @dev See {IERC777-revokeOperator}. - */ - function revokeOperator(address operator) public virtual override { - require(operator != _msgSender(), "ERC777: revoking self as operator"); - - if (_defaultOperators[operator]) { - _revokedDefaultOperators[_msgSender()][operator] = true; - } else { - delete _operators[_msgSender()][operator]; - } - - emit RevokedOperator(operator, _msgSender()); - } - - /** - * @dev See {IERC777-defaultOperators}. - */ - function defaultOperators() public view virtual override returns (address[] memory) { - return _defaultOperatorsArray; - } - - /** - * @dev See {IERC777-operatorSend}. - * - * Emits {Sent} and {IERC20-Transfer} events. - */ - function operatorSend( - address sender, - address recipient, - uint256 amount, - bytes memory data, - bytes memory operatorData - ) public virtual override { - require(isOperatorFor(_msgSender(), sender), "ERC777: caller is not an operator for holder"); - _send(sender, recipient, amount, data, operatorData, true); - } - - /** - * @dev See {IERC777-operatorBurn}. - * - * Emits {Burned} and {IERC20-Transfer} events. - */ - function operatorBurn( - address account, - uint256 amount, - bytes memory data, - bytes memory operatorData - ) public virtual override { - require(isOperatorFor(_msgSender(), account), "ERC777: caller is not an operator for holder"); - _burn(account, amount, data, operatorData); - } - - /** - * @dev See {IERC20-allowance}. - * - * Note that operator and allowance concepts are orthogonal: operators may - * not have allowance, and accounts with allowance may not be operators - * themselves. - */ - function allowance(address holder, address spender) public view virtual override returns (uint256) { - return _allowances[holder][spender]; - } - - /** - * @dev See {IERC20-approve}. - * - * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on - * `transferFrom`. This is semantically equivalent to an infinite approval. - * - * Note that accounts cannot have allowance issued by their operators. - */ - function approve(address spender, uint256 value) public virtual override returns (bool) { - address holder = _msgSender(); - _approve(holder, spender, value); - return true; - } - - /** - * @dev See {IERC20-transferFrom}. - * - * NOTE: Does not update the allowance if the current allowance - * is the maximum `uint256`. - * - * Note that operator and allowance concepts are orthogonal: operators cannot - * call `transferFrom` (unless they have allowance), and accounts with - * allowance cannot call `operatorSend` (unless they are operators). - * - * Emits {Sent}, {IERC20-Transfer} and {IERC20-Approval} events. - */ - function transferFrom(address holder, address recipient, uint256 amount) public virtual override returns (bool) { - address spender = _msgSender(); - _spendAllowance(holder, spender, amount); - _send(holder, recipient, amount, "", "", false); - return true; - } - - /** - * @dev Creates `amount` tokens and assigns them to `account`, increasing - * the total supply. - * - * If a send hook is registered for `account`, the corresponding function - * will be called with the caller address as the `operator` and with - * `userData` and `operatorData`. - * - * See {IERC777Sender} and {IERC777Recipient}. - * - * Emits {Minted} and {IERC20-Transfer} events. - * - * Requirements - * - * - `account` cannot be the zero address. - * - if `account` is a contract, it must implement the {IERC777Recipient} - * interface. - */ - function _mint(address account, uint256 amount, bytes memory userData, bytes memory operatorData) internal virtual { - _mint(account, amount, userData, operatorData, true); - } - - /** - * @dev Creates `amount` tokens and assigns them to `account`, increasing - * the total supply. - * - * If `requireReceptionAck` is set to true, and if a send hook is - * registered for `account`, the corresponding function will be called with - * `operator`, `data` and `operatorData`. - * - * See {IERC777Sender} and {IERC777Recipient}. - * - * Emits {Minted} and {IERC20-Transfer} events. - * - * Requirements - * - * - `account` cannot be the zero address. - * - if `account` is a contract, it must implement the {IERC777Recipient} - * interface. - */ - function _mint( - address account, - uint256 amount, - bytes memory userData, - bytes memory operatorData, - bool requireReceptionAck - ) internal virtual { - require(account != address(0), "ERC777: mint to the zero address"); - - address operator = _msgSender(); - - _beforeTokenTransfer(operator, address(0), account, amount); - - // Update state variables - _totalSupply += amount; - _balances[account] += amount; - - _callTokensReceived(operator, address(0), account, amount, userData, operatorData, requireReceptionAck); - - emit Minted(operator, account, amount, userData, operatorData); - emit Transfer(address(0), account, amount); - } - - /** - * @dev Send tokens - * @param from address token holder address - * @param to address recipient address - * @param amount uint256 amount of tokens to transfer - * @param userData bytes extra information provided by the token holder (if any) - * @param operatorData bytes extra information provided by the operator (if any) - * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient - */ - function _send( - address from, - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData, - bool requireReceptionAck - ) internal virtual { - require(from != address(0), "ERC777: transfer from the zero address"); - require(to != address(0), "ERC777: transfer to the zero address"); - - address operator = _msgSender(); - - _callTokensToSend(operator, from, to, amount, userData, operatorData); - - _move(operator, from, to, amount, userData, operatorData); - - _callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck); - } - - /** - * @dev Burn tokens - * @param from address token holder address - * @param amount uint256 amount of tokens to burn - * @param data bytes extra information provided by the token holder - * @param operatorData bytes extra information provided by the operator (if any) - */ - function _burn(address from, uint256 amount, bytes memory data, bytes memory operatorData) internal virtual { - require(from != address(0), "ERC777: burn from the zero address"); - - address operator = _msgSender(); - - _callTokensToSend(operator, from, address(0), amount, data, operatorData); - - _beforeTokenTransfer(operator, from, address(0), amount); - - // Update state variables - uint256 fromBalance = _balances[from]; - require(fromBalance >= amount, "ERC777: burn amount exceeds balance"); - unchecked { - _balances[from] = fromBalance - amount; - } - _totalSupply -= amount; - - emit Burned(operator, from, amount, data, operatorData); - emit Transfer(from, address(0), amount); - } - - function _move( - address operator, - address from, - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData - ) private { - _beforeTokenTransfer(operator, from, to, amount); - - uint256 fromBalance = _balances[from]; - require(fromBalance >= amount, "ERC777: transfer amount exceeds balance"); - unchecked { - _balances[from] = fromBalance - amount; - } - _balances[to] += amount; - - emit Sent(operator, from, to, amount, userData, operatorData); - emit Transfer(from, to, amount); - } - - /** - * @dev See {ERC20-_approve}. - * - * Note that accounts cannot have allowance issued by their operators. - */ - function _approve(address holder, address spender, uint256 value) internal virtual { - require(holder != address(0), "ERC777: approve from the zero address"); - require(spender != address(0), "ERC777: approve to the zero address"); - - _allowances[holder][spender] = value; - emit Approval(holder, spender, value); - } - - /** - * @dev Call from.tokensToSend() if the interface is registered - * @param operator address operator requesting the transfer - * @param from address token holder address - * @param to address recipient address - * @param amount uint256 amount of tokens to transfer - * @param userData bytes extra information provided by the token holder (if any) - * @param operatorData bytes extra information provided by the operator (if any) - */ - function _callTokensToSend( - address operator, - address from, - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData - ) private { - address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(from, _TOKENS_SENDER_INTERFACE_HASH); - if (implementer != address(0)) { - IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData); - } - } - - /** - * @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but - * tokensReceived() was not registered for the recipient - * @param operator address operator requesting the transfer - * @param from address token holder address - * @param to address recipient address - * @param amount uint256 amount of tokens to transfer - * @param userData bytes extra information provided by the token holder (if any) - * @param operatorData bytes extra information provided by the operator (if any) - * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient - */ - function _callTokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData, - bool requireReceptionAck - ) private { - address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(to, _TOKENS_RECIPIENT_INTERFACE_HASH); - if (implementer != address(0)) { - IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData); - } else if (requireReceptionAck) { - require( - to.code.length == 0, - "ERC777: token recipient contract has no implementer for ERC777TokensRecipient" - ); - } - } - - /** - * @dev Updates `owner` s allowance for `spender` based on spent `amount`. - * - * Does not update the allowance amount in case of infinite allowance. - * Revert if not enough allowance is available. - * - * Might emit an {IERC20-Approval} event. - */ - function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { - uint256 currentAllowance = allowance(owner, spender); - if (currentAllowance != type(uint256).max) { - require(currentAllowance >= amount, "ERC777: insufficient allowance"); - unchecked { - _approve(owner, spender, currentAllowance - amount); - } - } - } - - /** - * @dev Hook that is called before any token transfer. This includes - * calls to {send}, {transfer}, {operatorSend}, {transferFrom}, minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * will be to transferred to `to`. - * - when `from` is zero, `amount` tokens will be minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens will be burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer(address operator, address from, address to, uint256 amount) internal virtual {} -} diff --git a/contracts/token/ERC777/IERC777.sol b/contracts/token/ERC777/IERC777.sol deleted file mode 100644 index d3bede62610..00000000000 --- a/contracts/token/ERC777/IERC777.sol +++ /dev/null @@ -1,200 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC777/IERC777.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC777Token standard as defined in the EIP. - * - * This contract uses the - * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let - * token holders and recipients react to token movements by using setting implementers - * for the associated interfaces in said registry. See {IERC1820Registry} and - * {ERC1820Implementer}. - */ -interface IERC777 { - /** - * @dev Emitted when `amount` tokens are created by `operator` and assigned to `to`. - * - * Note that some additional user `data` and `operatorData` can be logged in the event. - */ - event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData); - - /** - * @dev Emitted when `operator` destroys `amount` tokens from `account`. - * - * Note that some additional user `data` and `operatorData` can be logged in the event. - */ - event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData); - - /** - * @dev Emitted when `operator` is made operator for `tokenHolder`. - */ - event AuthorizedOperator(address indexed operator, address indexed tokenHolder); - - /** - * @dev Emitted when `operator` is revoked its operator status for `tokenHolder`. - */ - event RevokedOperator(address indexed operator, address indexed tokenHolder); - - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the smallest part of the token that is not divisible. This - * means all token operations (creation, movement and destruction) must have - * amounts that are a multiple of this number. - * - * For most token contracts, this value will equal 1. - */ - function granularity() external view returns (uint256); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by an account (`owner`). - */ - function balanceOf(address owner) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `recipient`. - * - * If send or receive hooks are registered for the caller and `recipient`, - * the corresponding functions will be called with `data` and empty - * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. - * - * Emits a {Sent} event. - * - * Requirements - * - * - the caller must have at least `amount` tokens. - * - `recipient` cannot be the zero address. - * - if `recipient` is a contract, it must implement the {IERC777Recipient} - * interface. - */ - function send(address recipient, uint256 amount, bytes calldata data) external; - - /** - * @dev Destroys `amount` tokens from the caller's account, reducing the - * total supply. - * - * If a send hook is registered for the caller, the corresponding function - * will be called with `data` and empty `operatorData`. See {IERC777Sender}. - * - * Emits a {Burned} event. - * - * Requirements - * - * - the caller must have at least `amount` tokens. - */ - function burn(uint256 amount, bytes calldata data) external; - - /** - * @dev Returns true if an account is an operator of `tokenHolder`. - * Operators can send and burn tokens on behalf of their owners. All - * accounts are their own operator. - * - * See {operatorSend} and {operatorBurn}. - */ - function isOperatorFor(address operator, address tokenHolder) external view returns (bool); - - /** - * @dev Make an account an operator of the caller. - * - * See {isOperatorFor}. - * - * Emits an {AuthorizedOperator} event. - * - * Requirements - * - * - `operator` cannot be calling address. - */ - function authorizeOperator(address operator) external; - - /** - * @dev Revoke an account's operator status for the caller. - * - * See {isOperatorFor} and {defaultOperators}. - * - * Emits a {RevokedOperator} event. - * - * Requirements - * - * - `operator` cannot be calling address. - */ - function revokeOperator(address operator) external; - - /** - * @dev Returns the list of default operators. These accounts are operators - * for all token holders, even if {authorizeOperator} was never called on - * them. - * - * This list is immutable, but individual holders may revoke these via - * {revokeOperator}, in which case {isOperatorFor} will return false. - */ - function defaultOperators() external view returns (address[] memory); - - /** - * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must - * be an operator of `sender`. - * - * If send or receive hooks are registered for `sender` and `recipient`, - * the corresponding functions will be called with `data` and - * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. - * - * Emits a {Sent} event. - * - * Requirements - * - * - `sender` cannot be the zero address. - * - `sender` must have at least `amount` tokens. - * - the caller must be an operator for `sender`. - * - `recipient` cannot be the zero address. - * - if `recipient` is a contract, it must implement the {IERC777Recipient} - * interface. - */ - function operatorSend( - address sender, - address recipient, - uint256 amount, - bytes calldata data, - bytes calldata operatorData - ) external; - - /** - * @dev Destroys `amount` tokens from `account`, reducing the total supply. - * The caller must be an operator of `account`. - * - * If a send hook is registered for `account`, the corresponding function - * will be called with `data` and `operatorData`. See {IERC777Sender}. - * - * Emits a {Burned} event. - * - * Requirements - * - * - `account` cannot be the zero address. - * - `account` must have at least `amount` tokens. - * - the caller must be an operator for `account`. - */ - function operatorBurn(address account, uint256 amount, bytes calldata data, bytes calldata operatorData) external; - - event Sent( - address indexed operator, - address indexed from, - address indexed to, - uint256 amount, - bytes data, - bytes operatorData - ); -} diff --git a/contracts/token/ERC777/IERC777Recipient.sol b/contracts/token/ERC777/IERC777Recipient.sol deleted file mode 100644 index 717dd8f8c2e..00000000000 --- a/contracts/token/ERC777/IERC777Recipient.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777Recipient.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. - * - * Accounts can be notified of {IERC777} tokens being sent to them by having a - * contract implement this interface (contract holders can be their own - * implementer) and registering it on the - * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. - * - * See {IERC1820Registry} and {ERC1820Implementer}. - */ -interface IERC777Recipient { - /** - * @dev Called by an {IERC777} token contract whenever tokens are being - * moved or created into a registered account (`to`). The type of operation - * is conveyed by `from` being the zero address or not. - * - * This call occurs _after_ the token contract's state is updated, so - * {IERC777-balanceOf}, etc., can be used to query the post-operation state. - * - * This function may revert to prevent the operation from being executed. - */ - function tokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external; -} diff --git a/contracts/token/ERC777/IERC777Sender.sol b/contracts/token/ERC777/IERC777Sender.sol deleted file mode 100644 index 969e3e367c9..00000000000 --- a/contracts/token/ERC777/IERC777Sender.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777Sender.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC777TokensSender standard as defined in the EIP. - * - * {IERC777} Token holders can be notified of operations performed on their - * tokens by having a contract implement this interface (contract holders can be - * their own implementer) and registering it on the - * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. - * - * See {IERC1820Registry} and {ERC1820Implementer}. - */ -interface IERC777Sender { - /** - * @dev Called by an {IERC777} token contract whenever a registered holder's - * (`from`) tokens are about to be moved or destroyed. The type of operation - * is conveyed by `to` being the zero address or not. - * - * This call occurs _before_ the token contract's state is updated, so - * {IERC777-balanceOf}, etc., can be used to query the pre-operation state. - * - * This function may revert to prevent the operation from being executed. - */ - function tokensToSend( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external; -} diff --git a/contracts/token/ERC777/README.adoc b/contracts/token/ERC777/README.adoc deleted file mode 100644 index cc096f74c90..00000000000 --- a/contracts/token/ERC777/README.adoc +++ /dev/null @@ -1,26 +0,0 @@ -= ERC 777 - -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc777 - -CAUTION: As of v4.9, OpenZeppelin's implementation of ERC-777 is deprecated and will be removed in the next major release. - -This set of interfaces and contracts are all related to the https://eips.ethereum.org/EIPS/eip-777[ERC777 token standard]. - -TIP: For an overview of ERC777 tokens and a walk through on how to create a token contract read our xref:ROOT:erc777.adoc[ERC777 guide]. - -The token behavior itself is implemented in the core contracts: {IERC777}, {ERC777}. - -Additionally there are interfaces used to develop contracts that react to token movements: {IERC777Sender}, {IERC777Recipient}. - -== Core - -{{IERC777}} - -{{ERC777}} - -== Hooks - -{{IERC777Sender}} - -{{IERC777Recipient}} diff --git a/contracts/utils/Checkpoints.sol b/contracts/utils/Checkpoints.sol index 55ff42fd124..aff54258546 100644 --- a/contracts/utils/Checkpoints.sol +++ b/contracts/utils/Checkpoints.sol @@ -17,202 +17,6 @@ import "./math/SafeCast.sol"; * _Available since v4.5._ */ library Checkpoints { - struct History { - Checkpoint[] _checkpoints; - } - - struct Checkpoint { - uint32 _blockNumber; - uint224 _value; - } - - /** - * @dev Returns the value at a given block number. If a checkpoint is not available at that block, the closest one - * before it is returned, or zero otherwise. Because the number returned corresponds to that at the end of the - * block, the requested block number must be in the past, excluding the current block. - */ - function getAtBlock(History storage self, uint256 blockNumber) internal view returns (uint256) { - require(blockNumber < block.number, "Checkpoints: block not yet mined"); - uint32 key = SafeCast.toUint32(blockNumber); - - uint256 len = self._checkpoints.length; - uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len); - return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; - } - - /** - * @dev Returns the value at a given block number. If a checkpoint is not available at that block, the closest one - * before it is returned, or zero otherwise. Similar to {upperLookup} but optimized for the case when the searched - * checkpoint is probably "recent", defined as being among the last sqrt(N) checkpoints where N is the number of - * checkpoints. - */ - function getAtProbablyRecentBlock(History storage self, uint256 blockNumber) internal view returns (uint256) { - require(blockNumber < block.number, "Checkpoints: block not yet mined"); - uint32 key = SafeCast.toUint32(blockNumber); - - uint256 len = self._checkpoints.length; - - uint256 low = 0; - uint256 high = len; - - if (len > 5) { - uint256 mid = len - Math.sqrt(len); - if (key < _unsafeAccess(self._checkpoints, mid)._blockNumber) { - high = mid; - } else { - low = mid + 1; - } - } - - uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high); - - return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; - } - - /** - * @dev Pushes a value onto a History so that it is stored as the checkpoint for the current block. - * - * Returns previous value and new value. - */ - function push(History storage self, uint256 value) internal returns (uint256, uint256) { - return _insert(self._checkpoints, SafeCast.toUint32(block.number), SafeCast.toUint224(value)); - } - - /** - * @dev Pushes a value onto a History, by updating the latest value using binary operation `op`. The new value will - * be set to `op(latest, delta)`. - * - * Returns previous value and new value. - */ - function push( - History storage self, - function(uint256, uint256) view returns (uint256) op, - uint256 delta - ) internal returns (uint256, uint256) { - return push(self, op(latest(self), delta)); - } - - /** - * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints. - */ - function latest(History storage self) internal view returns (uint224) { - uint256 pos = self._checkpoints.length; - return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; - } - - /** - * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value - * in the most recent checkpoint. - */ - function latestCheckpoint( - History storage self - ) internal view returns (bool exists, uint32 _blockNumber, uint224 _value) { - uint256 pos = self._checkpoints.length; - if (pos == 0) { - return (false, 0, 0); - } else { - Checkpoint memory ckpt = _unsafeAccess(self._checkpoints, pos - 1); - return (true, ckpt._blockNumber, ckpt._value); - } - } - - /** - * @dev Returns the number of checkpoint. - */ - function length(History storage self) internal view returns (uint256) { - return self._checkpoints.length; - } - - /** - * @dev Returns checkpoint at given position. - */ - function at(History storage self, uint32 pos) internal view returns (Checkpoint memory) { - return self._checkpoints[pos]; - } - - /** - * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint, - * or by updating the last one. - */ - function _insert(Checkpoint[] storage self, uint32 key, uint224 value) private returns (uint224, uint224) { - uint256 pos = self.length; - - if (pos > 0) { - // Copying to memory is important here. - Checkpoint memory last = _unsafeAccess(self, pos - 1); - - // Checkpoint keys must be non-decreasing. - require(last._blockNumber <= key, "Checkpoint: decreasing keys"); - - // Update or push new checkpoint - if (last._blockNumber == key) { - _unsafeAccess(self, pos - 1)._value = value; - } else { - self.push(Checkpoint({_blockNumber: key, _value: value})); - } - return (last._value, value); - } else { - self.push(Checkpoint({_blockNumber: key, _value: value})); - return (0, value); - } - } - - /** - * @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high` if there is none. - * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. - * - * WARNING: `high` should not be greater than the array's length. - */ - function _upperBinaryLookup( - Checkpoint[] storage self, - uint32 key, - uint256 low, - uint256 high - ) private view returns (uint256) { - while (low < high) { - uint256 mid = Math.average(low, high); - if (_unsafeAccess(self, mid)._blockNumber > key) { - high = mid; - } else { - low = mid + 1; - } - } - return high; - } - - /** - * @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or `high` if there is none. - * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. - * - * WARNING: `high` should not be greater than the array's length. - */ - function _lowerBinaryLookup( - Checkpoint[] storage self, - uint32 key, - uint256 low, - uint256 high - ) private view returns (uint256) { - while (low < high) { - uint256 mid = Math.average(low, high); - if (_unsafeAccess(self, mid)._blockNumber < key) { - low = mid + 1; - } else { - high = mid; - } - } - return high; - } - - /** - * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. - */ - function _unsafeAccess(Checkpoint[] storage self, uint256 pos) private pure returns (Checkpoint storage result) { - assembly { - mstore(0, self.slot) - result.slot := add(keccak256(0, 0x20), pos) - } - } - struct Trace224 { Checkpoint224[] _checkpoints; } diff --git a/contracts/utils/README.adoc b/contracts/utils/README.adoc index 994b4b956d0..07cee64270c 100644 --- a/contracts/utils/README.adoc +++ b/contracts/utils/README.adoc @@ -31,10 +31,6 @@ Finally, {Create2} contains all necessary utilities to safely use the https://bl {{SafeCast}} -{{SafeMath}} - -{{SignedSafeMath}} - == Cryptography {{ECDSA}} @@ -45,39 +41,18 @@ Finally, {Create2} contains all necessary utilities to safely use the https://bl {{EIP712}} -== Escrow - -{{ConditionalEscrow}} - -{{Escrow}} - -{{RefundEscrow}} - == Introspection This set of interfaces and contracts deal with https://en.wikipedia.org/wiki/Type_introspection[type introspection] of contracts, that is, examining which functions can be called on them. This is usually referred to as a contract's _interface_. Ethereum contracts have no native concept of an interface, so applications must usually simply trust they are not making an incorrect call. For trusted setups this is a non-issue, but often unknown and untrusted third-party addresses need to be interacted with. There may even not be any direct calls to them! (e.g. `ERC20` tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract _declaring_ its interface can be very helpful in preventing errors. -There are two main ways to approach this. - -* Locally, where a contract implements `IERC165` and declares an interface, and a second one queries it directly via `ERC165Checker`. -* Globally, where a global and unique registry (`IERC1820Registry`) is used to register implementers of a certain interface (`IERC1820Implementer`). It is then the registry that is queried, which allows for more complex setups, like contracts implementing interfaces for externally-owned accounts. - -Note that, in all cases, accounts simply _declare_ their interfaces, but they are not required to actually implement them. This mechanism can therefore be used to both prevent errors and allow for complex interactions (see `ERC777`), but it must not be relied on for security. - {{IERC165}} {{ERC165}} {{ERC165Checker}} -{{IERC1820Registry}} - -{{IERC1820Implementer}} - -{{ERC1820Implementer}} - == Data Structures {{BitMaps}} diff --git a/contracts/utils/Timers.sol b/contracts/utils/Timers.sol deleted file mode 100644 index 1c92b029b16..00000000000 --- a/contracts/utils/Timers.sol +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Timers.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Tooling for timepoints, timers and delays - * - * CAUTION: This file is deprecated as of 4.9 and will be removed in the next major release. - */ -library Timers { - struct Timestamp { - uint64 _deadline; - } - - function getDeadline(Timestamp memory timer) internal pure returns (uint64) { - return timer._deadline; - } - - function setDeadline(Timestamp storage timer, uint64 timestamp) internal { - timer._deadline = timestamp; - } - - function reset(Timestamp storage timer) internal { - timer._deadline = 0; - } - - function isUnset(Timestamp memory timer) internal pure returns (bool) { - return timer._deadline == 0; - } - - function isStarted(Timestamp memory timer) internal pure returns (bool) { - return timer._deadline > 0; - } - - function isPending(Timestamp memory timer) internal view returns (bool) { - return timer._deadline > block.timestamp; - } - - function isExpired(Timestamp memory timer) internal view returns (bool) { - return isStarted(timer) && timer._deadline <= block.timestamp; - } - - struct BlockNumber { - uint64 _deadline; - } - - function getDeadline(BlockNumber memory timer) internal pure returns (uint64) { - return timer._deadline; - } - - function setDeadline(BlockNumber storage timer, uint64 timestamp) internal { - timer._deadline = timestamp; - } - - function reset(BlockNumber storage timer) internal { - timer._deadline = 0; - } - - function isUnset(BlockNumber memory timer) internal pure returns (bool) { - return timer._deadline == 0; - } - - function isStarted(BlockNumber memory timer) internal pure returns (bool) { - return timer._deadline > 0; - } - - function isPending(BlockNumber memory timer) internal view returns (bool) { - return timer._deadline > block.number; - } - - function isExpired(BlockNumber memory timer) internal view returns (bool) { - return isStarted(timer) && timer._deadline <= block.number; - } -} diff --git a/contracts/utils/cryptography/ECDSA.sol b/contracts/utils/cryptography/ECDSA.sol index 77279eb4f18..03d2b0eef37 100644 --- a/contracts/utils/cryptography/ECDSA.sol +++ b/contracts/utils/cryptography/ECDSA.sol @@ -16,8 +16,7 @@ library ECDSA { NoError, InvalidSignature, InvalidSignatureLength, - InvalidSignatureS, - InvalidSignatureV // Deprecated in v4.8 + InvalidSignatureS } function _throwError(RecoverError error) private pure { diff --git a/contracts/utils/cryptography/draft-EIP712.sol b/contracts/utils/cryptography/draft-EIP712.sol deleted file mode 100644 index fdae3ba3e88..00000000000 --- a/contracts/utils/cryptography/draft-EIP712.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/draft-EIP712.sol) - -pragma solidity ^0.8.0; - -// EIP-712 is Final as of 2022-08-11. This file is deprecated. - -import "./EIP712.sol"; diff --git a/contracts/utils/escrow/ConditionalEscrow.sol b/contracts/utils/escrow/ConditionalEscrow.sol deleted file mode 100644 index 87f53815b1e..00000000000 --- a/contracts/utils/escrow/ConditionalEscrow.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/escrow/ConditionalEscrow.sol) - -pragma solidity ^0.8.0; - -import "./Escrow.sol"; - -/** - * @title ConditionalEscrow - * @dev Base abstract escrow to only allow withdrawal if a condition is met. - * @dev Intended usage: See {Escrow}. Same usage guidelines apply here. - */ -abstract contract ConditionalEscrow is Escrow { - /** - * @dev Returns whether an address is allowed to withdraw their funds. To be - * implemented by derived contracts. - * @param payee The destination address of the funds. - */ - function withdrawalAllowed(address payee) public view virtual returns (bool); - - function withdraw(address payable payee) public virtual override { - require(withdrawalAllowed(payee), "ConditionalEscrow: payee is not allowed to withdraw"); - super.withdraw(payee); - } -} diff --git a/contracts/utils/escrow/Escrow.sol b/contracts/utils/escrow/Escrow.sol deleted file mode 100644 index 48dd51ab784..00000000000 --- a/contracts/utils/escrow/Escrow.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/escrow/Escrow.sol) - -pragma solidity ^0.8.0; - -import "../../access/Ownable.sol"; -import "../Address.sol"; - -/** - * @title Escrow - * @dev Base escrow contract, holds funds designated for a payee until they - * withdraw them. - * - * Intended usage: This contract (and derived escrow contracts) should be a - * standalone contract, that only interacts with the contract that instantiated - * it. That way, it is guaranteed that all Ether will be handled according to - * the `Escrow` rules, and there is no need to check for payable functions or - * transfers in the inheritance tree. The contract that uses the escrow as its - * payment method should be its owner, and provide public methods redirecting - * to the escrow's deposit and withdraw. - */ -contract Escrow is Ownable { - using Address for address payable; - - event Deposited(address indexed payee, uint256 weiAmount); - event Withdrawn(address indexed payee, uint256 weiAmount); - - mapping(address => uint256) private _deposits; - - function depositsOf(address payee) public view returns (uint256) { - return _deposits[payee]; - } - - /** - * @dev Stores the sent amount as credit to be withdrawn. - * @param payee The destination address of the funds. - * - * Emits a {Deposited} event. - */ - function deposit(address payee) public payable virtual onlyOwner { - uint256 amount = msg.value; - _deposits[payee] += amount; - emit Deposited(payee, amount); - } - - /** - * @dev Withdraw accumulated balance for a payee, forwarding all gas to the - * recipient. - * - * WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities. - * Make sure you trust the recipient, or are either following the - * checks-effects-interactions pattern or using {ReentrancyGuard}. - * - * @param payee The address whose funds will be withdrawn and transferred to. - * - * Emits a {Withdrawn} event. - */ - function withdraw(address payable payee) public virtual onlyOwner { - uint256 payment = _deposits[payee]; - - _deposits[payee] = 0; - - payee.sendValue(payment); - - emit Withdrawn(payee, payment); - } -} diff --git a/contracts/utils/escrow/RefundEscrow.sol b/contracts/utils/escrow/RefundEscrow.sol deleted file mode 100644 index 0e9621fee5f..00000000000 --- a/contracts/utils/escrow/RefundEscrow.sol +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/escrow/RefundEscrow.sol) - -pragma solidity ^0.8.0; - -import "./ConditionalEscrow.sol"; - -/** - * @title RefundEscrow - * @dev Escrow that holds funds for a beneficiary, deposited from multiple - * parties. - * @dev Intended usage: See {Escrow}. Same usage guidelines apply here. - * @dev The owner account (that is, the contract that instantiates this - * contract) may deposit, close the deposit period, and allow for either - * withdrawal by the beneficiary, or refunds to the depositors. All interactions - * with `RefundEscrow` will be made through the owner contract. - */ -contract RefundEscrow is ConditionalEscrow { - using Address for address payable; - - enum State { - Active, - Refunding, - Closed - } - - event RefundsClosed(); - event RefundsEnabled(); - - State private _state; - address payable private immutable _beneficiary; - - /** - * @dev Constructor. - * @param beneficiary_ The beneficiary of the deposits. - */ - constructor(address payable beneficiary_) { - require(beneficiary_ != address(0), "RefundEscrow: beneficiary is the zero address"); - _beneficiary = beneficiary_; - _state = State.Active; - } - - /** - * @return The current state of the escrow. - */ - function state() public view virtual returns (State) { - return _state; - } - - /** - * @return The beneficiary of the escrow. - */ - function beneficiary() public view virtual returns (address payable) { - return _beneficiary; - } - - /** - * @dev Stores funds that may later be refunded. - * @param refundee The address funds will be sent to if a refund occurs. - */ - function deposit(address refundee) public payable virtual override { - require(state() == State.Active, "RefundEscrow: can only deposit while active"); - super.deposit(refundee); - } - - /** - * @dev Allows for the beneficiary to withdraw their funds, rejecting - * further deposits. - */ - function close() public virtual onlyOwner { - require(state() == State.Active, "RefundEscrow: can only close while active"); - _state = State.Closed; - emit RefundsClosed(); - } - - /** - * @dev Allows for refunds to take place, rejecting further deposits. - */ - function enableRefunds() public virtual onlyOwner { - require(state() == State.Active, "RefundEscrow: can only enable refunds while active"); - _state = State.Refunding; - emit RefundsEnabled(); - } - - /** - * @dev Withdraws the beneficiary's funds. - */ - function beneficiaryWithdraw() public virtual { - require(state() == State.Closed, "RefundEscrow: beneficiary can only withdraw while closed"); - beneficiary().sendValue(address(this).balance); - } - - /** - * @dev Returns whether refundees can withdraw their deposits (be refunded). The overridden function receives a - * 'payee' argument, but we ignore it here since the condition is global, not per-payee. - */ - function withdrawalAllowed(address) public view override returns (bool) { - return state() == State.Refunding; - } -} diff --git a/contracts/utils/introspection/ERC1820Implementer.sol b/contracts/utils/introspection/ERC1820Implementer.sol deleted file mode 100644 index cf4b50498d4..00000000000 --- a/contracts/utils/introspection/ERC1820Implementer.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC1820Implementer.sol) - -pragma solidity ^0.8.0; - -import "./IERC1820Implementer.sol"; - -/** - * @dev Implementation of the {IERC1820Implementer} interface. - * - * Contracts may inherit from this and call {_registerInterfaceForAddress} to - * declare their willingness to be implementers. - * {IERC1820Registry-setInterfaceImplementer} should then be called for the - * registration to be complete. - * - * CAUTION: This file is deprecated as of v4.9 and will be removed in the next major release. - */ -contract ERC1820Implementer is IERC1820Implementer { - bytes32 private constant _ERC1820_ACCEPT_MAGIC = keccak256("ERC1820_ACCEPT_MAGIC"); - - mapping(bytes32 => mapping(address => bool)) private _supportedInterfaces; - - /** - * @dev See {IERC1820Implementer-canImplementInterfaceForAddress}. - */ - function canImplementInterfaceForAddress( - bytes32 interfaceHash, - address account - ) public view virtual override returns (bytes32) { - return _supportedInterfaces[interfaceHash][account] ? _ERC1820_ACCEPT_MAGIC : bytes32(0x00); - } - - /** - * @dev Declares the contract as willing to be an implementer of - * `interfaceHash` for `account`. - * - * See {IERC1820Registry-setInterfaceImplementer} and - * {IERC1820Registry-interfaceHash}. - */ - function _registerInterfaceForAddress(bytes32 interfaceHash, address account) internal virtual { - _supportedInterfaces[interfaceHash][account] = true; - } -} diff --git a/contracts/utils/introspection/IERC1820Implementer.sol b/contracts/utils/introspection/IERC1820Implementer.sol deleted file mode 100644 index c4d0b30289a..00000000000 --- a/contracts/utils/introspection/IERC1820Implementer.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC1820Implementer.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface for an ERC1820 implementer, as defined in the - * https://eips.ethereum.org/EIPS/eip-1820#interface-implementation-erc1820implementerinterface[EIP]. - * Used by contracts that will be registered as implementers in the - * {IERC1820Registry}. - */ -interface IERC1820Implementer { - /** - * @dev Returns a special value (`ERC1820_ACCEPT_MAGIC`) if this contract - * implements `interfaceHash` for `account`. - * - * See {IERC1820Registry-setInterfaceImplementer}. - */ - function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) external view returns (bytes32); -} diff --git a/contracts/utils/introspection/IERC1820Registry.sol b/contracts/utils/introspection/IERC1820Registry.sol deleted file mode 100644 index a146bc2a68b..00000000000 --- a/contracts/utils/introspection/IERC1820Registry.sol +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/introspection/IERC1820Registry.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the global ERC1820 Registry, as defined in the - * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register - * implementers for interfaces in this registry, as well as query support. - * - * Implementers may be shared by multiple accounts, and can also implement more - * than a single interface for each account. Contracts can implement interfaces - * for themselves, but externally-owned accounts (EOA) must delegate this to a - * contract. - * - * {IERC165} interfaces can also be queried via the registry. - * - * For an in-depth explanation and source code analysis, see the EIP text. - */ -interface IERC1820Registry { - event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer); - - event ManagerChanged(address indexed account, address indexed newManager); - - /** - * @dev Sets `newManager` as the manager for `account`. A manager of an - * account is able to set interface implementers for it. - * - * By default, each account is its own manager. Passing a value of `0x0` in - * `newManager` will reset the manager to this initial state. - * - * Emits a {ManagerChanged} event. - * - * Requirements: - * - * - the caller must be the current manager for `account`. - */ - function setManager(address account, address newManager) external; - - /** - * @dev Returns the manager for `account`. - * - * See {setManager}. - */ - function getManager(address account) external view returns (address); - - /** - * @dev Sets the `implementer` contract as ``account``'s implementer for - * `interfaceHash`. - * - * `account` being the zero address is an alias for the caller's address. - * The zero address can also be used in `implementer` to remove an old one. - * - * See {interfaceHash} to learn how these are created. - * - * Emits an {InterfaceImplementerSet} event. - * - * Requirements: - * - * - the caller must be the current manager for `account`. - * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not - * end in 28 zeroes). - * - `implementer` must implement {IERC1820Implementer} and return true when - * queried for support, unless `implementer` is the caller. See - * {IERC1820Implementer-canImplementInterfaceForAddress}. - */ - function setInterfaceImplementer(address account, bytes32 _interfaceHash, address implementer) external; - - /** - * @dev Returns the implementer of `interfaceHash` for `account`. If no such - * implementer is registered, returns the zero address. - * - * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28 - * zeroes), `account` will be queried for support of it. - * - * `account` being the zero address is an alias for the caller's address. - */ - function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address); - - /** - * @dev Returns the interface hash for an `interfaceName`, as defined in the - * corresponding - * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP]. - */ - function interfaceHash(string calldata interfaceName) external pure returns (bytes32); - - /** - * @notice Updates the cache with whether the contract implements an ERC165 interface or not. - * @param account Address of the contract for which to update the cache. - * @param interfaceId ERC165 interface for which to update the cache. - */ - function updateERC165Cache(address account, bytes4 interfaceId) external; - - /** - * @notice Checks whether a contract implements an ERC165 interface or not. - * If the result is not cached a direct lookup on the contract address is performed. - * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling - * {updateERC165Cache} with the contract address. - * @param account Address of the contract to check. - * @param interfaceId ERC165 interface to check. - * @return True if `account` implements `interfaceId`, false otherwise. - */ - function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool); - - /** - * @notice Checks whether a contract implements an ERC165 interface or not without using or updating the cache. - * @param account Address of the contract to check. - * @param interfaceId ERC165 interface to check. - * @return True if `account` implements `interfaceId`, false otherwise. - */ - function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool); -} diff --git a/contracts/utils/math/Math.sol b/contracts/utils/math/Math.sol index f8e7ca0a95c..430d43a6da5 100644 --- a/contracts/utils/math/Math.sol +++ b/contracts/utils/math/Math.sol @@ -13,6 +13,72 @@ library Math { Zero // Toward zero } + /** + * @dev Returns the addition of two unsigned integers, with an overflow flag. + * + * _Available since v5.0._ + */ + function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + uint256 c = a + b; + if (c < a) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the subtraction of two unsigned integers, with an overflow flag. + * + * _Available since v5.0._ + */ + function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b > a) return (false, 0); + return (true, a - b); + } + } + + /** + * @dev Returns the multiplication of two unsigned integers, with an overflow flag. + * + * _Available since v5.0._ + */ + function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) return (true, 0); + uint256 c = a * b; + if (c / a != b) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the division of two unsigned integers, with a division by zero flag. + * + * _Available since v5.0._ + */ + function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b == 0) return (false, 0); + return (true, a / b); + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. + * + * _Available since v5.0._ + */ + function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b == 0) return (false, 0); + return (true, a % b); + } + } + /** * @dev Returns the largest of two numbers. */ diff --git a/contracts/utils/math/SafeCast.sol b/contracts/utils/math/SafeCast.sol index 435a5f9450b..0744b37119b 100644 --- a/contracts/utils/math/SafeCast.sol +++ b/contracts/utils/math/SafeCast.sol @@ -15,9 +15,6 @@ pragma solidity ^0.8.0; * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. - * - * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing - * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** diff --git a/contracts/utils/math/SafeMath.sol b/contracts/utils/math/SafeMath.sol deleted file mode 100644 index 2f48fb7360a..00000000000 --- a/contracts/utils/math/SafeMath.sol +++ /dev/null @@ -1,215 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) - -pragma solidity ^0.8.0; - -// CAUTION -// This version of SafeMath should only be used with Solidity 0.8 or later, -// because it relies on the compiler's built in overflow checks. - -/** - * @dev Wrappers over Solidity's arithmetic operations. - * - * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler - * now has built in overflow checking. - */ -library SafeMath { - /** - * @dev Returns the addition of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - uint256 c = a + b; - if (c < a) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the subtraction of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b > a) return (false, 0); - return (true, a - b); - } - } - - /** - * @dev Returns the multiplication of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) return (true, 0); - uint256 c = a * b; - if (c / a != b) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the division of two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a / b); - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a % b); - } - } - - /** - * @dev Returns the addition of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - * - Addition cannot overflow. - */ - function add(uint256 a, uint256 b) internal pure returns (uint256) { - return a + b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - return a - b; - } - - /** - * @dev Returns the multiplication of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - * - Multiplication cannot overflow. - */ - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - return a * b; - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b) internal pure returns (uint256) { - return a / b; - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b) internal pure returns (uint256) { - return a % b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting with custom message on - * overflow (when the result is negative). - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {trySub}. - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { - unchecked { - require(b <= a, errorMessage); - return a - b; - } - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting with custom message on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a / b; - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting with custom message when dividing by zero. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryMod}. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a % b; - } - } -} diff --git a/contracts/utils/math/SignedSafeMath.sol b/contracts/utils/math/SignedSafeMath.sol deleted file mode 100644 index 6704d4ce265..00000000000 --- a/contracts/utils/math/SignedSafeMath.sol +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/math/SignedSafeMath.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Wrappers over Solidity's arithmetic operations. - * - * NOTE: `SignedSafeMath` is no longer needed starting with Solidity 0.8. The compiler - * now has built in overflow checking. - */ -library SignedSafeMath { - /** - * @dev Returns the multiplication of two signed integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - * - Multiplication cannot overflow. - */ - function mul(int256 a, int256 b) internal pure returns (int256) { - return a * b; - } - - /** - * @dev Returns the integer division of two signed integers. Reverts on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(int256 a, int256 b) internal pure returns (int256) { - return a / b; - } - - /** - * @dev Returns the subtraction of two signed integers, reverting on - * overflow. - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(int256 a, int256 b) internal pure returns (int256) { - return a - b; - } - - /** - * @dev Returns the addition of two signed integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - * - Addition cannot overflow. - */ - function add(int256 a, int256 b) internal pure returns (int256) { - return a + b; - } -} diff --git a/contracts/utils/structs/EnumerableMap.sol b/contracts/utils/structs/EnumerableMap.sol index fb21f02cfdf..0320ee9a006 100644 --- a/contracts/utils/structs/EnumerableMap.sol +++ b/contracts/utils/structs/EnumerableMap.sol @@ -140,22 +140,6 @@ library EnumerableMap { return value; } - /** - * @dev Same as {get}, with a custom error message when `key` is not in the map. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryGet}. - */ - function get( - Bytes32ToBytes32Map storage map, - bytes32 key, - string memory errorMessage - ) internal view returns (bytes32) { - bytes32 value = map._values[key]; - require(value != 0 || contains(map, key), errorMessage); - return value; - } - /** * @dev Return the an array containing all the keys * @@ -242,16 +226,6 @@ library EnumerableMap { return uint256(get(map._inner, bytes32(key))); } - /** - * @dev Same as {get}, with a custom error message when `key` is not in the map. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryGet}. - */ - function get(UintToUintMap storage map, uint256 key, string memory errorMessage) internal view returns (uint256) { - return uint256(get(map._inner, bytes32(key), errorMessage)); - } - /** * @dev Return the an array containing all the keys * @@ -346,20 +320,6 @@ library EnumerableMap { return address(uint160(uint256(get(map._inner, bytes32(key))))); } - /** - * @dev Same as {get}, with a custom error message when `key` is not in the map. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryGet}. - */ - function get( - UintToAddressMap storage map, - uint256 key, - string memory errorMessage - ) internal view returns (address) { - return address(uint160(uint256(get(map._inner, bytes32(key), errorMessage)))); - } - /** * @dev Return the an array containing all the keys * @@ -454,20 +414,6 @@ library EnumerableMap { return uint256(get(map._inner, bytes32(uint256(uint160(key))))); } - /** - * @dev Same as {get}, with a custom error message when `key` is not in the map. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryGet}. - */ - function get( - AddressToUintMap storage map, - address key, - string memory errorMessage - ) internal view returns (uint256) { - return uint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage)); - } - /** * @dev Return the an array containing all the keys * @@ -562,20 +508,6 @@ library EnumerableMap { return uint256(get(map._inner, key)); } - /** - * @dev Same as {get}, with a custom error message when `key` is not in the map. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryGet}. - */ - function get( - Bytes32ToUintMap storage map, - bytes32 key, - string memory errorMessage - ) internal view returns (uint256) { - return uint256(get(map._inner, key, errorMessage)); - } - /** * @dev Return the an array containing all the keys * diff --git a/contracts/vendor/amb/IAMB.sol b/contracts/vendor/amb/IAMB.sol deleted file mode 100644 index 73a2bd24bcb..00000000000 --- a/contracts/vendor/amb/IAMB.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (vendor/amb/IAMB.sol) -pragma solidity ^0.8.0; - -interface IAMB { - event UserRequestForAffirmation(bytes32 indexed messageId, bytes encodedData); - event UserRequestForSignature(bytes32 indexed messageId, bytes encodedData); - event AffirmationCompleted( - address indexed sender, - address indexed executor, - bytes32 indexed messageId, - bool status - ); - event RelayedMessage(address indexed sender, address indexed executor, bytes32 indexed messageId, bool status); - - function messageSender() external view returns (address); - - function maxGasPerTx() external view returns (uint256); - - function transactionHash() external view returns (bytes32); - - function messageId() external view returns (bytes32); - - function messageSourceChainId() external view returns (bytes32); - - function messageCallStatus(bytes32 _messageId) external view returns (bool); - - function failedMessageDataHash(bytes32 _messageId) external view returns (bytes32); - - function failedMessageReceiver(bytes32 _messageId) external view returns (address); - - function failedMessageSender(bytes32 _messageId) external view returns (address); - - function requireToPassMessage(address _contract, bytes calldata _data, uint256 _gas) external returns (bytes32); - - function requireToConfirmMessage(address _contract, bytes calldata _data, uint256 _gas) external returns (bytes32); - - function sourceChainId() external view returns (uint256); - - function destinationChainId() external view returns (uint256); -} diff --git a/contracts/vendor/arbitrum/IArbSys.sol b/contracts/vendor/arbitrum/IArbSys.sol deleted file mode 100644 index 9b79d5c16a2..00000000000 --- a/contracts/vendor/arbitrum/IArbSys.sol +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE -// SPDX-License-Identifier: BUSL-1.1 -// OpenZeppelin Contracts (last updated v4.8.0) (vendor/arbitrum/IArbSys.sol) - -pragma solidity >=0.4.21 <0.9.0; - -/** - * @title System level functionality - * @notice For use by contracts to interact with core L2-specific functionality. - * Precompiled contract that exists in every Arbitrum chain at address(100), 0x0000000000000000000000000000000000000064. - */ -interface IArbSys { - /** - * @notice Get Arbitrum block number (distinct from L1 block number; Arbitrum genesis block has block number 0) - * @return block number as int - */ - function arbBlockNumber() external view returns (uint256); - - /** - * @notice Get Arbitrum block hash (reverts unless currentBlockNum-256 <= arbBlockNum < currentBlockNum) - * @return block hash - */ - function arbBlockHash(uint256 arbBlockNum) external view returns (bytes32); - - /** - * @notice Gets the rollup's unique chain identifier - * @return Chain identifier as int - */ - function arbChainID() external view returns (uint256); - - /** - * @notice Get internal version number identifying an ArbOS build - * @return version number as int - */ - function arbOSVersion() external view returns (uint256); - - /** - * @notice Returns 0 since Nitro has no concept of storage gas - * @return uint 0 - */ - function getStorageGasAvailable() external view returns (uint256); - - /** - * @notice (deprecated) check if current call is top level (meaning it was triggered by an EoA or a L1 contract) - * @dev this call has been deprecated and may be removed in a future release - * @return true if current execution frame is not a call by another L2 contract - */ - function isTopLevelCall() external view returns (bool); - - /** - * @notice map L1 sender contract address to its L2 alias - * @param sender sender address - * @param unused argument no longer used - * @return aliased sender address - */ - function mapL1SenderContractAddressToL2Alias(address sender, address unused) external pure returns (address); - - /** - * @notice check if the caller (of this caller of this) is an aliased L1 contract address - * @return true iff the caller's address is an alias for an L1 contract address - */ - function wasMyCallersAddressAliased() external view returns (bool); - - /** - * @notice return the address of the caller (of this caller of this), without applying L1 contract address aliasing - * @return address of the caller's caller, without applying L1 contract address aliasing - */ - function myCallersAddressWithoutAliasing() external view returns (address); - - /** - * @notice Send given amount of Eth to dest from sender. - * This is a convenience function, which is equivalent to calling sendTxToL1 with empty data. - * @param destination recipient address on L1 - * @return unique identifier for this L2-to-L1 transaction. - */ - function withdrawEth(address destination) external payable returns (uint256); - - /** - * @notice Send a transaction to L1 - * @dev it is not possible to execute on the L1 any L2-to-L1 transaction which contains data - * to a contract address without any code (as enforced by the Bridge contract). - * @param destination recipient address on L1 - * @param data (optional) calldata for L1 contract call - * @return a unique identifier for this L2-to-L1 transaction. - */ - function sendTxToL1(address destination, bytes calldata data) external payable returns (uint256); - - /** - * @notice Get send Merkle tree state - * @return size number of sends in the history - * @return root root hash of the send history - * @return partials hashes of partial subtrees in the send history tree - */ - function sendMerkleTreeState() external view returns (uint256 size, bytes32 root, bytes32[] memory partials); - - /** - * @notice creates a send txn from L2 to L1 - * @param position = (level << 192) + leaf = (0 << 192) + leaf = leaf - */ - event L2ToL1Tx( - address caller, - address indexed destination, - uint256 indexed hash, - uint256 indexed position, - uint256 arbBlockNum, - uint256 ethBlockNum, - uint256 timestamp, - uint256 callvalue, - bytes data - ); - - /// @dev DEPRECATED in favour of the new L2ToL1Tx event above after the nitro upgrade - event L2ToL1Transaction( - address caller, - address indexed destination, - uint256 indexed uniqueId, - uint256 indexed batchNumber, - uint256 indexInBatch, - uint256 arbBlockNum, - uint256 ethBlockNum, - uint256 timestamp, - uint256 callvalue, - bytes data - ); - - /** - * @notice logs a merkle branch for proof synthesis - * @param reserved an index meant only to align the 4th index with L2ToL1Transaction's 4th event - * @param hash the merkle hash - * @param position = (level << 192) + leaf - */ - event SendMerkleUpdate(uint256 indexed reserved, bytes32 indexed hash, uint256 indexed position); -} diff --git a/contracts/vendor/arbitrum/IBridge.sol b/contracts/vendor/arbitrum/IBridge.sol deleted file mode 100644 index e71bedce012..00000000000 --- a/contracts/vendor/arbitrum/IBridge.sol +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// SPDX-License-Identifier: BUSL-1.1 -// OpenZeppelin Contracts (last updated v4.8.0) (vendor/arbitrum/IBridge.sol) - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - -interface IBridge { - event MessageDelivered( - uint256 indexed messageIndex, - bytes32 indexed beforeInboxAcc, - address inbox, - uint8 kind, - address sender, - bytes32 messageDataHash, - uint256 baseFeeL1, - uint64 timestamp - ); - - event BridgeCallTriggered(address indexed outbox, address indexed to, uint256 value, bytes data); - - event InboxToggle(address indexed inbox, bool enabled); - - event OutboxToggle(address indexed outbox, bool enabled); - - event SequencerInboxUpdated(address newSequencerInbox); - - function allowedDelayedInboxList(uint256) external returns (address); - - function allowedOutboxList(uint256) external returns (address); - - /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. - function delayedInboxAccs(uint256) external view returns (bytes32); - - /// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. - function sequencerInboxAccs(uint256) external view returns (bytes32); - - // OpenZeppelin: changed return type from IOwnable - function rollup() external view returns (address); - - function sequencerInbox() external view returns (address); - - function activeOutbox() external view returns (address); - - function allowedDelayedInboxes(address inbox) external view returns (bool); - - function allowedOutboxes(address outbox) external view returns (bool); - - function sequencerReportedSubMessageCount() external view returns (uint256); - - /** - * @dev Enqueue a message in the delayed inbox accumulator. - * These messages are later sequenced in the SequencerInbox, either - * by the sequencer as part of a normal batch, or by force inclusion. - */ - function enqueueDelayedMessage( - uint8 kind, - address sender, - bytes32 messageDataHash - ) external payable returns (uint256); - - function executeCall( - address to, - uint256 value, - bytes calldata data - ) external returns (bool success, bytes memory returnData); - - function delayedMessageCount() external view returns (uint256); - - function sequencerMessageCount() external view returns (uint256); - - // ---------- onlySequencerInbox functions ---------- - - function enqueueSequencerMessage( - bytes32 dataHash, - uint256 afterDelayedMessagesRead, - uint256 prevMessageCount, - uint256 newMessageCount - ) external returns (uint256 seqMessageIndex, bytes32 beforeAcc, bytes32 delayedAcc, bytes32 acc); - - /** - * @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type - * This is done through a separate function entrypoint instead of allowing the sequencer inbox - * to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either - * every delayed inbox or every sequencer inbox call. - */ - function submitBatchSpendingReport(address batchPoster, bytes32 dataHash) external returns (uint256 msgNum); - - // ---------- onlyRollupOrOwner functions ---------- - - function setSequencerInbox(address _sequencerInbox) external; - - function setDelayedInbox(address inbox, bool enabled) external; - - function setOutbox(address inbox, bool enabled) external; - - // ---------- initializer ---------- - - // OpenZeppelin: changed rollup_ type from IOwnable - function initialize(address rollup_) external; -} diff --git a/contracts/vendor/arbitrum/IDelayedMessageProvider.sol b/contracts/vendor/arbitrum/IDelayedMessageProvider.sol deleted file mode 100644 index 914c25fb753..00000000000 --- a/contracts/vendor/arbitrum/IDelayedMessageProvider.sol +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// SPDX-License-Identifier: BUSL-1.1 -// OpenZeppelin Contracts (last updated v4.8.0) (vendor/arbitrum/IDelayedMessageProvider.sol) - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - -interface IDelayedMessageProvider { - /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator - event InboxMessageDelivered(uint256 indexed messageNum, bytes data); - - /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator - /// same as InboxMessageDelivered but the batch data is available in tx.input - event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum); -} diff --git a/contracts/vendor/arbitrum/IInbox.sol b/contracts/vendor/arbitrum/IInbox.sol deleted file mode 100644 index a8b67511c4f..00000000000 --- a/contracts/vendor/arbitrum/IInbox.sol +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// SPDX-License-Identifier: BUSL-1.1 -// OpenZeppelin Contracts (last updated v4.8.0) (vendor/arbitrum/IInbox.sol) - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - -import "./IBridge.sol"; -import "./IDelayedMessageProvider.sol"; - -interface IInbox is IDelayedMessageProvider { - function bridge() external view returns (IBridge); - - // OpenZeppelin: changed return type from ISequencerInbox - function sequencerInbox() external view returns (address); - - /** - * @notice Send a generic L2 message to the chain - * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input - * @param messageData Data of the message being sent - */ - function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256); - - /** - * @notice Send a generic L2 message to the chain - * @dev This method can be used to send any type of message that doesn't require L1 validation - * @param messageData Data of the message being sent - */ - function sendL2Message(bytes calldata messageData) external returns (uint256); - - function sendL1FundedUnsignedTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - bytes calldata data - ) external payable returns (uint256); - - function sendL1FundedContractTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - address to, - bytes calldata data - ) external payable returns (uint256); - - function sendUnsignedTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - uint256 nonce, - address to, - uint256 value, - bytes calldata data - ) external returns (uint256); - - function sendContractTransaction( - uint256 gasLimit, - uint256 maxFeePerGas, - address to, - uint256 value, - bytes calldata data - ) external returns (uint256); - - /** - * @notice Get the L1 fee for submitting a retryable - * @dev This fee can be paid by funds already in the L2 aliased address or by the current message value - * @dev This formula may change in the future, to future proof your code query this method instead of inlining!! - * @param dataLength The length of the retryable's calldata, in bytes - * @param baseFee The block basefee when the retryable is included in the chain, if 0 current block.basefee will be used - */ - function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee) external view returns (uint256); - - /** - * @notice Deposit eth from L1 to L2 to address of the sender if sender is an EOA, and to its aliased address if the sender is a contract - * @dev This does not trigger the fallback function when receiving in the L2 side. - * Look into retryable tickets if you are interested in this functionality. - * @dev This function should not be called inside contract constructors - */ - function depositEth() external payable returns (uint256); - - /** - * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts - * @dev all msg.value will deposited to callValueRefundAddress on L2 - * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error - * @param to destination L2 contract address - * @param l2CallValue call value for retryable L2 message - * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee - * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance - * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled - * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param data ABI encoded data of L2 message - * @return unique message number of the retryable transaction - */ - function createRetryableTicket( - address to, - uint256 l2CallValue, - uint256 maxSubmissionCost, - address excessFeeRefundAddress, - address callValueRefundAddress, - uint256 gasLimit, - uint256 maxFeePerGas, - bytes calldata data - ) external payable returns (uint256); - - /** - * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts - * @dev Same as createRetryableTicket, but does not guarantee that submission will succeed by requiring the needed funds - * come from the deposit alone, rather than falling back on the user's L2 balance - * @dev Advanced usage only (does not rewrite aliases for excessFeeRefundAddress and callValueRefundAddress). - * createRetryableTicket method is the recommended standard. - * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error - * @param to destination L2 contract address - * @param l2CallValue call value for retryable L2 message - * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee - * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance - * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled - * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) - * @param data ABI encoded data of L2 message - * @return unique message number of the retryable transaction - */ - function unsafeCreateRetryableTicket( - address to, - uint256 l2CallValue, - uint256 maxSubmissionCost, - address excessFeeRefundAddress, - address callValueRefundAddress, - uint256 gasLimit, - uint256 maxFeePerGas, - bytes calldata data - ) external payable returns (uint256); - - // ---------- onlyRollupOrOwner functions ---------- - - /// @notice pauses all inbox functionality - function pause() external; - - /// @notice unpauses all inbox functionality - function unpause() external; - - // ---------- initializer ---------- - - /** - * @dev function to be called one time during the inbox upgrade process - * this is used to fix the storage slots - */ - function postUpgradeInit(IBridge _bridge) external; - - // OpenZeppelin: changed _sequencerInbox type from ISequencerInbox - function initialize(IBridge _bridge, address _sequencerInbox) external; -} diff --git a/contracts/vendor/arbitrum/IOutbox.sol b/contracts/vendor/arbitrum/IOutbox.sol deleted file mode 100644 index 22fa58f405a..00000000000 --- a/contracts/vendor/arbitrum/IOutbox.sol +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE -// SPDX-License-Identifier: BUSL-1.1 -// OpenZeppelin Contracts (last updated v4.8.0) (vendor/arbitrum/IOutbox.sol) - -// solhint-disable-next-line compiler-version -pragma solidity >=0.6.9 <0.9.0; - -import "./IBridge.sol"; - -interface IOutbox { - event SendRootUpdated(bytes32 indexed blockHash, bytes32 indexed outputRoot); - event OutBoxTransactionExecuted( - address indexed to, - address indexed l2Sender, - uint256 indexed zero, - uint256 transactionIndex - ); - - function rollup() external view returns (address); // the rollup contract - - function bridge() external view returns (IBridge); // the bridge contract - - function spent(uint256) external view returns (bytes32); // packed spent bitmap - - function roots(bytes32) external view returns (bytes32); // maps root hashes => L2 block hash - - // solhint-disable-next-line func-name-mixedcase - function OUTBOX_VERSION() external view returns (uint128); // the outbox version - - function updateSendRoot(bytes32 sendRoot, bytes32 l2BlockHash) external; - - /// @notice When l2ToL1Sender returns a nonzero address, the message was originated by an L2 account - /// When the return value is zero, that means this is a system message - /// @dev the l2ToL1Sender behaves as the tx.origin, the msg.sender should be validated to protect against reentrancies - function l2ToL1Sender() external view returns (address); - - /// @return l2Block return L2 block when the L2 tx was initiated or 0 if no L2 to L1 transaction is active - function l2ToL1Block() external view returns (uint256); - - /// @return l1Block return L1 block when the L2 tx was initiated or 0 if no L2 to L1 transaction is active - function l2ToL1EthBlock() external view returns (uint256); - - /// @return timestamp return L2 timestamp when the L2 tx was initiated or 0 if no L2 to L1 transaction is active - function l2ToL1Timestamp() external view returns (uint256); - - /// @return outputId returns the unique output identifier of the L2 to L1 tx or 0 if no L2 to L1 transaction is active - function l2ToL1OutputId() external view returns (bytes32); - - /** - * @notice Executes a messages in an Outbox entry. - * @dev Reverts if dispute period hasn't expired, since the outbox entry - * is only created once the rollup confirms the respective assertion. - * @dev it is not possible to execute any L2-to-L1 transaction which contains data - * to a contract address without any code (as enforced by the Bridge contract). - * @param proof Merkle proof of message inclusion in send root - * @param index Merkle path to message - * @param l2Sender sender if original message (i.e., caller of ArbSys.sendTxToL1) - * @param to destination address for L1 contract call - * @param l2Block l2 block number at which sendTxToL1 call was made - * @param l1Block l1 block number at which sendTxToL1 call was made - * @param l2Timestamp l2 Timestamp at which sendTxToL1 call was made - * @param value wei in L1 message - * @param data abi-encoded L1 message data - */ - function executeTransaction( - bytes32[] calldata proof, - uint256 index, - address l2Sender, - address to, - uint256 l2Block, - uint256 l1Block, - uint256 l2Timestamp, - uint256 value, - bytes calldata data - ) external; - - /** - * @dev function used to simulate the result of a particular function call from the outbox - * it is useful for things such as gas estimates. This function includes all costs except for - * proof validation (which can be considered offchain as a somewhat of a fixed cost - it's - * not really a fixed cost, but can be treated as so with a fixed overhead for gas estimation). - * We can't include the cost of proof validation since this is intended to be used to simulate txs - * that are included in yet-to-be confirmed merkle roots. The simulation entrypoint could instead pretend - * to confirm a pending merkle root, but that would be less practical for integrating with tooling. - * It is only possible to trigger it when the msg sender is address zero, which should be impossible - * unless under simulation in an eth_call or eth_estimateGas - */ - function executeTransactionSimulation( - uint256 index, - address l2Sender, - address to, - uint256 l2Block, - uint256 l1Block, - uint256 l2Timestamp, - uint256 value, - bytes calldata data - ) external; - - /** - * @param index Merkle path to message - * @return true if the message has been spent - */ - function isSpent(uint256 index) external view returns (bool); - - function calculateItemHash( - address l2Sender, - address to, - uint256 l2Block, - uint256 l1Block, - uint256 l2Timestamp, - uint256 value, - bytes calldata data - ) external pure returns (bytes32); - - function calculateMerkleRoot(bytes32[] memory proof, uint256 path, bytes32 item) external pure returns (bytes32); -} diff --git a/contracts/vendor/optimism/ICrossDomainMessenger.sol b/contracts/vendor/optimism/ICrossDomainMessenger.sol deleted file mode 100644 index cc01a48ab9a..00000000000 --- a/contracts/vendor/optimism/ICrossDomainMessenger.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (vendor/optimism/ICrossDomainMessenger.sol) -pragma solidity >0.5.0 <0.9.0; - -/** - * @title ICrossDomainMessenger - */ -interface ICrossDomainMessenger { - /********** - * Events * - **********/ - - event SentMessage(address indexed target, address sender, bytes message, uint256 messageNonce, uint256 gasLimit); - event RelayedMessage(bytes32 indexed msgHash); - event FailedRelayedMessage(bytes32 indexed msgHash); - - /************* - * Variables * - *************/ - - function xDomainMessageSender() external view returns (address); - - /******************** - * Public Functions * - ********************/ - - /** - * Sends a cross domain message to the target messenger. - * @param _target Target contract address. - * @param _message Message to send to the target. - * @param _gasLimit Gas limit for the provided message. - */ - function sendMessage(address _target, bytes calldata _message, uint32 _gasLimit) external; -} diff --git a/contracts/vendor/optimism/LICENSE b/contracts/vendor/optimism/LICENSE deleted file mode 100644 index 6a7da5218bb..00000000000 --- a/contracts/vendor/optimism/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -(The MIT License) - -Copyright 2020-2021 Optimism - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/contracts/vendor/polygon/IFxMessageProcessor.sol b/contracts/vendor/polygon/IFxMessageProcessor.sol deleted file mode 100644 index be73e6f53cd..00000000000 --- a/contracts/vendor/polygon/IFxMessageProcessor.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (vendor/polygon/IFxMessageProcessor.sol) -pragma solidity ^0.8.0; - -interface IFxMessageProcessor { - function processMessageFromRoot(uint256 stateId, address rootMessageSender, bytes calldata data) external; -} diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 071045e683a..fc38e895335 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -11,14 +11,11 @@ ** xref:erc20.adoc[ERC20] *** xref:erc20-supply.adoc[Creating Supply] ** xref:erc721.adoc[ERC721] -** xref:erc777.adoc[ERC777] ** xref:erc1155.adoc[ERC1155] ** xref:erc4626.adoc[ERC4626] * xref:governance.adoc[Governance] -* xref:crosschain.adoc[Crosschain] - * xref:utilities.adoc[Utilities] * xref:subgraphs::index.adoc[Subgraphs] diff --git a/docs/modules/ROOT/pages/crosschain.adoc b/docs/modules/ROOT/pages/crosschain.adoc deleted file mode 100644 index cbe24df774b..00000000000 --- a/docs/modules/ROOT/pages/crosschain.adoc +++ /dev/null @@ -1,210 +0,0 @@ -= Adding cross-chain support to contracts - -If your contract is targeting to be used in the context of multichain operations, you may need specific tools to identify and process these cross-chain operations. - -OpenZeppelin provides the xref:api:crosschain.adoc#CrossChainEnabled[`CrossChainEnabled`] abstract contract, that includes dedicated internal functions. - -In this guide, we will go through an example use case: _how to build an upgradeable & mintable ERC20 token controlled by a governor present on a foreign chain_. - -== Starting point, our ERC20 contract - -Let's start with a small ERC20 contract, that we bootstrapped using the https://wizard.openzeppelin.com/[OpenZeppelin Contracts Wizard], and extended with an owner that has the ability to mint. Note that for demonstration purposes we have not used the built-in `Ownable` contract. - -[source,solidity] ----- -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; - -contract MyToken is Initializable, ERC20Upgradeable, UUPSUpgradeable { - address public owner; - - modifier onlyOwner() { - require(owner == _msgSender(), "Not authorized"); - _; - } - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() initializer {} - - function initialize(address initialOwner) initializer public { - __ERC20_init("MyToken", "MTK"); - __UUPSUpgradeable_init(); - - owner = initialOwner; - } - - function mint(address to, uint256 amount) public onlyOwner { - _mint(to, amount); - } - - function _authorizeUpgrade(address newImplementation) internal override onlyOwner { - } -} ----- - -This token is mintable and upgradeable by the owner of the contract. - -== Preparing our contract for cross-chain operations. - -Let's now imagine that this contract is going to live on one chain, but we want the minting and the upgrading to be performed by a xref:governance.adoc[`governor`] contract on another chain. - -For example, we could have our token on xDai, with our governor on mainnet, or we could have our token on mainnet, with our governor on optimism - -In order to do that, we will start by adding xref:api:crosschain.adoc#CrossChainEnabled[`CrossChainEnabled`] to our contract. You will notice that the contract is now abstract. This is because `CrossChainEnabled` is an abstract contract: it is not tied to any particular chain and it deals with cross-chain interactions in an abstract way. This is what enables us to easily reuse the code for different chains. We will specialize it later by inheriting from a chain-specific implementation of the abstraction. - -```diff - import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -+import "@openzeppelin/contracts-upgradeable/crosschain/CrossChainEnabled.sol"; - --contract MyToken is Initializable, ERC20Upgradeable, UUPSUpgradeable { -+abstract contract MyTokenCrossChain is Initializable, ERC20Upgradeable, UUPSUpgradeable, CrossChainEnabled { -``` - -Once that is done, we can use the `onlyCrossChainSender` modifier, provided by `CrossChainEnabled` in order to protect the minting and upgrading operations. - -```diff -- function mint(address to, uint256 amount) public onlyOwner { -+ function mint(address to, uint256 amount) public onlyCrossChainSender(owner) { - -- function _authorizeUpgrade(address newImplementation) internal override onlyOwner { -+ function _authorizeUpgrade(address newImplementation) internal override onlyCrossChainSender(owner) { -``` - -This change will effectively restrict the mint and upgrade operations to the `owner` on the remote chain. - -== Specializing for a specific chain - -Once the abstract cross-chain version of our token is ready we can easily specialize it for the chain we want, or more precisely for the bridge system that we want to rely on. - -This is done using one of the many `CrossChainEnabled` implementations. - -For example, if our token is on xDai, and our governor on mainnet, we can use the https://docs.tokenbridge.net/amb-bridge/about-amb-bridge[AMB] bridge available on xDai at https://blockscout.com/xdai/mainnet/address/0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59[0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59] - -[source,solidity] ----- -[...] - -import "@openzeppelin/contracts-upgradeable/crosschain/amb/CrossChainEnabledAMB.sol"; - -contract MyTokenXDAI is - MyTokenCrossChain, - CrossChainEnabledAMB(0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59) -{} ----- - -If the token is on Ethereum mainnet, and our governor on Optimism, we use the Optimism https://community.optimism.io/docs/protocol/protocol-2.0/#l1crossdomainmessenger[CrossDomainMessenger] available on mainnet at https://etherscan.io/address/0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1[0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1]. - -[source,solidity] ----- -[...] - -import "@openzeppelin/contracts-upgradeable/crosschain/optimismCrossChainEnabledOptimism.sol"; - -contract MyTokenOptimism is - MyTokenCrossChain, - CrossChainEnabledOptimism(0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1) -{} ----- - -== Mixing cross domain addresses is dangerous - -When designing a contract with cross-chain support, it is essential to understand possible fallbacks and the security assumption that are being made. - -In this guide, we are particularly focusing on restricting access to a specific caller. This is usually done (as shown above) using `msg.sender` or `_msgSender()`. However, when going cross-chain, it is not just that simple. Even without considering possible bridge issues, it is important to keep in mind that the same address can correspond to very different entities when considering a multi-chain space. EOA wallets can only execute operations if the wallet's private-key signs the transaction. To our knowledge this is the case in all EVM chains, so a cross-chain message coming from such a wallet is arguably equivalent to a non-cross-chain message by the same wallet. The situation is however very different for smart contracts. - -Due to the way smart contract addresses are computed, and the fact that smart contracts on different chains live independent lives, you could have two very different contracts live at the same address on different chains. You could imagine two multisig wallets with different signers using the same address on different chains. You could also see a very basic smart wallet live on one chain at the same address as a full-fledged governor on another chain. Therefore, you should be careful that whenever you give permissions to a specific address, you control with chain this address can act from. - -== Going further with access control - -In the previous example, we have both an `onlyOwner()` modifier and the `onlyCrossChainSender(owner)` mechanism. We didn't use the xref:access-control.adoc#ownership-and-ownable[`Ownable`] pattern because the ownership transfer mechanism in includes is not designed to work with the owner being a cross-chain entity. Unlike xref:access-control.adoc#ownership-and-ownable[`Ownable`], xref:access-control.adoc#role-based-access-control[`AccessControl`] is more effective at capturing the nuances and can effectively be used to build cross-chain-aware contracts. - -Using xref:api:access.adoc#AccessControlCrossChain[`AccessControlCrossChain`] includes both the xref:api:access.adoc#AccessControl[`AccessControl`] core and the xref:api:crosschain.adoc#CrossChainEnabled[`CrossChainEnabled`] abstraction. It also includes some binding to make role management compatible with cross-chain operations. - -In the case of the `mint` function, the caller must have the `MINTER_ROLE` when the call originates from the same chain. If the caller is on a remote chain, then the caller should not have the `MINTER_ROLE`, but the "aliased" version (`MINTER_ROLE ^ CROSSCHAIN_ALIAS`). This mitigates the danger described in the previous section by strictly separating local accounts from remote accounts from a different chain. See the xref:api:access.adoc#AccessControlCrossChain[`AccessControlCrossChain`] documentation for more details. - - -```diff - import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; - import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -+import "@openzeppelin/contracts-upgradeable/access/AccessControlCrossChainUpgradeable.sol"; - --abstract contract MyTokenCrossChain is Initializable, ERC20Upgradeable, UUPSUpgradeable, CrossChainEnabled { -+abstract contract MyTokenCrossChain is Initializable, ERC20Upgradeable, UUPSUpgradeable, AccessControlCrossChainUpgradeable { - -- address public owner; -- modifier onlyOwner() { -- require(owner == _msgSender(), "Not authorized"); -- _; -- } - -+ bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); -+ bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); - - function initialize(address initialOwner) initializer public { - __ERC20_init("MyToken", "MTK"); - __UUPSUpgradeable_init(); -+ __AccessControl_init(); -+ _grantRole(_crossChainRoleAlias(DEFAULT_ADMIN_ROLE), initialOwner); // initialOwner is on a remote chain -- owner = initialOwner; - } - -- function mint(address to, uint256 amount) public onlyCrossChainSender(owner) { -+ function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) { - -- function _authorizeUpgrade(address newImplementation) internal override onlyCrossChainSender(owner) { -+ function _authorizeUpgrade(address newImplementation) internal override onlyRole(UPGRADER_ROLE) { -``` - -This results in the following, final, code: - -[source,solidity] ----- -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/access/AccessControlCrossChainUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; - -abstract contract MyTokenCrossChain is Initializable, ERC20Upgradeable, AccessControlCrossChainUpgradeable, UUPSUpgradeable { - bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); - bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() initializer {} - - function initialize(address initialOwner) initializer public { - __ERC20_init("MyToken", "MTK"); - __AccessControl_init(); - __UUPSUpgradeable_init(); - - _grantRole(_crossChainRoleAlias(DEFAULT_ADMIN_ROLE), initialOwner); // initialOwner is on a remote chain - } - - function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) { - _mint(to, amount); - } - - function _authorizeUpgrade(address newImplementation) internal onlyRole(UPGRADER_ROLE) override { - } -} - -import "@openzeppelin/contracts-upgradeable/crosschain/amb/CrossChainEnabledAMB.sol"; - -contract MyTokenXDAI is - MyTokenCrossChain, - CrossChainEnabledAMB(0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59) -{} - -import "@openzeppelin/contracts-upgradeable/crosschain/optimismCrossChainEnabledOptimism.sol"; - -contract MyTokenOptimism is - MyTokenCrossChain, - CrossChainEnabledOptimism(0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1) -{} ----- diff --git a/docs/modules/ROOT/pages/erc1155.adoc b/docs/modules/ROOT/pages/erc1155.adoc index a01542c5a82..d7976ca1fec 100644 --- a/docs/modules/ROOT/pages/erc1155.adoc +++ b/docs/modules/ROOT/pages/erc1155.adoc @@ -2,7 +2,7 @@ ERC1155 is a novel token standard that aims to take the best from previous standards to create a xref:tokens.adoc#different-kinds-of-tokens[*fungibility-agnostic*] and *gas-efficient* xref:tokens.adoc#but_first_coffee_a_primer_on_token_contracts[token contract]. -TIP: ERC1155 draws ideas from all of xref:erc20.adoc[ERC20], xref:erc721.adoc[ERC721], and xref:erc777.adoc[ERC777]. If you're unfamiliar with those standards, head to their guides before moving on. +TIP: ERC1155 draws ideas from all of xref:erc20.adoc[ERC20], xref:erc721.adoc[ERC721], and https://eips.ethereum.org/EIPS/eip-777[ERC777]. If you're unfamiliar with those standards, head to their guides before moving on. [[multi-token-standard]] == Multi Token Standard @@ -22,9 +22,9 @@ In the spirit of the standard, we've also included batch operations in the non-s == Constructing an ERC1155 Token Contract -We'll use ERC1155 to track multiple items in our game, which will each have their own unique attributes. We mint all items to the deployer of the contract, which we can later transfer to players. Players are free to keep their tokens or trade them with other people as they see fit, as they would any other asset on the blockchain! +We'll use ERC1155 to track multiple items in our game, which will each have their own unique attributes. We mint all items to the deployer of the contract, which we can later transfer to players. Players are free to keep their tokens or trade them with other people as they see fit, as they would any other asset on the blockchain! -For simplicity, we will mint all items in the constructor, but you could add minting functionality to the contract to mint on demand to players. +For simplicity, we will mint all items in the constructor, but you could add minting functionality to the contract to mint on demand to players. TIP: For an overview of minting mechanisms, check out xref:erc20-supply.adoc[Creating ERC20 Supply]. @@ -112,7 +112,7 @@ The JSON document for token ID 2 might look something like: For more information about the metadata JSON Schema, check out the https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md#erc-1155-metadata-uri-json-schema[ERC-1155 Metadata URI JSON Schema]. -NOTE: You'll notice that the item's information is included in the metadata, but that information isn't on-chain! So a game developer could change the underlying metadata, changing the rules of the game! +NOTE: You'll notice that the item's information is included in the metadata, but that information isn't on-chain! So a game developer could change the underlying metadata, changing the rules of the game! TIP: If you'd like to put all item information on-chain, you can extend ERC721 to do so (though it will be rather costly) by providing a xref:utilities.adoc#base64[`Base64`] Data URI with the JSON schema encoded. You could also leverage IPFS to store the URI information, but these techniques are out of the scope of this overview guide diff --git a/docs/modules/ROOT/pages/erc777.adoc b/docs/modules/ROOT/pages/erc777.adoc deleted file mode 100644 index 4a0af16c396..00000000000 --- a/docs/modules/ROOT/pages/erc777.adoc +++ /dev/null @@ -1,75 +0,0 @@ -= ERC777 - -CAUTION: As of v4.9, OpenZeppelin's implementation of ERC-777 is deprecated and will be removed in the next major release. - -Like xref:erc20.adoc[ERC20], ERC777 is a standard for xref:tokens.adoc#different-kinds-of-tokens[_fungible_ tokens], and is focused around allowing more complex interactions when trading tokens. More generally, it brings tokens and Ether closer together by providing the equivalent of a `msg.value` field, but for tokens. - -The standard also brings multiple quality-of-life improvements, such as getting rid of the confusion around `decimals`, minting and burning with proper events, among others, but its killer feature is *receive hooks*. A hook is simply a function in a contract that is called when tokens are sent to it, meaning *accounts and contracts can react to receiving tokens*. - -This enables a lot of interesting use cases, including atomic purchases using tokens (no need to do `approve` and `transferFrom` in two separate transactions), rejecting reception of tokens (by reverting on the hook call), redirecting the received tokens to other addresses (similarly to how xref:api:payment#PaymentSplitter[`PaymentSplitter`] does it), among many others. - -Furthermore, since contracts are required to implement these hooks in order to receive tokens, _no tokens can get stuck in a contract that is unaware of the ERC777 protocol_, as has happened countless times when using ERC20s. - -== What If I Already Use ERC20? - -The standard has you covered! The ERC777 standard is *backwards compatible with ERC20*, meaning you can interact with these tokens as if they were ERC20, using the standard functions, while still getting all of the niceties, including send hooks. See the https://eips.ethereum.org/EIPS/eip-777#backward-compatibility[EIP's Backwards Compatibility section] to learn more. - -== Constructing an ERC777 Token Contract - -We will replicate the `GLD` example of the xref:erc20.adoc#constructing-an-erc20-token-contract[ERC20 guide], this time using ERC777. As always, check out the xref:api:token/ERC777.adoc[`API reference`] to learn more about the details of each function. - -[source,solidity] ----- -// contracts/GLDToken.sol -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/token/ERC777/ERC777.sol"; - -contract GLDToken is ERC777 { - constructor(uint256 initialSupply, address[] memory defaultOperators) - ERC777("Gold", "GLD", defaultOperators) - { - _mint(msg.sender, initialSupply, "", ""); - } -} ----- - -In this case, we'll be extending from the xref:api:token/ERC777.adoc#ERC777[`ERC777`] contract, which provides an implementation with compatibility support for ERC20. The API is quite similar to that of xref:api:token/ERC777.adoc#ERC777[`ERC777`], and we'll once again make use of xref:api:token/ERC777.adoc#ERC777-_mint-address-address-uint256-bytes-bytes-[`_mint`] to assign the `initialSupply` to the deployer account. Unlike xref:api:token/ERC20.adoc#ERC20-_mint-address-uint256-[ERC20's `_mint`], this one includes some extra parameters, but you can safely ignore those for now. - -You'll notice both xref:api:token/ERC777.adoc#IERC777-name--[`name`] and xref:api:token/ERC777.adoc#IERC777-symbol--[`symbol`] are assigned, but not xref:api:token/ERC777.adoc#ERC777-decimals--[`decimals`]. The ERC777 specification makes it mandatory to include support for these functions (unlike ERC20, where it is optional and we had to include xref:api:token/ERC20.adoc#ERC20Detailed[`ERC20Detailed`]), but also mandates that `decimals` always returns a fixed value of `18`, so there's no need to set it ourselves. For a review of ``decimals``'s role and importance, refer back to our xref:erc20.adoc#a-note-on-decimals[ERC20 guide]. - -Finally, we'll need to set the xref:api:token/ERC777.adoc#IERC777-defaultOperators--[`defaultOperators`]: special accounts (usually other smart contracts) that will be able to transfer tokens on behalf of their holders. If you're not planning on using operators in your token, you can simply pass an empty array. _Stay tuned for an upcoming in-depth guide on ERC777 operators!_ - -That's it for a basic token contract! We can now deploy it, and use the same xref:api:token/ERC777.adoc#IERC777-balanceOf-address-[`balanceOf`] method to query the deployer's balance: - -[source,javascript] ----- -> GLDToken.balanceOf(deployerAddress) -1000 ----- - -To move tokens from one account to another, we can use both xref:api:token/ERC777.adoc#ERC777-transfer-address-uint256-[``ERC20``'s `transfer`] method, or the new xref:api:token/ERC777.adoc#ERC777-send-address-uint256-bytes-[``ERC777``'s `send`], which fulfills a very similar role, but adds an optional `data` field: - -[source,javascript] ----- -> GLDToken.transfer(otherAddress, 300) -> GLDToken.send(otherAddress, 300, "") -> GLDToken.balanceOf(otherAddress) -600 -> GLDToken.balanceOf(deployerAddress) -400 ----- - -== Sending Tokens to Contracts - -A key difference when using xref:api:token/ERC777.adoc#ERC777-send-address-uint256-bytes-[`send`] is that token transfers to other contracts may revert with the following message: - -[source,text] ----- -ERC777: token recipient contract has no implementer for ERC777TokensRecipient ----- - -This is a good thing! It means that the recipient contract has not registered itself as aware of the ERC777 protocol, so transfers to it are disabled to *prevent tokens from being locked forever*. As an example, https://etherscan.io/token/0xa74476443119A942dE498590Fe1f2454d7D4aC0d?a=0xa74476443119A942dE498590Fe1f2454d7D4aC0d[the Golem contract currently holds over 350k `GNT` tokens], worth multiple tens of thousands of dollars, and lacks methods to get them out of there. This has happened to virtually every ERC20-backed project, usually due to user error. - -_An upcoming guide will cover how a contract can register itself as a recipient, send and receive hooks, and other advanced features of ERC777!_ diff --git a/docs/modules/ROOT/pages/tokens.adoc b/docs/modules/ROOT/pages/tokens.adoc index b168756df42..10626f54801 100644 --- a/docs/modules/ROOT/pages/tokens.adoc +++ b/docs/modules/ROOT/pages/tokens.adoc @@ -28,5 +28,4 @@ You've probably heard of the ERC20 or ERC721 token standards, and that's why you * xref:erc20.adoc[ERC20]: the most widespread token standard for fungible assets, albeit somewhat limited by its simplicity. * xref:erc721.adoc[ERC721]: the de-facto solution for non-fungible tokens, often used for collectibles and games. - * xref:erc777.adoc[ERC777]: a richer standard for fungible tokens, enabling new use cases and building on past learnings. Backwards compatible with ERC20. * xref:erc1155.adoc[ERC1155]: a novel standard for multi-tokens, allowing for a single contract to represent multiple fungible and non-fungible tokens, along with batched operations for increased gas efficiency. diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index 88207f0e109..eecb8293ea2 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -87,9 +87,7 @@ Easy! Want to split some payments between multiple people? Maybe you have an app that sends 30% of art purchases to the original creator and 70% of the profits to the current owner; you can build that with xref:api:finance.adoc#PaymentSplitter[`PaymentSplitter`]! -In Solidity, there are some security concerns with blindly sending money to accounts, since it allows them to execute arbitrary code. You can read up on these security concerns in the https://consensys.github.io/smart-contract-best-practices/[Ethereum Smart Contract Best Practices] website. One of the ways to fix reentrancy and stalling problems is, instead of immediately sending Ether to accounts that need it, you can use xref:api:security.adoc#PullPayment[`PullPayment`], which offers an xref:api:security.adoc#PullPayment-_asyncTransfer-address-uint256-[`_asyncTransfer`] function for sending money to something and requesting that they xref:api:security.adoc#PullPayment-withdrawPayments-address-payable-[`withdrawPayments()`] it later. - -If you want to Escrow some funds, check out xref:api:utils.adoc#Escrow[`Escrow`] and xref:api:utils.adoc#ConditionalEscrow[`ConditionalEscrow`] for governing the release of some escrowed Ether. +In Solidity, there are some security concerns with blindly sending money to accounts, since it allows them to execute arbitrary code. You can read up on these security concerns in the https://consensys.github.io/smart-contract-best-practices/[Ethereum Smart Contract Best Practices] website. [[collections]] == Collections @@ -103,7 +101,7 @@ Want to keep track of some numbers that increment by 1 every time you want anoth === Base64 -xref:api:utils.adoc#Base64[`Base64`] util allows you to transform `bytes32` data into its Base64 `string` representation. +xref:api:utils.adoc#Base64[`Base64`] util allows you to transform `bytes32` data into its Base64 `string` representation. This is especially useful for building URL-safe tokenURIs for both xref:api:token/ERC721.adoc#IERC721Metadata-tokenURI-uint256-[`ERC721`] or xref:api:token/ERC1155.adoc#IERC1155MetadataURI-uri-uint256-[`ERC1155`]. This library provides a clever way to serve URL-safe https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs/[Data URI] compliant strings to serve on-chain data structures. @@ -122,7 +120,7 @@ contract My721Token is ERC721 { using Strings for uint256; constructor() ERC721("My721Token", "MTK") {} - + ... function tokenURI(uint256 tokenId) @@ -140,7 +138,7 @@ contract My721Token is ERC721 { return string( abi.encodePacked( - "data:application/json;base64,", + "data:application/json;base64,", Base64.encode(dataURI) ) ); diff --git a/hardhat.config.js b/hardhat.config.js index 5fd703d06db..17d2c47b234 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -93,11 +93,7 @@ module.exports = { }, exposed: { initializers: true, - exclude: [ - 'vendor/**/*', - // Exclude Timers from hardhat-exposed because its overloaded functions are not transformed correctly - 'utils/Timers{,Upgradeable}.sol', - ], + exclude: ['vendor/**/*'], }, docgen: require('./docs/config'), }; diff --git a/scripts/generate/templates/Checkpoints.js b/scripts/generate/templates/Checkpoints.js index f64973b3102..de58ad19e3a 100644 --- a/scripts/generate/templates/Checkpoints.js +++ b/scripts/generate/templates/Checkpoints.js @@ -1,5 +1,5 @@ const format = require('../format-lines'); -const { OPTS, LEGACY_OPTS } = require('./Checkpoints.opts.js'); +const { OPTS } = require('./Checkpoints.opts.js'); // TEMPLATE const header = `\ @@ -19,7 +19,7 @@ import "./math/SafeCast.sol"; */ `; -const types = opts => `\ +const template = opts => `\ struct ${opts.historyTypeName} { ${opts.checkpointTypeName}[] ${opts.checkpointFieldName}; } @@ -28,10 +28,7 @@ struct ${opts.checkpointTypeName} { ${opts.keyTypeName} ${opts.keyFieldName}; ${opts.valueTypeName} ${opts.valueFieldName}; } -`; -/* eslint-disable max-len */ -const operations = opts => `\ /** * @dev Pushes a (\`key\`, \`value\`) pair into a ${opts.historyTypeName} so that it is stored as the checkpoint. * @@ -87,77 +84,7 @@ function upperLookupRecent(${opts.historyTypeName} storage self, ${opts.keyTypeN return pos == 0 ? 0 : _unsafeAccess(self.${opts.checkpointFieldName}, pos - 1).${opts.valueFieldName}; } -`; - -const legacyOperations = opts => `\ -/** - * @dev Returns the value at a given block number. If a checkpoint is not available at that block, the closest one - * before it is returned, or zero otherwise. Because the number returned corresponds to that at the end of the - * block, the requested block number must be in the past, excluding the current block. - */ -function getAtBlock(${opts.historyTypeName} storage self, uint256 blockNumber) internal view returns (uint256) { - require(blockNumber < block.number, "Checkpoints: block not yet mined"); - uint32 key = SafeCast.toUint32(blockNumber); - - uint256 len = self.${opts.checkpointFieldName}.length; - uint256 pos = _upperBinaryLookup(self.${opts.checkpointFieldName}, key, 0, len); - return pos == 0 ? 0 : _unsafeAccess(self.${opts.checkpointFieldName}, pos - 1).${opts.valueFieldName}; -} - -/** - * @dev Returns the value at a given block number. If a checkpoint is not available at that block, the closest one - * before it is returned, or zero otherwise. Similar to {upperLookup} but optimized for the case when the searched - * checkpoint is probably "recent", defined as being among the last sqrt(N) checkpoints where N is the number of - * checkpoints. - */ -function getAtProbablyRecentBlock(${opts.historyTypeName} storage self, uint256 blockNumber) internal view returns (uint256) { - require(blockNumber < block.number, "Checkpoints: block not yet mined"); - uint32 key = SafeCast.toUint32(blockNumber); - - uint256 len = self.${opts.checkpointFieldName}.length; - - uint256 low = 0; - uint256 high = len; - - if (len > 5) { - uint256 mid = len - Math.sqrt(len); - if (key < _unsafeAccess(self.${opts.checkpointFieldName}, mid)._blockNumber) { - high = mid; - } else { - low = mid + 1; - } - } - - uint256 pos = _upperBinaryLookup(self.${opts.checkpointFieldName}, key, low, high); - - return pos == 0 ? 0 : _unsafeAccess(self.${opts.checkpointFieldName}, pos - 1).${opts.valueFieldName}; -} - -/** - * @dev Pushes a value onto a History so that it is stored as the checkpoint for the current block. - * - * Returns previous value and new value. - */ -function push(${opts.historyTypeName} storage self, uint256 value) internal returns (uint256, uint256) { - return _insert(self.${opts.checkpointFieldName}, SafeCast.toUint32(block.number), SafeCast.toUint224(value)); -} - -/** - * @dev Pushes a value onto a History, by updating the latest value using binary operation \`op\`. The new value will - * be set to \`op(latest, delta)\`. - * - * Returns previous value and new value. - */ -function push( - ${opts.historyTypeName} storage self, - function(uint256, uint256) view returns (uint256) op, - uint256 delta -) internal returns (uint256, uint256) { - return push(self, op(latest(self), delta)); -} -`; -const common = opts => `\ /** * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints. */ @@ -299,13 +226,6 @@ function _unsafeAccess(${opts.checkpointTypeName}[] storage self, uint256 pos) module.exports = format( header.trimEnd(), 'library Checkpoints {', - [ - // Legacy types & functions - types(LEGACY_OPTS), - legacyOperations(LEGACY_OPTS), - common(LEGACY_OPTS), - // New flavors - ...OPTS.flatMap(opts => [types(opts), operations(opts), common(opts)]), - ], + OPTS.flatMap(opts => template(opts)), '}', ); diff --git a/scripts/generate/templates/Checkpoints.opts.js b/scripts/generate/templates/Checkpoints.opts.js index 03c5a9569d7..b8be23104d5 100644 --- a/scripts/generate/templates/Checkpoints.opts.js +++ b/scripts/generate/templates/Checkpoints.opts.js @@ -12,11 +12,6 @@ const defaultOpts = size => ({ }); module.exports = { + VALUE_SIZES, OPTS: VALUE_SIZES.map(size => defaultOpts(size)), - LEGACY_OPTS: { - ...defaultOpts(224), - historyTypeName: 'History', - checkpointTypeName: 'Checkpoint', - keyFieldName: '_blockNumber', - }, }; diff --git a/scripts/generate/templates/Checkpoints.t.js b/scripts/generate/templates/Checkpoints.t.js index b3da933a1e8..0451ecea442 100644 --- a/scripts/generate/templates/Checkpoints.t.js +++ b/scripts/generate/templates/Checkpoints.t.js @@ -1,6 +1,6 @@ const format = require('../format-lines'); const { capitalize } = require('../../helpers'); -const { OPTS, LEGACY_OPTS } = require('./Checkpoints.opts.js'); +const { OPTS } = require('./Checkpoints.opts.js'); // TEMPLATE const header = `\ @@ -12,7 +12,7 @@ import "../../contracts/utils/math/SafeCast.sol"; `; /* eslint-disable max-len */ -const common = opts => `\ +const template = opts => `\ using Checkpoints for Checkpoints.${opts.historyTypeName}; // Maximum gap between keys used during the fuzzing tests: the \`_prepareKeys\` function with make sure that @@ -52,9 +52,7 @@ function _assertLatestCheckpoint( assertEq(_key, key); assertEq(_value, value); } -`; -const testTrace = opts => `\ // tests function testPush( ${opts.keyTypeName}[] memory keys, @@ -88,7 +86,7 @@ function testPush( ${opts.keyTypeName} lastKey = keys[keys.length - 1]; if (lastKey > 0) { pastKey = _bound${capitalize(opts.keyTypeName)}(pastKey, 0, lastKey - 1); - + vm.expectRevert(); this.push(pastKey, values[keys.length % values.length]); } @@ -141,116 +139,8 @@ function testLookup( } `; -const testHistory = opts => `\ -// tests -function testPush( - ${opts.keyTypeName}[] memory keys, - ${opts.valueTypeName}[] memory values, - ${opts.keyTypeName} pastKey -) public { - vm.assume(values.length > 0 && values.length <= keys.length); - _prepareKeys(keys, _KEY_MAX_GAP); - - // initial state - assertEq(_ckpts.length(), 0); - assertEq(_ckpts.latest(), 0); - _assertLatestCheckpoint(false, 0, 0); - - uint256 duplicates = 0; - for (uint256 i = 0; i < keys.length; ++i) { - ${opts.keyTypeName} key = keys[i]; - ${opts.valueTypeName} value = values[i % values.length]; - if (i > 0 && key == keys[i - 1]) ++duplicates; - - // push - vm.roll(key); - _ckpts.push(value); - - // check length & latest - assertEq(_ckpts.length(), i + 1 - duplicates); - assertEq(_ckpts.latest(), value); - _assertLatestCheckpoint(true, key, value); - } - - // Can't push any key in the past - if (keys.length > 0) { - ${opts.keyTypeName} lastKey = keys[keys.length - 1]; - if (lastKey > 0) { - pastKey = _bound${capitalize(opts.keyTypeName)}(pastKey, 0, lastKey - 1); - - vm.roll(pastKey); - vm.expectRevert(); - this.push(values[keys.length % values.length]); - } - } -} - -// used to test reverts -function push(${opts.valueTypeName} value) external { - _ckpts.push(value); -} - -function testLookup( - ${opts.keyTypeName}[] memory keys, - ${opts.valueTypeName}[] memory values, - ${opts.keyTypeName} lookup -) public { - vm.assume(keys.length > 0); - vm.assume(values.length > 0 && values.length <= keys.length); - _prepareKeys(keys, _KEY_MAX_GAP); - - ${opts.keyTypeName} lastKey = keys[keys.length - 1]; - vm.assume(lastKey > 0); - lookup = _bound${capitalize(opts.keyTypeName)}(lookup, 0, lastKey - 1); - - ${opts.valueTypeName} upper = 0; - for (uint256 i = 0; i < keys.length; ++i) { - ${opts.keyTypeName} key = keys[i]; - ${opts.valueTypeName} value = values[i % values.length]; - - // push - vm.roll(key); - _ckpts.push(value); - - // track expected result of lookups - if (key <= lookup) { - upper = value; - } - } - - // check lookup - assertEq(_ckpts.getAtBlock(lookup), upper); - assertEq(_ckpts.getAtProbablyRecentBlock(lookup), upper); - - vm.expectRevert(); this.getAtBlock(lastKey); - vm.expectRevert(); this.getAtBlock(lastKey + 1); - vm.expectRevert(); this.getAtProbablyRecentBlock(lastKey); - vm.expectRevert(); this.getAtProbablyRecentBlock(lastKey + 1); -} - -// used to test reverts -function getAtBlock(${opts.keyTypeName} key) external view { - _ckpts.getAtBlock(key); -} - -// used to test reverts -function getAtProbablyRecentBlock(${opts.keyTypeName} key) external view { - _ckpts.getAtProbablyRecentBlock(key); -} -`; -/* eslint-enable max-len */ - // GENERATE module.exports = format( header, - // HISTORY - `contract Checkpoints${LEGACY_OPTS.historyTypeName}Test is Test {`, - [common(LEGACY_OPTS), testHistory(LEGACY_OPTS)], - '}', - // TRACEXXX - ...OPTS.flatMap(opts => [ - `contract Checkpoints${opts.historyTypeName}Test is Test {`, - [common(opts), testTrace(opts)], - '}', - ]), + ...OPTS.flatMap(opts => [`contract Checkpoints${opts.historyTypeName}Test is Test {`, [template(opts)], '}']), ); diff --git a/scripts/generate/templates/EnumerableMap.js b/scripts/generate/templates/EnumerableMap.js index 2bbb69e3e7c..61e23cfb434 100644 --- a/scripts/generate/templates/EnumerableMap.js +++ b/scripts/generate/templates/EnumerableMap.js @@ -153,22 +153,6 @@ function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns return value; } -/** - * @dev Same as {get}, with a custom error message when \`key\` is not in the map. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryGet}. - */ -function get( - Bytes32ToBytes32Map storage map, - bytes32 key, - string memory errorMessage -) internal view returns (bytes32) { - bytes32 value = map._values[key]; - require(value != 0 || contains(map, key), errorMessage); - return value; -} - /** * @dev Return the an array containing all the keys * @@ -261,20 +245,6 @@ function get(${name} storage map, ${keyType} key) internal view returns (${value return ${fromBytes32(valueType, `get(map._inner, ${toBytes32(keyType, 'key')})`)}; } -/** - * @dev Same as {get}, with a custom error message when \`key\` is not in the map. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryGet}. - */ -function get( - ${name} storage map, - ${keyType} key, - string memory errorMessage -) internal view returns (${valueType}) { - return ${fromBytes32(valueType, `get(map._inner, ${toBytes32(keyType, 'key')}, errorMessage)`)}; -} - /** * @dev Return the an array containing all the keys * diff --git a/scripts/generate/templates/SafeCast.js b/scripts/generate/templates/SafeCast.js index 3e2b148fda3..b47193be63b 100644 --- a/scripts/generate/templates/SafeCast.js +++ b/scripts/generate/templates/SafeCast.js @@ -74,9 +74,6 @@ pragma solidity ^0.8.0; * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. - * - * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing - * all math on \`uint256\` and \`int256\` and then downcasting. */ `; diff --git a/scripts/upgradeable/upgradeable.patch b/scripts/upgradeable/upgradeable.patch index 6f022d7b52e..bc675ab8a18 100644 --- a/scripts/upgradeable/upgradeable.patch +++ b/scripts/upgradeable/upgradeable.patch @@ -159,19 +159,6 @@ index 55e70b17..ceefb984 100644 }, "keywords": [ "solidity", -diff --git a/contracts/security/PullPayment.sol b/contracts/security/PullPayment.sol -index 65b4980f..f336592e 100644 ---- a/contracts/security/PullPayment.sol -+++ b/contracts/security/PullPayment.sol -@@ -22,6 +22,8 @@ import "../utils/escrow/Escrow.sol"; - * To use, derive from the `PullPayment` contract, and use {_asyncTransfer} - * instead of Solidity's `transfer` function. Payees can query their due - * payments with {payments}, and retrieve them with {withdrawPayments}. -+ * -+ * @custom:storage-size 51 - */ - abstract contract PullPayment { - Escrow private immutable _escrow; diff --git a/contracts/token/ERC20/extensions/ERC20Capped.sol b/contracts/token/ERC20/extensions/ERC20Capped.sol index 16f830d1..9ef98148 100644 --- a/contracts/token/ERC20/extensions/ERC20Capped.sol diff --git a/test/access/AccessControlCrossChain.test.js b/test/access/AccessControlCrossChain.test.js deleted file mode 100644 index d5a41076b0b..00000000000 --- a/test/access/AccessControlCrossChain.test.js +++ /dev/null @@ -1,49 +0,0 @@ -const { expectRevert } = require('@openzeppelin/test-helpers'); -const { BridgeHelper } = require('../helpers/crosschain'); - -const { DEFAULT_ADMIN_ROLE, shouldBehaveLikeAccessControl } = require('./AccessControl.behavior.js'); - -const crossChainRoleAlias = role => - web3.utils.leftPad( - web3.utils.toHex(web3.utils.toBN(role).xor(web3.utils.toBN(web3.utils.soliditySha3('CROSSCHAIN_ALIAS')))), - 64, - ); - -const AccessControlCrossChainMock = artifacts.require('$AccessControlCrossChainMock'); - -const ROLE = web3.utils.soliditySha3('ROLE'); - -contract('AccessControl', function (accounts) { - before(async function () { - this.bridge = await BridgeHelper.deploy(); - }); - - beforeEach(async function () { - this.accessControl = await AccessControlCrossChainMock.new({ from: accounts[0] }); - await this.accessControl.$_grantRole(DEFAULT_ADMIN_ROLE, accounts[0]); - }); - - shouldBehaveLikeAccessControl('AccessControl', ...accounts); - - describe('CrossChain enabled', function () { - beforeEach(async function () { - await this.accessControl.grantRole(ROLE, accounts[0], { from: accounts[0] }); - await this.accessControl.grantRole(crossChainRoleAlias(ROLE), accounts[1], { from: accounts[0] }); - }); - - it('check alliassing', async function () { - expect(await this.accessControl.$_crossChainRoleAlias(ROLE)).to.be.bignumber.equal(crossChainRoleAlias(ROLE)); - }); - - it('Crosschain calls not authorized to non-aliased addresses', async function () { - await expectRevert( - this.bridge.call(accounts[0], this.accessControl, '$_checkRole(bytes32)', [ROLE]), - `AccessControl: account ${accounts[0].toLowerCase()} is missing role ${crossChainRoleAlias(ROLE)}`, - ); - }); - - it('Crosschain calls not authorized to non-aliased addresses', async function () { - await this.bridge.call(accounts[1], this.accessControl, '$_checkRole(bytes32)', [ROLE]); - }); - }); -}); diff --git a/test/crosschain/CrossChainEnabled.test.js b/test/crosschain/CrossChainEnabled.test.js deleted file mode 100644 index 9e7d26308da..00000000000 --- a/test/crosschain/CrossChainEnabled.test.js +++ /dev/null @@ -1,78 +0,0 @@ -const { BridgeHelper } = require('../helpers/crosschain'); -const { expectRevertCustomError } = require('../helpers/customError'); - -function randomAddress() { - return web3.utils.toChecksumAddress(web3.utils.randomHex(20)); -} - -const CrossChainEnabledAMBMock = artifacts.require('CrossChainEnabledAMBMock'); -const CrossChainEnabledArbitrumL1Mock = artifacts.require('CrossChainEnabledArbitrumL1Mock'); -const CrossChainEnabledArbitrumL2Mock = artifacts.require('CrossChainEnabledArbitrumL2Mock'); -const CrossChainEnabledOptimismMock = artifacts.require('CrossChainEnabledOptimismMock'); -const CrossChainEnabledPolygonChildMock = artifacts.require('CrossChainEnabledPolygonChildMock'); - -function shouldBehaveLikeReceiver(sender = randomAddress()) { - it('should reject same-chain calls', async function () { - await expectRevertCustomError(this.receiver.crossChainRestricted(), 'NotCrossChainCall()'); - - await expectRevertCustomError(this.receiver.crossChainOwnerRestricted(), 'NotCrossChainCall()'); - }); - - it('should restrict to cross-chain call from a invalid sender', async function () { - await expectRevertCustomError( - this.bridge.call(sender, this.receiver, 'crossChainOwnerRestricted()'), - `InvalidCrossChainSender("${sender}", "${await this.receiver.owner()}")`, - ); - }); - - it('should grant access to cross-chain call from the owner', async function () { - await this.bridge.call(await this.receiver.owner(), this.receiver, 'crossChainOwnerRestricted()'); - }); -} - -contract('CrossChainEnabled', function () { - describe('AMB', function () { - beforeEach(async function () { - this.bridge = await BridgeHelper.deploy('AMB'); - this.receiver = await CrossChainEnabledAMBMock.new(this.bridge.address); - }); - - shouldBehaveLikeReceiver(); - }); - - describe('Arbitrum-L1', function () { - beforeEach(async function () { - this.bridge = await BridgeHelper.deploy('Arbitrum-L1'); - this.receiver = await CrossChainEnabledArbitrumL1Mock.new(this.bridge.address); - }); - - shouldBehaveLikeReceiver(); - }); - - describe('Arbitrum-L2', function () { - beforeEach(async function () { - this.bridge = await BridgeHelper.deploy('Arbitrum-L2'); - this.receiver = await CrossChainEnabledArbitrumL2Mock.new(); - }); - - shouldBehaveLikeReceiver(); - }); - - describe('Optimism', function () { - beforeEach(async function () { - this.bridge = await BridgeHelper.deploy('Optimism'); - this.receiver = await CrossChainEnabledOptimismMock.new(this.bridge.address); - }); - - shouldBehaveLikeReceiver(); - }); - - describe('Polygon-Child', function () { - beforeEach(async function () { - this.bridge = await BridgeHelper.deploy('Polygon-Child'); - this.receiver = await CrossChainEnabledPolygonChildMock.new(this.bridge.address); - }); - - shouldBehaveLikeReceiver(); - }); -}); diff --git a/test/helpers/crosschain.js b/test/helpers/crosschain.js deleted file mode 100644 index 9e6ff9610a5..00000000000 --- a/test/helpers/crosschain.js +++ /dev/null @@ -1,61 +0,0 @@ -const { promisify } = require('util'); - -const BridgeAMBMock = artifacts.require('BridgeAMBMock'); -const BridgeArbitrumL1Mock = artifacts.require('BridgeArbitrumL1Mock'); -const BridgeArbitrumL2Mock = artifacts.require('BridgeArbitrumL2Mock'); -const BridgeOptimismMock = artifacts.require('BridgeOptimismMock'); -const BridgePolygonChildMock = artifacts.require('BridgePolygonChildMock'); - -class BridgeHelper { - static async deploy(type) { - return new BridgeHelper(await deployBridge(type)); - } - - constructor(bridge) { - this.bridge = bridge; - this.address = bridge.address; - } - - call(from, target, selector = undefined, args = []) { - return this.bridge.relayAs( - target.address || target, - selector ? target.contract.methods[selector](...args).encodeABI() : '0x', - from, - ); - } -} - -async function deployBridge(type = 'Arbitrum-L2') { - switch (type) { - case 'AMB': - return BridgeAMBMock.new(); - - case 'Arbitrum-L1': - return BridgeArbitrumL1Mock.new(); - - case 'Arbitrum-L2': { - const instance = await BridgeArbitrumL2Mock.new(); - const code = await web3.eth.getCode(instance.address); - await promisify(web3.currentProvider.send.bind(web3.currentProvider))({ - jsonrpc: '2.0', - method: 'hardhat_setCode', - params: ['0x0000000000000000000000000000000000000064', code], - id: new Date().getTime(), - }); - return BridgeArbitrumL2Mock.at('0x0000000000000000000000000000000000000064'); - } - - case 'Optimism': - return BridgeOptimismMock.new(); - - case 'Polygon-Child': - return BridgePolygonChildMock.new(); - - default: - throw new Error(`CrossChain: ${type} is not supported`); - } -} - -module.exports = { - BridgeHelper, -}; diff --git a/test/security/PullPayment.test.js b/test/security/PullPayment.test.js deleted file mode 100644 index 5bf72bbe699..00000000000 --- a/test/security/PullPayment.test.js +++ /dev/null @@ -1,51 +0,0 @@ -const { balance, ether } = require('@openzeppelin/test-helpers'); - -const { expect } = require('chai'); - -const PullPaymentMock = artifacts.require('PullPaymentMock'); - -contract('PullPayment', function (accounts) { - const [payer, payee1, payee2] = accounts; - - const amount = ether('17'); - - beforeEach(async function () { - this.contract = await PullPaymentMock.new({ value: amount }); - }); - - describe('payments', function () { - it('can record an async payment correctly', async function () { - await this.contract.callTransfer(payee1, 100, { from: payer }); - expect(await this.contract.payments(payee1)).to.be.bignumber.equal('100'); - }); - - it('can add multiple balances on one account', async function () { - await this.contract.callTransfer(payee1, 200, { from: payer }); - await this.contract.callTransfer(payee1, 300, { from: payer }); - expect(await this.contract.payments(payee1)).to.be.bignumber.equal('500'); - }); - - it('can add balances on multiple accounts', async function () { - await this.contract.callTransfer(payee1, 200, { from: payer }); - await this.contract.callTransfer(payee2, 300, { from: payer }); - - expect(await this.contract.payments(payee1)).to.be.bignumber.equal('200'); - - expect(await this.contract.payments(payee2)).to.be.bignumber.equal('300'); - }); - }); - - describe('withdrawPayments', function () { - it('can withdraw payment', async function () { - const balanceTracker = await balance.tracker(payee1); - - await this.contract.callTransfer(payee1, amount, { from: payer }); - expect(await this.contract.payments(payee1)).to.be.bignumber.equal(amount); - - await this.contract.withdrawPayments(payee1); - - expect(await balanceTracker.delta()).to.be.bignumber.equal(amount); - expect(await this.contract.payments(payee1)).to.be.bignumber.equal('0'); - }); - }); -}); diff --git a/test/token/ERC20/utils/SafeERC20.test.js b/test/token/ERC20/utils/SafeERC20.test.js index d4981dd30cc..04b3b5cb14d 100644 --- a/test/token/ERC20/utils/SafeERC20.test.js +++ b/test/token/ERC20/utils/SafeERC20.test.js @@ -182,17 +182,6 @@ contract('SafeERC20', function (accounts) { await this.token.$_approve(this.mock.address, spender, 100); }); - it('safeApprove fails to update approval to non-zero', async function () { - await expectRevert( - this.mock.$safeApprove(this.token.address, spender, 200), - 'SafeERC20: approve from non-zero to non-zero allowance', - ); - }); - - it('safeApprove can update approval to zero', async function () { - await this.mock.$safeApprove(this.token.address, spender, 0); - }); - it('safeApprove can increase approval', async function () { await expectRevert(this.mock.$safeIncreaseAllowance(this.token.address, spender, 10), 'USDT approval failure'); }); @@ -217,10 +206,6 @@ function shouldRevertOnAllCalls([receiver, spender], reason) { await expectRevert(this.mock.$safeTransferFrom(this.token.address, this.mock.address, receiver, 0), reason); }); - it('reverts on approve', async function () { - await expectRevert(this.mock.$safeApprove(this.token.address, spender, 0), reason); - }); - it('reverts on increaseAllowance', async function () { // [TODO] make sure it's reverting for the right reason await expectRevert.unspecified(this.mock.$safeIncreaseAllowance(this.token.address, spender, 0)); @@ -269,16 +254,6 @@ function shouldOnlyRevertOnErrors([owner, receiver, spender]) { await this.token.$_approve(this.mock.address, spender, 0); }); - it("doesn't revert when approving a non-zero allowance", async function () { - await this.mock.$safeApprove(this.token.address, spender, 100); - expect(await this.token.allowance(this.mock.address, spender)).to.be.bignumber.equal('100'); - }); - - it("doesn't revert when approving a zero allowance", async function () { - await this.mock.$safeApprove(this.token.address, spender, 0); - expect(await this.token.allowance(this.mock.address, spender)).to.be.bignumber.equal('0'); - }); - it("doesn't revert when force approving a non-zero allowance", async function () { await this.mock.$forceApprove(this.token.address, spender, 100); expect(await this.token.allowance(this.mock.address, spender)).to.be.bignumber.equal('100'); @@ -307,18 +282,6 @@ function shouldOnlyRevertOnErrors([owner, receiver, spender]) { await this.token.$_approve(this.mock.address, spender, 100); }); - it('reverts when approving a non-zero allowance', async function () { - await expectRevert( - this.mock.$safeApprove(this.token.address, spender, 20), - 'SafeERC20: approve from non-zero to non-zero allowance', - ); - }); - - it("doesn't revert when approving a zero allowance", async function () { - await this.mock.$safeApprove(this.token.address, spender, 0); - expect(await this.token.allowance(this.mock.address, spender)).to.be.bignumber.equal('0'); - }); - it("doesn't revert when force approving a non-zero allowance", async function () { await this.mock.$forceApprove(this.token.address, spender, 20); expect(await this.token.allowance(this.mock.address, spender)).to.be.bignumber.equal('20'); diff --git a/test/token/ERC777/ERC777.behavior.js b/test/token/ERC777/ERC777.behavior.js deleted file mode 100644 index b1585bc9154..00000000000 --- a/test/token/ERC777/ERC777.behavior.js +++ /dev/null @@ -1,597 +0,0 @@ -const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); -const { ZERO_ADDRESS } = constants; - -const { expect } = require('chai'); - -const ERC777SenderRecipientMock = artifacts.require('ERC777SenderRecipientMock'); - -function shouldBehaveLikeERC777DirectSendBurn(holder, recipient, data) { - shouldBehaveLikeERC777DirectSend(holder, recipient, data); - shouldBehaveLikeERC777DirectBurn(holder, data); -} - -function shouldBehaveLikeERC777OperatorSendBurn(holder, recipient, operator, data, operatorData) { - shouldBehaveLikeERC777OperatorSend(holder, recipient, operator, data, operatorData); - shouldBehaveLikeERC777OperatorBurn(holder, operator, data, operatorData); -} - -function shouldBehaveLikeERC777UnauthorizedOperatorSendBurn(holder, recipient, operator, data, operatorData) { - shouldBehaveLikeERC777UnauthorizedOperatorSend(holder, recipient, operator, data, operatorData); - shouldBehaveLikeERC777UnauthorizedOperatorBurn(holder, operator, data, operatorData); -} - -function shouldBehaveLikeERC777DirectSend(holder, recipient, data) { - describe('direct send', function () { - context('when the sender has tokens', function () { - shouldDirectSendTokens(holder, recipient, new BN('0'), data); - shouldDirectSendTokens(holder, recipient, new BN('1'), data); - - it('reverts when sending more than the balance', async function () { - const balance = await this.token.balanceOf(holder); - await expectRevert.unspecified(this.token.send(recipient, balance.addn(1), data, { from: holder })); - }); - - it('reverts when sending to the zero address', async function () { - await expectRevert.unspecified(this.token.send(ZERO_ADDRESS, new BN('1'), data, { from: holder })); - }); - }); - - context('when the sender has no tokens', function () { - removeBalance(holder); - - shouldDirectSendTokens(holder, recipient, new BN('0'), data); - - it('reverts when sending a non-zero amount', async function () { - await expectRevert.unspecified(this.token.send(recipient, new BN('1'), data, { from: holder })); - }); - }); - }); -} - -function shouldBehaveLikeERC777OperatorSend(holder, recipient, operator, data, operatorData) { - describe('operator send', function () { - context('when the sender has tokens', async function () { - shouldOperatorSendTokens(holder, operator, recipient, new BN('0'), data, operatorData); - shouldOperatorSendTokens(holder, operator, recipient, new BN('1'), data, operatorData); - - it('reverts when sending more than the balance', async function () { - const balance = await this.token.balanceOf(holder); - await expectRevert.unspecified( - this.token.operatorSend(holder, recipient, balance.addn(1), data, operatorData, { from: operator }), - ); - }); - - it('reverts when sending to the zero address', async function () { - await expectRevert.unspecified( - this.token.operatorSend(holder, ZERO_ADDRESS, new BN('1'), data, operatorData, { from: operator }), - ); - }); - }); - - context('when the sender has no tokens', function () { - removeBalance(holder); - - shouldOperatorSendTokens(holder, operator, recipient, new BN('0'), data, operatorData); - - it('reverts when sending a non-zero amount', async function () { - await expectRevert.unspecified( - this.token.operatorSend(holder, recipient, new BN('1'), data, operatorData, { from: operator }), - ); - }); - - it('reverts when sending from the zero address', async function () { - // This is not yet reflected in the spec - await expectRevert.unspecified( - this.token.operatorSend(ZERO_ADDRESS, recipient, new BN('0'), data, operatorData, { from: operator }), - ); - }); - }); - }); -} - -function shouldBehaveLikeERC777UnauthorizedOperatorSend(holder, recipient, operator, data, operatorData) { - describe('operator send', function () { - it('reverts', async function () { - await expectRevert.unspecified(this.token.operatorSend(holder, recipient, new BN('0'), data, operatorData)); - }); - }); -} - -function shouldBehaveLikeERC777DirectBurn(holder, data) { - describe('direct burn', function () { - context('when the sender has tokens', function () { - shouldDirectBurnTokens(holder, new BN('0'), data); - shouldDirectBurnTokens(holder, new BN('1'), data); - - it('reverts when burning more than the balance', async function () { - const balance = await this.token.balanceOf(holder); - await expectRevert.unspecified(this.token.burn(balance.addn(1), data, { from: holder })); - }); - }); - - context('when the sender has no tokens', function () { - removeBalance(holder); - - shouldDirectBurnTokens(holder, new BN('0'), data); - - it('reverts when burning a non-zero amount', async function () { - await expectRevert.unspecified(this.token.burn(new BN('1'), data, { from: holder })); - }); - }); - }); -} - -function shouldBehaveLikeERC777OperatorBurn(holder, operator, data, operatorData) { - describe('operator burn', function () { - context('when the sender has tokens', async function () { - shouldOperatorBurnTokens(holder, operator, new BN('0'), data, operatorData); - shouldOperatorBurnTokens(holder, operator, new BN('1'), data, operatorData); - - it('reverts when burning more than the balance', async function () { - const balance = await this.token.balanceOf(holder); - await expectRevert.unspecified( - this.token.operatorBurn(holder, balance.addn(1), data, operatorData, { from: operator }), - ); - }); - }); - - context('when the sender has no tokens', function () { - removeBalance(holder); - - shouldOperatorBurnTokens(holder, operator, new BN('0'), data, operatorData); - - it('reverts when burning a non-zero amount', async function () { - await expectRevert.unspecified( - this.token.operatorBurn(holder, new BN('1'), data, operatorData, { from: operator }), - ); - }); - - it('reverts when burning from the zero address', async function () { - // This is not yet reflected in the spec - await expectRevert.unspecified( - this.token.operatorBurn(ZERO_ADDRESS, new BN('0'), data, operatorData, { from: operator }), - ); - }); - }); - }); -} - -function shouldBehaveLikeERC777UnauthorizedOperatorBurn(holder, operator, data, operatorData) { - describe('operator burn', function () { - it('reverts', async function () { - await expectRevert.unspecified(this.token.operatorBurn(holder, new BN('0'), data, operatorData)); - }); - }); -} - -function shouldDirectSendTokens(from, to, amount, data) { - shouldSendTokens(from, null, to, amount, data, null); -} - -function shouldOperatorSendTokens(from, operator, to, amount, data, operatorData) { - shouldSendTokens(from, operator, to, amount, data, operatorData); -} - -function shouldSendTokens(from, operator, to, amount, data, operatorData) { - const operatorCall = operator !== null; - - it(`${operatorCall ? 'operator ' : ''}can send an amount of ${amount}`, async function () { - const initialTotalSupply = await this.token.totalSupply(); - const initialFromBalance = await this.token.balanceOf(from); - const initialToBalance = await this.token.balanceOf(to); - - let receipt; - if (!operatorCall) { - receipt = await this.token.send(to, amount, data, { from }); - expectEvent(receipt, 'Sent', { - operator: from, - from, - to, - amount, - data, - operatorData: null, - }); - } else { - receipt = await this.token.operatorSend(from, to, amount, data, operatorData, { from: operator }); - expectEvent(receipt, 'Sent', { - operator, - from, - to, - amount, - data, - operatorData, - }); - } - - expectEvent(receipt, 'Transfer', { - from, - to, - value: amount, - }); - - const finalTotalSupply = await this.token.totalSupply(); - const finalFromBalance = await this.token.balanceOf(from); - const finalToBalance = await this.token.balanceOf(to); - - expect(finalTotalSupply).to.be.bignumber.equal(initialTotalSupply); - expect(finalToBalance.sub(initialToBalance)).to.be.bignumber.equal(amount); - expect(finalFromBalance.sub(initialFromBalance)).to.be.bignumber.equal(amount.neg()); - }); -} - -function shouldDirectBurnTokens(from, amount, data) { - shouldBurnTokens(from, null, amount, data, null); -} - -function shouldOperatorBurnTokens(from, operator, amount, data, operatorData) { - shouldBurnTokens(from, operator, amount, data, operatorData); -} - -function shouldBurnTokens(from, operator, amount, data, operatorData) { - const operatorCall = operator !== null; - - it(`${operatorCall ? 'operator ' : ''}can burn an amount of ${amount}`, async function () { - const initialTotalSupply = await this.token.totalSupply(); - const initialFromBalance = await this.token.balanceOf(from); - - let receipt; - if (!operatorCall) { - receipt = await this.token.burn(amount, data, { from }); - expectEvent(receipt, 'Burned', { - operator: from, - from, - amount, - data, - operatorData: null, - }); - } else { - receipt = await this.token.operatorBurn(from, amount, data, operatorData, { from: operator }); - expectEvent(receipt, 'Burned', { - operator, - from, - amount, - data, - operatorData, - }); - } - - expectEvent(receipt, 'Transfer', { - from, - to: ZERO_ADDRESS, - value: amount, - }); - - const finalTotalSupply = await this.token.totalSupply(); - const finalFromBalance = await this.token.balanceOf(from); - - expect(finalTotalSupply.sub(initialTotalSupply)).to.be.bignumber.equal(amount.neg()); - expect(finalFromBalance.sub(initialFromBalance)).to.be.bignumber.equal(amount.neg()); - }); -} - -function shouldBehaveLikeERC777InternalMint(recipient, operator, amount, data, operatorData) { - shouldInternalMintTokens(operator, recipient, new BN('0'), data, operatorData); - shouldInternalMintTokens(operator, recipient, amount, data, operatorData); - - it('reverts when minting tokens for the zero address', async function () { - await expectRevert.unspecified( - this.token.$_mint(ZERO_ADDRESS, amount, data, operatorData, true, { from: operator }), - ); - }); -} - -function shouldInternalMintTokens(operator, to, amount, data, operatorData) { - it(`can (internal) mint an amount of ${amount}`, async function () { - const initialTotalSupply = await this.token.totalSupply(); - const initialToBalance = await this.token.balanceOf(to); - - const receipt = await this.token.$_mint(to, amount, data, operatorData, true, { from: operator }); - - expectEvent(receipt, 'Minted', { - operator, - to, - amount, - data, - operatorData, - }); - - expectEvent(receipt, 'Transfer', { - from: ZERO_ADDRESS, - to, - value: amount, - }); - - const finalTotalSupply = await this.token.totalSupply(); - const finalToBalance = await this.token.balanceOf(to); - - expect(finalTotalSupply.sub(initialTotalSupply)).to.be.bignumber.equal(amount); - expect(finalToBalance.sub(initialToBalance)).to.be.bignumber.equal(amount); - }); -} - -function shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData) { - context('when TokensRecipient reverts', function () { - beforeEach(async function () { - await this.tokensRecipientImplementer.setShouldRevertReceive(true); - }); - - it('send reverts', async function () { - await expectRevert.unspecified(sendFromHolder(this.token, this.sender, this.recipient, amount, data)); - }); - - it('operatorSend reverts', async function () { - await expectRevert.unspecified( - this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator }), - ); - }); - - it('mint (internal) reverts', async function () { - await expectRevert.unspecified( - this.token.$_mint(this.recipient, amount, data, operatorData, true, { from: operator }), - ); - }); - }); - - context('when TokensRecipient does not revert', function () { - beforeEach(async function () { - await this.tokensRecipientImplementer.setShouldRevertSend(false); - }); - - it('TokensRecipient receives send data and is called after state mutation', async function () { - const { tx } = await sendFromHolder(this.token, this.sender, this.recipient, amount, data); - - const postSenderBalance = await this.token.balanceOf(this.sender); - const postRecipientBalance = await this.token.balanceOf(this.recipient); - - await assertTokensReceivedCalled( - this.token, - tx, - this.sender, - this.sender, - this.recipient, - amount, - data, - null, - postSenderBalance, - postRecipientBalance, - ); - }); - - it('TokensRecipient receives operatorSend data and is called after state mutation', async function () { - const { tx } = await this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { - from: operator, - }); - - const postSenderBalance = await this.token.balanceOf(this.sender); - const postRecipientBalance = await this.token.balanceOf(this.recipient); - - await assertTokensReceivedCalled( - this.token, - tx, - operator, - this.sender, - this.recipient, - amount, - data, - operatorData, - postSenderBalance, - postRecipientBalance, - ); - }); - - it('TokensRecipient receives mint (internal) data and is called after state mutation', async function () { - const { tx } = await this.token.$_mint(this.recipient, amount, data, operatorData, true, { from: operator }); - - const postRecipientBalance = await this.token.balanceOf(this.recipient); - - await assertTokensReceivedCalled( - this.token, - tx, - operator, - ZERO_ADDRESS, - this.recipient, - amount, - data, - operatorData, - new BN('0'), - postRecipientBalance, - ); - }); - }); -} - -function shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData) { - context('when TokensSender reverts', function () { - beforeEach(async function () { - await this.tokensSenderImplementer.setShouldRevertSend(true); - }); - - it('send reverts', async function () { - await expectRevert.unspecified(sendFromHolder(this.token, this.sender, this.recipient, amount, data)); - }); - - it('operatorSend reverts', async function () { - await expectRevert.unspecified( - this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator }), - ); - }); - - it('burn reverts', async function () { - await expectRevert.unspecified(burnFromHolder(this.token, this.sender, amount, data)); - }); - - it('operatorBurn reverts', async function () { - await expectRevert.unspecified( - this.token.operatorBurn(this.sender, amount, data, operatorData, { from: operator }), - ); - }); - }); - - context('when TokensSender does not revert', function () { - beforeEach(async function () { - await this.tokensSenderImplementer.setShouldRevertSend(false); - }); - - it('TokensSender receives send data and is called before state mutation', async function () { - const preSenderBalance = await this.token.balanceOf(this.sender); - const preRecipientBalance = await this.token.balanceOf(this.recipient); - - const { tx } = await sendFromHolder(this.token, this.sender, this.recipient, amount, data); - - await assertTokensToSendCalled( - this.token, - tx, - this.sender, - this.sender, - this.recipient, - amount, - data, - null, - preSenderBalance, - preRecipientBalance, - ); - }); - - it('TokensSender receives operatorSend data and is called before state mutation', async function () { - const preSenderBalance = await this.token.balanceOf(this.sender); - const preRecipientBalance = await this.token.balanceOf(this.recipient); - - const { tx } = await this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { - from: operator, - }); - - await assertTokensToSendCalled( - this.token, - tx, - operator, - this.sender, - this.recipient, - amount, - data, - operatorData, - preSenderBalance, - preRecipientBalance, - ); - }); - - it('TokensSender receives burn data and is called before state mutation', async function () { - const preSenderBalance = await this.token.balanceOf(this.sender); - - const { tx } = await burnFromHolder(this.token, this.sender, amount, data, { from: this.sender }); - - await assertTokensToSendCalled( - this.token, - tx, - this.sender, - this.sender, - ZERO_ADDRESS, - amount, - data, - null, - preSenderBalance, - ); - }); - - it('TokensSender receives operatorBurn data and is called before state mutation', async function () { - const preSenderBalance = await this.token.balanceOf(this.sender); - - const { tx } = await this.token.operatorBurn(this.sender, amount, data, operatorData, { from: operator }); - - await assertTokensToSendCalled( - this.token, - tx, - operator, - this.sender, - ZERO_ADDRESS, - amount, - data, - operatorData, - preSenderBalance, - ); - }); - }); -} - -function removeBalance(holder) { - beforeEach(async function () { - await this.token.burn(await this.token.balanceOf(holder), '0x', { from: holder }); - expect(await this.token.balanceOf(holder)).to.be.bignumber.equal('0'); - }); -} - -async function assertTokensReceivedCalled( - token, - txHash, - operator, - from, - to, - amount, - data, - operatorData, - fromBalance, - toBalance = '0', -) { - await expectEvent.inTransaction(txHash, ERC777SenderRecipientMock, 'TokensReceivedCalled', { - operator, - from, - to, - amount, - data, - operatorData, - token: token.address, - fromBalance, - toBalance, - }); -} - -async function assertTokensToSendCalled( - token, - txHash, - operator, - from, - to, - amount, - data, - operatorData, - fromBalance, - toBalance = '0', -) { - await expectEvent.inTransaction(txHash, ERC777SenderRecipientMock, 'TokensToSendCalled', { - operator, - from, - to, - amount, - data, - operatorData, - token: token.address, - fromBalance, - toBalance, - }); -} - -async function sendFromHolder(token, holder, to, amount, data) { - if ((await web3.eth.getCode(holder)).length <= '0x'.length) { - return token.send(to, amount, data, { from: holder }); - } else { - // assume holder is ERC777SenderRecipientMock contract - return (await ERC777SenderRecipientMock.at(holder)).send(token.address, to, amount, data); - } -} - -async function burnFromHolder(token, holder, amount, data) { - if ((await web3.eth.getCode(holder)).length <= '0x'.length) { - return token.burn(amount, data, { from: holder }); - } else { - // assume holder is ERC777SenderRecipientMock contract - return (await ERC777SenderRecipientMock.at(holder)).burn(token.address, amount, data); - } -} - -module.exports = { - shouldBehaveLikeERC777DirectSendBurn, - shouldBehaveLikeERC777OperatorSendBurn, - shouldBehaveLikeERC777UnauthorizedOperatorSendBurn, - shouldBehaveLikeERC777InternalMint, - shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook, - shouldBehaveLikeERC777SendBurnWithSendHook, -}; diff --git a/test/token/ERC777/ERC777.test.js b/test/token/ERC777/ERC777.test.js deleted file mode 100644 index 44bc2535149..00000000000 --- a/test/token/ERC777/ERC777.test.js +++ /dev/null @@ -1,556 +0,0 @@ -const { BN, constants, expectEvent, expectRevert, singletons } = require('@openzeppelin/test-helpers'); -const { ZERO_ADDRESS } = constants; - -const { expect } = require('chai'); - -const { - shouldBehaveLikeERC777DirectSendBurn, - shouldBehaveLikeERC777OperatorSendBurn, - shouldBehaveLikeERC777UnauthorizedOperatorSendBurn, - shouldBehaveLikeERC777InternalMint, - shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook, - shouldBehaveLikeERC777SendBurnWithSendHook, -} = require('./ERC777.behavior'); - -const { shouldBehaveLikeERC20, shouldBehaveLikeERC20Approve } = require('../ERC20/ERC20.behavior'); - -const ERC777 = artifacts.require('$ERC777Mock'); -const ERC777SenderRecipientMock = artifacts.require('$ERC777SenderRecipientMock'); - -contract('ERC777', function (accounts) { - const [registryFunder, holder, defaultOperatorA, defaultOperatorB, newOperator, anyone] = accounts; - - const initialSupply = new BN('10000'); - const name = 'ERC777Test'; - const symbol = '777T'; - const data = web3.utils.sha3('OZ777TestData'); - const operatorData = web3.utils.sha3('OZ777TestOperatorData'); - - const defaultOperators = [defaultOperatorA, defaultOperatorB]; - - beforeEach(async function () { - this.erc1820 = await singletons.ERC1820Registry(registryFunder); - }); - - context('with default operators', function () { - beforeEach(async function () { - this.token = await ERC777.new(name, symbol, defaultOperators); - await this.token.$_mint(holder, initialSupply, '0x', '0x'); - }); - - describe('as an ERC20 token', function () { - shouldBehaveLikeERC20('ERC777', initialSupply, holder, anyone, defaultOperatorA); - - describe('_approve', function () { - shouldBehaveLikeERC20Approve('ERC777', holder, anyone, initialSupply, function (owner, spender, amount) { - return this.token.$_approve(owner, spender, amount); - }); - - describe('when the owner is the zero address', function () { - it('reverts', async function () { - await expectRevert( - this.token.$_approve(ZERO_ADDRESS, anyone, initialSupply), - 'ERC777: approve from the zero address', - ); - }); - }); - }); - }); - - it('does not emit AuthorizedOperator events for default operators', async function () { - await expectEvent.notEmitted.inConstruction(this.token, 'AuthorizedOperator'); - }); - - describe('basic information', function () { - it('returns the name', async function () { - expect(await this.token.name()).to.equal(name); - }); - - it('returns the symbol', async function () { - expect(await this.token.symbol()).to.equal(symbol); - }); - - it('returns a granularity of 1', async function () { - expect(await this.token.granularity()).to.be.bignumber.equal('1'); - }); - - it('returns the default operators', async function () { - expect(await this.token.defaultOperators()).to.deep.equal(defaultOperators); - }); - - it('default operators are operators for all accounts', async function () { - for (const operator of defaultOperators) { - expect(await this.token.isOperatorFor(operator, anyone)).to.equal(true); - } - }); - - it('returns the total supply', async function () { - expect(await this.token.totalSupply()).to.be.bignumber.equal(initialSupply); - }); - - it('returns 18 when decimals is called', async function () { - expect(await this.token.decimals()).to.be.bignumber.equal('18'); - }); - - it('the ERC777Token interface is registered in the registry', async function () { - expect( - await this.erc1820.getInterfaceImplementer(this.token.address, web3.utils.soliditySha3('ERC777Token')), - ).to.equal(this.token.address); - }); - - it('the ERC20Token interface is registered in the registry', async function () { - expect( - await this.erc1820.getInterfaceImplementer(this.token.address, web3.utils.soliditySha3('ERC20Token')), - ).to.equal(this.token.address); - }); - }); - - describe('balanceOf', function () { - context('for an account with no tokens', function () { - it('returns zero', async function () { - expect(await this.token.balanceOf(anyone)).to.be.bignumber.equal('0'); - }); - }); - - context('for an account with tokens', function () { - it('returns their balance', async function () { - expect(await this.token.balanceOf(holder)).to.be.bignumber.equal(initialSupply); - }); - }); - }); - - context('with no ERC777TokensSender and no ERC777TokensRecipient implementers', function () { - describe('send/burn', function () { - shouldBehaveLikeERC777DirectSendBurn(holder, anyone, data); - - context('with self operator', function () { - shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, holder, data, operatorData); - }); - - context('with first default operator', function () { - shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, defaultOperatorA, data, operatorData); - }); - - context('with second default operator', function () { - shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, defaultOperatorB, data, operatorData); - }); - - context('before authorizing a new operator', function () { - shouldBehaveLikeERC777UnauthorizedOperatorSendBurn(holder, anyone, newOperator, data, operatorData); - }); - - context('with new authorized operator', function () { - beforeEach(async function () { - await this.token.authorizeOperator(newOperator, { from: holder }); - }); - - shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, newOperator, data, operatorData); - - context('with revoked operator', function () { - beforeEach(async function () { - await this.token.revokeOperator(newOperator, { from: holder }); - }); - - shouldBehaveLikeERC777UnauthorizedOperatorSendBurn(holder, anyone, newOperator, data, operatorData); - }); - }); - }); - - describe('mint (internal)', function () { - const to = anyone; - const amount = new BN('5'); - - context('with default operator', function () { - const operator = defaultOperatorA; - - shouldBehaveLikeERC777InternalMint(to, operator, amount, data, operatorData); - }); - - context('with non operator', function () { - const operator = newOperator; - - shouldBehaveLikeERC777InternalMint(to, operator, amount, data, operatorData); - }); - }); - - describe('mint (internal extended)', function () { - const amount = new BN('5'); - - context('to anyone', function () { - beforeEach(async function () { - this.recipient = anyone; - }); - - context('with default operator', function () { - const operator = defaultOperatorA; - - it('without requireReceptionAck', async function () { - await this.token.$_mint(this.recipient, amount, data, operatorData, false, { from: operator }); - }); - - it('with requireReceptionAck', async function () { - await this.token.$_mint(this.recipient, amount, data, operatorData, true, { from: operator }); - }); - }); - - context('with non operator', function () { - const operator = newOperator; - - it('without requireReceptionAck', async function () { - await this.token.$_mint(this.recipient, amount, data, operatorData, false, { from: operator }); - }); - - it('with requireReceptionAck', async function () { - await this.token.$_mint(this.recipient, amount, data, operatorData, true, { from: operator }); - }); - }); - }); - - context('to non ERC777TokensRecipient implementer', function () { - beforeEach(async function () { - this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); - this.recipient = this.tokensRecipientImplementer.address; - }); - - context('with default operator', function () { - const operator = defaultOperatorA; - - it('without requireReceptionAck', async function () { - await this.token.$_mint(this.recipient, amount, data, operatorData, false, { from: operator }); - }); - - it('with requireReceptionAck', async function () { - await expectRevert( - this.token.$_mint(this.recipient, amount, data, operatorData, true, { from: operator }), - 'ERC777: token recipient contract has no implementer for ERC777TokensRecipient', - ); - }); - }); - - context('with non operator', function () { - const operator = newOperator; - - it('without requireReceptionAck', async function () { - await this.token.$_mint(this.recipient, amount, data, operatorData, false, { from: operator }); - }); - - it('with requireReceptionAck', async function () { - await expectRevert( - this.token.$_mint(this.recipient, amount, data, operatorData, true, { from: operator }), - 'ERC777: token recipient contract has no implementer for ERC777TokensRecipient', - ); - }); - }); - }); - }); - }); - - describe('operator management', function () { - it('accounts are their own operator', async function () { - expect(await this.token.isOperatorFor(holder, holder)).to.equal(true); - }); - - it('reverts when self-authorizing', async function () { - await expectRevert( - this.token.authorizeOperator(holder, { from: holder }), - 'ERC777: authorizing self as operator', - ); - }); - - it('reverts when self-revoking', async function () { - await expectRevert(this.token.revokeOperator(holder, { from: holder }), 'ERC777: revoking self as operator'); - }); - - it('non-operators can be revoked', async function () { - expect(await this.token.isOperatorFor(newOperator, holder)).to.equal(false); - - const receipt = await this.token.revokeOperator(newOperator, { from: holder }); - expectEvent(receipt, 'RevokedOperator', { operator: newOperator, tokenHolder: holder }); - - expect(await this.token.isOperatorFor(newOperator, holder)).to.equal(false); - }); - - it('non-operators can be authorized', async function () { - expect(await this.token.isOperatorFor(newOperator, holder)).to.equal(false); - - const receipt = await this.token.authorizeOperator(newOperator, { from: holder }); - expectEvent(receipt, 'AuthorizedOperator', { operator: newOperator, tokenHolder: holder }); - - expect(await this.token.isOperatorFor(newOperator, holder)).to.equal(true); - }); - - describe('new operators', function () { - beforeEach(async function () { - await this.token.authorizeOperator(newOperator, { from: holder }); - }); - - it('are not added to the default operators list', async function () { - expect(await this.token.defaultOperators()).to.deep.equal(defaultOperators); - }); - - it('can be re-authorized', async function () { - const receipt = await this.token.authorizeOperator(newOperator, { from: holder }); - expectEvent(receipt, 'AuthorizedOperator', { operator: newOperator, tokenHolder: holder }); - - expect(await this.token.isOperatorFor(newOperator, holder)).to.equal(true); - }); - - it('can be revoked', async function () { - const receipt = await this.token.revokeOperator(newOperator, { from: holder }); - expectEvent(receipt, 'RevokedOperator', { operator: newOperator, tokenHolder: holder }); - - expect(await this.token.isOperatorFor(newOperator, holder)).to.equal(false); - }); - }); - - describe('default operators', function () { - it('can be re-authorized', async function () { - const receipt = await this.token.authorizeOperator(defaultOperatorA, { from: holder }); - expectEvent(receipt, 'AuthorizedOperator', { operator: defaultOperatorA, tokenHolder: holder }); - - expect(await this.token.isOperatorFor(defaultOperatorA, holder)).to.equal(true); - }); - - it('can be revoked', async function () { - const receipt = await this.token.revokeOperator(defaultOperatorA, { from: holder }); - expectEvent(receipt, 'RevokedOperator', { operator: defaultOperatorA, tokenHolder: holder }); - - expect(await this.token.isOperatorFor(defaultOperatorA, holder)).to.equal(false); - }); - - it('cannot be revoked for themselves', async function () { - await expectRevert( - this.token.revokeOperator(defaultOperatorA, { from: defaultOperatorA }), - 'ERC777: revoking self as operator', - ); - }); - - context('with revoked default operator', function () { - beforeEach(async function () { - await this.token.revokeOperator(defaultOperatorA, { from: holder }); - }); - - it('default operator is not revoked for other holders', async function () { - expect(await this.token.isOperatorFor(defaultOperatorA, anyone)).to.equal(true); - }); - - it('other default operators are not revoked', async function () { - expect(await this.token.isOperatorFor(defaultOperatorB, holder)).to.equal(true); - }); - - it('default operators list is not modified', async function () { - expect(await this.token.defaultOperators()).to.deep.equal(defaultOperators); - }); - - it('revoked default operator can be re-authorized', async function () { - const receipt = await this.token.authorizeOperator(defaultOperatorA, { from: holder }); - expectEvent(receipt, 'AuthorizedOperator', { operator: defaultOperatorA, tokenHolder: holder }); - - expect(await this.token.isOperatorFor(defaultOperatorA, holder)).to.equal(true); - }); - }); - }); - }); - - describe('send and receive hooks', function () { - const amount = new BN('1'); - const operator = defaultOperatorA; - // sender and recipient are stored inside 'this', since in some tests their addresses are determined dynamically - - describe('tokensReceived', function () { - beforeEach(function () { - this.sender = holder; - }); - - context('with no ERC777TokensRecipient implementer', function () { - context('with contract recipient', function () { - beforeEach(async function () { - this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); - this.recipient = this.tokensRecipientImplementer.address; - - // Note that tokensRecipientImplementer doesn't implement the recipient interface for the recipient - }); - - it('send reverts', async function () { - await expectRevert( - this.token.send(this.recipient, amount, data, { from: holder }), - 'ERC777: token recipient contract has no implementer for ERC777TokensRecipient', - ); - }); - - it('operatorSend reverts', async function () { - await expectRevert( - this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator }), - 'ERC777: token recipient contract has no implementer for ERC777TokensRecipient', - ); - }); - - it('mint (internal) reverts', async function () { - await expectRevert( - this.token.$_mint(this.recipient, amount, data, operatorData, true, { from: operator }), - 'ERC777: token recipient contract has no implementer for ERC777TokensRecipient', - ); - }); - - it('(ERC20) transfer succeeds', async function () { - await this.token.transfer(this.recipient, amount, { from: holder }); - }); - - it('(ERC20) transferFrom succeeds', async function () { - const approved = anyone; - await this.token.approve(approved, amount, { from: this.sender }); - await this.token.transferFrom(this.sender, this.recipient, amount, { from: approved }); - }); - }); - }); - - context('with ERC777TokensRecipient implementer', function () { - context('with contract as implementer for an externally owned account', function () { - beforeEach(async function () { - this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); - this.recipient = anyone; - - await this.tokensRecipientImplementer.recipientFor(this.recipient); - - await this.erc1820.setInterfaceImplementer( - this.recipient, - web3.utils.soliditySha3('ERC777TokensRecipient'), - this.tokensRecipientImplementer.address, - { from: this.recipient }, - ); - }); - - shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData); - }); - - context('with contract as implementer for another contract', function () { - beforeEach(async function () { - this.recipientContract = await ERC777SenderRecipientMock.new(); - this.recipient = this.recipientContract.address; - - this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); - await this.tokensRecipientImplementer.recipientFor(this.recipient); - await this.recipientContract.registerRecipient(this.tokensRecipientImplementer.address); - }); - - shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData); - }); - - context('with contract as implementer for itself', function () { - beforeEach(async function () { - this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); - this.recipient = this.tokensRecipientImplementer.address; - - await this.tokensRecipientImplementer.recipientFor(this.recipient); - }); - - shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData); - }); - }); - }); - - describe('tokensToSend', function () { - beforeEach(function () { - this.recipient = anyone; - }); - - context('with a contract as implementer for an externally owned account', function () { - beforeEach(async function () { - this.tokensSenderImplementer = await ERC777SenderRecipientMock.new(); - this.sender = holder; - - await this.tokensSenderImplementer.senderFor(this.sender); - - await this.erc1820.setInterfaceImplementer( - this.sender, - web3.utils.soliditySha3('ERC777TokensSender'), - this.tokensSenderImplementer.address, - { from: this.sender }, - ); - }); - - shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData); - }); - - context('with contract as implementer for another contract', function () { - beforeEach(async function () { - this.senderContract = await ERC777SenderRecipientMock.new(); - this.sender = this.senderContract.address; - - this.tokensSenderImplementer = await ERC777SenderRecipientMock.new(); - await this.tokensSenderImplementer.senderFor(this.sender); - await this.senderContract.registerSender(this.tokensSenderImplementer.address); - - // For the contract to be able to receive tokens (that it can later send), it must also implement the - // recipient interface. - - await this.senderContract.recipientFor(this.sender); - await this.token.send(this.sender, amount, data, { from: holder }); - }); - - shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData); - }); - - context('with a contract as implementer for itself', function () { - beforeEach(async function () { - this.tokensSenderImplementer = await ERC777SenderRecipientMock.new(); - this.sender = this.tokensSenderImplementer.address; - - await this.tokensSenderImplementer.senderFor(this.sender); - - // For the contract to be able to receive tokens (that it can later send), it must also implement the - // recipient interface. - - await this.tokensSenderImplementer.recipientFor(this.sender); - await this.token.send(this.sender, amount, data, { from: holder }); - }); - - shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData); - }); - }); - }); - }); - - context('with no default operators', function () { - beforeEach(async function () { - this.token = await ERC777.new(name, symbol, []); - }); - - it('default operators list is empty', async function () { - expect(await this.token.defaultOperators()).to.deep.equal([]); - }); - }); - - describe('relative order of hooks', function () { - beforeEach(async function () { - await singletons.ERC1820Registry(registryFunder); - this.sender = await ERC777SenderRecipientMock.new(); - await this.sender.registerRecipient(this.sender.address); - await this.sender.registerSender(this.sender.address); - this.token = await ERC777.new(name, symbol, []); - await this.token.$_mint(this.sender.address, 1, '0x', '0x'); - }); - - it('send', async function () { - const { receipt } = await this.sender.send(this.token.address, anyone, 1, '0x'); - - const internalBeforeHook = receipt.logs.findIndex(l => l.event === 'BeforeTokenTransfer'); - expect(internalBeforeHook).to.be.gte(0); - const externalSendHook = receipt.logs.findIndex(l => l.event === 'TokensToSendCalled'); - expect(externalSendHook).to.be.gte(0); - - expect(externalSendHook).to.be.lt(internalBeforeHook); - }); - - it('burn', async function () { - const { receipt } = await this.sender.burn(this.token.address, 1, '0x'); - - const internalBeforeHook = receipt.logs.findIndex(l => l.event === 'BeforeTokenTransfer'); - expect(internalBeforeHook).to.be.gte(0); - const externalSendHook = receipt.logs.findIndex(l => l.event === 'TokensToSendCalled'); - expect(externalSendHook).to.be.gte(0); - - expect(externalSendHook).to.be.lt(internalBeforeHook); - }); - }); -}); diff --git a/test/utils/Checkpoints.t.sol b/test/utils/Checkpoints.t.sol index f2cb587d57c..12598da46e5 100644 --- a/test/utils/Checkpoints.t.sol +++ b/test/utils/Checkpoints.t.sol @@ -7,129 +7,6 @@ import "forge-std/Test.sol"; import "../../contracts/utils/Checkpoints.sol"; import "../../contracts/utils/math/SafeCast.sol"; -contract CheckpointsHistoryTest is Test { - using Checkpoints for Checkpoints.History; - - // Maximum gap between keys used during the fuzzing tests: the `_prepareKeys` function with make sure that - // key#n+1 is in the [key#n, key#n + _KEY_MAX_GAP] range. - uint8 internal constant _KEY_MAX_GAP = 64; - - Checkpoints.History internal _ckpts; - - // helpers - function _boundUint32(uint32 x, uint32 min, uint32 max) internal view returns (uint32) { - return SafeCast.toUint32(bound(uint256(x), uint256(min), uint256(max))); - } - - function _prepareKeys(uint32[] memory keys, uint32 maxSpread) internal view { - uint32 lastKey = 0; - for (uint256 i = 0; i < keys.length; ++i) { - uint32 key = _boundUint32(keys[i], lastKey, lastKey + maxSpread); - keys[i] = key; - lastKey = key; - } - } - - function _assertLatestCheckpoint(bool exist, uint32 key, uint224 value) internal { - (bool _exist, uint32 _key, uint224 _value) = _ckpts.latestCheckpoint(); - assertEq(_exist, exist); - assertEq(_key, key); - assertEq(_value, value); - } - - // tests - function testPush(uint32[] memory keys, uint224[] memory values, uint32 pastKey) public { - vm.assume(values.length > 0 && values.length <= keys.length); - _prepareKeys(keys, _KEY_MAX_GAP); - - // initial state - assertEq(_ckpts.length(), 0); - assertEq(_ckpts.latest(), 0); - _assertLatestCheckpoint(false, 0, 0); - - uint256 duplicates = 0; - for (uint256 i = 0; i < keys.length; ++i) { - uint32 key = keys[i]; - uint224 value = values[i % values.length]; - if (i > 0 && key == keys[i - 1]) ++duplicates; - - // push - vm.roll(key); - _ckpts.push(value); - - // check length & latest - assertEq(_ckpts.length(), i + 1 - duplicates); - assertEq(_ckpts.latest(), value); - _assertLatestCheckpoint(true, key, value); - } - - // Can't push any key in the past - if (keys.length > 0) { - uint32 lastKey = keys[keys.length - 1]; - if (lastKey > 0) { - pastKey = _boundUint32(pastKey, 0, lastKey - 1); - - vm.roll(pastKey); - vm.expectRevert(); - this.push(values[keys.length % values.length]); - } - } - } - - // used to test reverts - function push(uint224 value) external { - _ckpts.push(value); - } - - function testLookup(uint32[] memory keys, uint224[] memory values, uint32 lookup) public { - vm.assume(keys.length > 0); - vm.assume(values.length > 0 && values.length <= keys.length); - _prepareKeys(keys, _KEY_MAX_GAP); - - uint32 lastKey = keys[keys.length - 1]; - vm.assume(lastKey > 0); - lookup = _boundUint32(lookup, 0, lastKey - 1); - - uint224 upper = 0; - for (uint256 i = 0; i < keys.length; ++i) { - uint32 key = keys[i]; - uint224 value = values[i % values.length]; - - // push - vm.roll(key); - _ckpts.push(value); - - // track expected result of lookups - if (key <= lookup) { - upper = value; - } - } - - // check lookup - assertEq(_ckpts.getAtBlock(lookup), upper); - assertEq(_ckpts.getAtProbablyRecentBlock(lookup), upper); - - vm.expectRevert(); - this.getAtBlock(lastKey); - vm.expectRevert(); - this.getAtBlock(lastKey + 1); - vm.expectRevert(); - this.getAtProbablyRecentBlock(lastKey); - vm.expectRevert(); - this.getAtProbablyRecentBlock(lastKey + 1); - } - - // used to test reverts - function getAtBlock(uint32 key) external view { - _ckpts.getAtBlock(key); - } - - // used to test reverts - function getAtProbablyRecentBlock(uint32 key) external view { - _ckpts.getAtProbablyRecentBlock(key); - } -} - contract CheckpointsTrace224Test is Test { using Checkpoints for Checkpoints.Trace224; diff --git a/test/utils/Checkpoints.test.js b/test/utils/Checkpoints.test.js index f395663befa..d9fd2c8ea8a 100644 --- a/test/utils/Checkpoints.test.js +++ b/test/utils/Checkpoints.test.js @@ -1,166 +1,51 @@ -const { expectRevert, time } = require('@openzeppelin/test-helpers'); - +const { expectRevert } = require('@openzeppelin/test-helpers'); const { expect } = require('chai'); -const { batchInBlock } = require('../helpers/txpool'); +const { VALUE_SIZES } = require('../../scripts/generate/templates/Checkpoints.opts.js'); const $Checkpoints = artifacts.require('$Checkpoints'); // The library name may be 'Checkpoints' or 'CheckpointsUpgradeable' const libraryName = $Checkpoints._json.contractName.replace(/^\$/, ''); -const traceLengths = [160, 224]; - const first = array => (array.length ? array[0] : undefined); const last = array => (array.length ? array[array.length - 1] : undefined); contract('Checkpoints', function () { beforeEach(async function () { this.mock = await $Checkpoints.new(); - - this.methods = { trace: {} }; - this.methods.history = { - latest: (...args) => this.mock.methods[`$latest_${libraryName}_History(uint256)`](0, ...args), - latestCheckpoint: (...args) => this.mock.methods[`$latestCheckpoint_${libraryName}_History(uint256)`](0, ...args), - length: (...args) => this.mock.methods[`$length_${libraryName}_History(uint256)`](0, ...args), - push: (...args) => this.mock.methods['$push(uint256,uint256)'](0, ...args), - getAtBlock: (...args) => this.mock.$getAtBlock(0, ...args), - getAtRecentBlock: (...args) => this.mock.$getAtProbablyRecentBlock(0, ...args), - }; - for (const length of traceLengths) { - this.methods.trace[length] = { - latest: (...args) => this.mock.methods[`$latest_${libraryName}_Trace${length}(uint256)`](0, ...args), - latestCheckpoint: (...args) => - this.mock.methods[`$latestCheckpoint_${libraryName}_Trace${length}(uint256)`](0, ...args), - length: (...args) => this.mock.methods[`$length_${libraryName}_Trace${length}(uint256)`](0, ...args), - push: (...args) => this.mock.methods[`$push(uint256,uint${256 - length},uint${length})`](0, ...args), - lowerLookup: (...args) => this.mock.methods[`$lowerLookup(uint256,uint${256 - length})`](0, ...args), - upperLookup: (...args) => this.mock.methods[`$upperLookup(uint256,uint${256 - length})`](0, ...args), - upperLookupRecent: (...args) => - this.mock.methods[`$upperLookupRecent(uint256,uint${256 - length})`](0, ...args), - }; - } }); - describe('History checkpoints', function () { - describe('without checkpoints', function () { - it('returns zero as latest value', async function () { - expect(await this.methods.history.latest()).to.be.bignumber.equal('0'); - - const ckpt = await this.methods.history.latestCheckpoint(); - expect(ckpt[0]).to.be.equal(false); - expect(ckpt[1]).to.be.bignumber.equal('0'); - expect(ckpt[2]).to.be.bignumber.equal('0'); - }); - - it('returns zero as past value', async function () { - await time.advanceBlock(); - expect(await this.methods.history.getAtBlock((await web3.eth.getBlockNumber()) - 1)).to.be.bignumber.equal('0'); - expect( - await this.methods.history.getAtRecentBlock((await web3.eth.getBlockNumber()) - 1), - ).to.be.bignumber.equal('0'); - }); - }); - - describe('with checkpoints', function () { - beforeEach('pushing checkpoints', async function () { - this.tx1 = await this.methods.history.push(1); - this.tx2 = await this.methods.history.push(2); - await time.advanceBlock(); - this.tx3 = await this.methods.history.push(3); - await time.advanceBlock(); - await time.advanceBlock(); - }); - - it('returns latest value', async function () { - expect(await this.methods.history.latest()).to.be.bignumber.equal('3'); - - const ckpt = await this.methods.history.latestCheckpoint(); - expect(ckpt[0]).to.be.equal(true); - expect(ckpt[1]).to.be.bignumber.equal(web3.utils.toBN(this.tx3.receipt.blockNumber)); - expect(ckpt[2]).to.be.bignumber.equal(web3.utils.toBN('3')); - }); - - for (const getAtBlockVariant of ['getAtBlock', 'getAtRecentBlock']) { - describe(`lookup: ${getAtBlockVariant}`, function () { - it('returns past values', async function () { - expect( - await this.methods.history[getAtBlockVariant](this.tx1.receipt.blockNumber - 1), - ).to.be.bignumber.equal('0'); - expect(await this.methods.history[getAtBlockVariant](this.tx1.receipt.blockNumber)).to.be.bignumber.equal( - '1', - ); - expect(await this.methods.history[getAtBlockVariant](this.tx2.receipt.blockNumber)).to.be.bignumber.equal( - '2', - ); - // Block with no new checkpoints - expect( - await this.methods.history[getAtBlockVariant](this.tx2.receipt.blockNumber + 1), - ).to.be.bignumber.equal('2'); - expect(await this.methods.history[getAtBlockVariant](this.tx3.receipt.blockNumber)).to.be.bignumber.equal( - '3', - ); - expect( - await this.methods.history[getAtBlockVariant](this.tx3.receipt.blockNumber + 1), - ).to.be.bignumber.equal('3'); - }); - it('reverts if block number >= current block', async function () { - await expectRevert( - this.methods.history[getAtBlockVariant](await web3.eth.getBlockNumber()), - 'Checkpoints: block not yet mined', - ); - - await expectRevert( - this.methods.history[getAtBlockVariant]((await web3.eth.getBlockNumber()) + 1), - 'Checkpoints: block not yet mined', - ); - }); - }); - } - - it('multiple checkpoints in the same block', async function () { - const lengthBefore = await this.methods.history.length(); - - await batchInBlock([ - () => this.methods.history.push(8, { gas: 100000 }), - () => this.methods.history.push(9, { gas: 100000 }), - () => this.methods.history.push(10, { gas: 100000 }), - ]); - - expect(await this.methods.history.length()).to.be.bignumber.equal(lengthBefore.addn(1)); - expect(await this.methods.history.latest()).to.be.bignumber.equal('10'); - }); - - it('more than 5 checkpoints', async function () { - for (let i = 4; i <= 6; i++) { - await this.methods.history.push(i); - } - expect(await this.methods.history.length()).to.be.bignumber.equal('6'); - const block = await web3.eth.getBlockNumber(); - // recent - expect(await this.methods.history.getAtRecentBlock(block - 1)).to.be.bignumber.equal('5'); - // non-recent - expect(await this.methods.history.getAtRecentBlock(block - 9)).to.be.bignumber.equal('0'); + for (const length of VALUE_SIZES) { + describe(`Trace${length}`, function () { + beforeEach(async function () { + this.methods = { + latest: (...args) => this.mock.methods[`$latest_${libraryName}_Trace${length}(uint256)`](0, ...args), + latestCheckpoint: (...args) => + this.mock.methods[`$latestCheckpoint_${libraryName}_Trace${length}(uint256)`](0, ...args), + length: (...args) => this.mock.methods[`$length_${libraryName}_Trace${length}(uint256)`](0, ...args), + push: (...args) => this.mock.methods[`$push(uint256,uint${256 - length},uint${length})`](0, ...args), + lowerLookup: (...args) => this.mock.methods[`$lowerLookup(uint256,uint${256 - length})`](0, ...args), + upperLookup: (...args) => this.mock.methods[`$upperLookup(uint256,uint${256 - length})`](0, ...args), + upperLookupRecent: (...args) => + this.mock.methods[`$upperLookupRecent(uint256,uint${256 - length})`](0, ...args), + }; }); - }); - }); - for (const length of traceLengths) { - describe(`Trace${length}`, function () { describe('without checkpoints', function () { it('returns zero as latest value', async function () { - expect(await this.methods.trace[length].latest()).to.be.bignumber.equal('0'); + expect(await this.methods.latest()).to.be.bignumber.equal('0'); - const ckpt = await this.methods.trace[length].latestCheckpoint(); + const ckpt = await this.methods.latestCheckpoint(); expect(ckpt[0]).to.be.equal(false); expect(ckpt[1]).to.be.bignumber.equal('0'); expect(ckpt[2]).to.be.bignumber.equal('0'); }); it('lookup returns 0', async function () { - expect(await this.methods.trace[length].lowerLookup(0)).to.be.bignumber.equal('0'); - expect(await this.methods.trace[length].upperLookup(0)).to.be.bignumber.equal('0'); - expect(await this.methods.trace[length].upperLookupRecent(0)).to.be.bignumber.equal('0'); + expect(await this.methods.lowerLookup(0)).to.be.bignumber.equal('0'); + expect(await this.methods.upperLookup(0)).to.be.bignumber.equal('0'); + expect(await this.methods.upperLookupRecent(0)).to.be.bignumber.equal('0'); }); }); @@ -174,49 +59,46 @@ contract('Checkpoints', function () { { key: '11', value: '99' }, ]; for (const { key, value } of this.checkpoints) { - await this.methods.trace[length].push(key, value); + await this.methods.push(key, value); } }); it('length', async function () { - expect(await this.methods.trace[length].length()).to.be.bignumber.equal(this.checkpoints.length.toString()); + expect(await this.methods.length()).to.be.bignumber.equal(this.checkpoints.length.toString()); }); it('returns latest value', async function () { - expect(await this.methods.trace[length].latest()).to.be.bignumber.equal(last(this.checkpoints).value); + expect(await this.methods.latest()).to.be.bignumber.equal(last(this.checkpoints).value); - const ckpt = await this.methods.trace[length].latestCheckpoint(); + const ckpt = await this.methods.latestCheckpoint(); expect(ckpt[0]).to.be.equal(true); expect(ckpt[1]).to.be.bignumber.equal(last(this.checkpoints).key); expect(ckpt[2]).to.be.bignumber.equal(last(this.checkpoints).value); }); it('cannot push values in the past', async function () { - await expectRevert( - this.methods.trace[length].push(last(this.checkpoints).key - 1, '0'), - 'Checkpoint: decreasing keys', - ); + await expectRevert(this.methods.push(last(this.checkpoints).key - 1, '0'), 'Checkpoint: decreasing keys'); }); it('can update last value', async function () { const newValue = '42'; // check length before the update - expect(await this.methods.trace[length].length()).to.be.bignumber.equal(this.checkpoints.length.toString()); + expect(await this.methods.length()).to.be.bignumber.equal(this.checkpoints.length.toString()); // update last key - await this.methods.trace[length].push(last(this.checkpoints).key, newValue); - expect(await this.methods.trace[length].latest()).to.be.bignumber.equal(newValue); + await this.methods.push(last(this.checkpoints).key, newValue); + expect(await this.methods.latest()).to.be.bignumber.equal(newValue); // check that length did not change - expect(await this.methods.trace[length].length()).to.be.bignumber.equal(this.checkpoints.length.toString()); + expect(await this.methods.length()).to.be.bignumber.equal(this.checkpoints.length.toString()); }); it('lower lookup', async function () { for (let i = 0; i < 14; ++i) { const value = first(this.checkpoints.filter(x => i <= x.key))?.value || '0'; - expect(await this.methods.trace[length].lowerLookup(i)).to.be.bignumber.equal(value); + expect(await this.methods.lowerLookup(i)).to.be.bignumber.equal(value); } }); @@ -224,8 +106,8 @@ contract('Checkpoints', function () { for (let i = 0; i < 14; ++i) { const value = last(this.checkpoints.filter(x => i >= x.key))?.value || '0'; - expect(await this.methods.trace[length].upperLookup(i)).to.be.bignumber.equal(value); - expect(await this.methods.trace[length].upperLookupRecent(i)).to.be.bignumber.equal(value); + expect(await this.methods.upperLookup(i)).to.be.bignumber.equal(value); + expect(await this.methods.upperLookupRecent(i)).to.be.bignumber.equal(value); } }); @@ -240,13 +122,13 @@ contract('Checkpoints', function () { const allCheckpoints = [].concat(this.checkpoints, moreCheckpoints); for (const { key, value } of moreCheckpoints) { - await this.methods.trace[length].push(key, value); + await this.methods.push(key, value); } for (let i = 0; i < 25; ++i) { const value = last(allCheckpoints.filter(x => i >= x.key))?.value || '0'; - expect(await this.methods.trace[length].upperLookup(i)).to.be.bignumber.equal(value); - expect(await this.methods.trace[length].upperLookupRecent(i)).to.be.bignumber.equal(value); + expect(await this.methods.upperLookup(i)).to.be.bignumber.equal(value); + expect(await this.methods.upperLookupRecent(i)).to.be.bignumber.equal(value); } }); }); diff --git a/test/utils/Create2.test.js b/test/utils/Create2.test.js index 2fc27dc15a6..526602600fd 100644 --- a/test/utils/Create2.test.js +++ b/test/utils/Create2.test.js @@ -4,7 +4,10 @@ const { expect } = require('chai'); const Create2 = artifacts.require('$Create2'); const VestingWallet = artifacts.require('VestingWallet'); -const ERC1820Implementer = artifacts.require('$ERC1820Implementer'); +// This should be a contract that: +// - has no constructor arguments +// - has no immutable variable populated during construction +const ConstructorLessContract = Create2; contract('Create2', function (accounts) { const [deployerAccount, other] = accounts; @@ -38,14 +41,14 @@ contract('Create2', function (accounts) { }); describe('deploy', function () { - it('deploys a ERC1820Implementer from inline assembly code', async function () { - const offChainComputed = computeCreate2Address(saltHex, ERC1820Implementer.bytecode, this.factory.address); + it('deploys a contract without constructor', async function () { + const offChainComputed = computeCreate2Address(saltHex, ConstructorLessContract.bytecode, this.factory.address); - expectEvent(await this.factory.$deploy(0, saltHex, ERC1820Implementer.bytecode), 'return$deploy', { + expectEvent(await this.factory.$deploy(0, saltHex, ConstructorLessContract.bytecode), 'return$deploy', { addr: offChainComputed, }); - expect(ERC1820Implementer.bytecode).to.include((await web3.eth.getCode(offChainComputed)).slice(2)); + expect(ConstructorLessContract.bytecode).to.include((await web3.eth.getCode(offChainComputed)).slice(2)); }); it('deploys a contract with constructor arguments', async function () { diff --git a/test/utils/TimersBlockNumberImpl.test.js b/test/utils/TimersBlockNumberImpl.test.js deleted file mode 100644 index ee0d54bfb46..00000000000 --- a/test/utils/TimersBlockNumberImpl.test.js +++ /dev/null @@ -1,55 +0,0 @@ -const { BN, time } = require('@openzeppelin/test-helpers'); -const { expect } = require('chai'); - -const TimersBlockNumberImpl = artifacts.require('TimersBlockNumberImpl'); - -contract('TimersBlockNumber', function () { - beforeEach(async function () { - this.instance = await TimersBlockNumberImpl.new(); - this.now = await web3.eth.getBlock('latest').then(({ number }) => number); - }); - - it('unset', async function () { - expect(await this.instance.getDeadline()).to.be.bignumber.equal('0'); - expect(await this.instance.isUnset()).to.be.equal(true); - expect(await this.instance.isStarted()).to.be.equal(false); - expect(await this.instance.isPending()).to.be.equal(false); - expect(await this.instance.isExpired()).to.be.equal(false); - }); - - it('pending', async function () { - await this.instance.setDeadline(this.now + 3); - expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(this.now + 3)); - expect(await this.instance.isUnset()).to.be.equal(false); - expect(await this.instance.isStarted()).to.be.equal(true); - expect(await this.instance.isPending()).to.be.equal(true); - expect(await this.instance.isExpired()).to.be.equal(false); - }); - - it('expired', async function () { - await this.instance.setDeadline(this.now - 3); - expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(this.now - 3)); - expect(await this.instance.isUnset()).to.be.equal(false); - expect(await this.instance.isStarted()).to.be.equal(true); - expect(await this.instance.isPending()).to.be.equal(false); - expect(await this.instance.isExpired()).to.be.equal(true); - }); - - it('reset', async function () { - await this.instance.reset(); - expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(0)); - expect(await this.instance.isUnset()).to.be.equal(true); - expect(await this.instance.isStarted()).to.be.equal(false); - expect(await this.instance.isPending()).to.be.equal(false); - expect(await this.instance.isExpired()).to.be.equal(false); - }); - - it('fast forward', async function () { - await this.instance.setDeadline(this.now + 3); - expect(await this.instance.isPending()).to.be.equal(true); - expect(await this.instance.isExpired()).to.be.equal(false); - await time.advanceBlockTo(this.now + 3); - expect(await this.instance.isPending()).to.be.equal(false); - expect(await this.instance.isExpired()).to.be.equal(true); - }); -}); diff --git a/test/utils/TimersTimestamp.test.js b/test/utils/TimersTimestamp.test.js deleted file mode 100644 index fc32de7d0f5..00000000000 --- a/test/utils/TimersTimestamp.test.js +++ /dev/null @@ -1,55 +0,0 @@ -const { BN, time } = require('@openzeppelin/test-helpers'); -const { expect } = require('chai'); - -const TimersTimestampImpl = artifacts.require('TimersTimestampImpl'); - -contract('TimersTimestamp', function () { - beforeEach(async function () { - this.instance = await TimersTimestampImpl.new(); - this.now = await web3.eth.getBlock('latest').then(({ timestamp }) => timestamp); - }); - - it('unset', async function () { - expect(await this.instance.getDeadline()).to.be.bignumber.equal('0'); - expect(await this.instance.isUnset()).to.be.equal(true); - expect(await this.instance.isStarted()).to.be.equal(false); - expect(await this.instance.isPending()).to.be.equal(false); - expect(await this.instance.isExpired()).to.be.equal(false); - }); - - it('pending', async function () { - await this.instance.setDeadline(this.now + 100); - expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(this.now + 100)); - expect(await this.instance.isUnset()).to.be.equal(false); - expect(await this.instance.isStarted()).to.be.equal(true); - expect(await this.instance.isPending()).to.be.equal(true); - expect(await this.instance.isExpired()).to.be.equal(false); - }); - - it('expired', async function () { - await this.instance.setDeadline(this.now - 100); - expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(this.now - 100)); - expect(await this.instance.isUnset()).to.be.equal(false); - expect(await this.instance.isStarted()).to.be.equal(true); - expect(await this.instance.isPending()).to.be.equal(false); - expect(await this.instance.isExpired()).to.be.equal(true); - }); - - it('reset', async function () { - await this.instance.reset(); - expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(0)); - expect(await this.instance.isUnset()).to.be.equal(true); - expect(await this.instance.isStarted()).to.be.equal(false); - expect(await this.instance.isPending()).to.be.equal(false); - expect(await this.instance.isExpired()).to.be.equal(false); - }); - - it('fast forward', async function () { - await this.instance.setDeadline(this.now + 100); - expect(await this.instance.isPending()).to.be.equal(true); - expect(await this.instance.isExpired()).to.be.equal(false); - await time.increaseTo(this.now + 100); - expect(await this.instance.isPending()).to.be.equal(false); - expect(await this.instance.isExpired()).to.be.equal(true); - }); -}); diff --git a/test/utils/escrow/ConditionalEscrow.test.js b/test/utils/escrow/ConditionalEscrow.test.js deleted file mode 100644 index 9e17a8bcebe..00000000000 --- a/test/utils/escrow/ConditionalEscrow.test.js +++ /dev/null @@ -1,37 +0,0 @@ -const { ether, expectRevert } = require('@openzeppelin/test-helpers'); -const { shouldBehaveLikeEscrow } = require('./Escrow.behavior'); - -const ConditionalEscrowMock = artifacts.require('ConditionalEscrowMock'); - -contract('ConditionalEscrow', function (accounts) { - const [owner, payee, ...otherAccounts] = accounts; - - beforeEach(async function () { - this.escrow = await ConditionalEscrowMock.new({ from: owner }); - }); - - context('when withdrawal is allowed', function () { - beforeEach(async function () { - await Promise.all(otherAccounts.map(payee => this.escrow.setAllowed(payee, true))); - }); - - shouldBehaveLikeEscrow(owner, otherAccounts); - }); - - context('when withdrawal is disallowed', function () { - const amount = ether('23'); - - beforeEach(async function () { - await this.escrow.setAllowed(payee, false); - }); - - it('reverts on withdrawals', async function () { - await this.escrow.deposit(payee, { from: owner, value: amount }); - - await expectRevert( - this.escrow.withdraw(payee, { from: owner }), - 'ConditionalEscrow: payee is not allowed to withdraw', - ); - }); - }); -}); diff --git a/test/utils/escrow/Escrow.behavior.js b/test/utils/escrow/Escrow.behavior.js deleted file mode 100644 index f2059ed62c0..00000000000 --- a/test/utils/escrow/Escrow.behavior.js +++ /dev/null @@ -1,90 +0,0 @@ -const { balance, ether, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); - -const { expect } = require('chai'); - -function shouldBehaveLikeEscrow(owner, [payee1, payee2]) { - const amount = ether('42'); - - describe('as an escrow', function () { - describe('deposits', function () { - it('can accept a single deposit', async function () { - await this.escrow.deposit(payee1, { from: owner, value: amount }); - - expect(await balance.current(this.escrow.address)).to.be.bignumber.equal(amount); - - expect(await this.escrow.depositsOf(payee1)).to.be.bignumber.equal(amount); - }); - - it('can accept an empty deposit', async function () { - await this.escrow.deposit(payee1, { from: owner, value: 0 }); - }); - - it('only the owner can deposit', async function () { - await expectRevert(this.escrow.deposit(payee1, { from: payee2 }), 'Ownable: caller is not the owner'); - }); - - it('emits a deposited event', async function () { - const receipt = await this.escrow.deposit(payee1, { from: owner, value: amount }); - expectEvent(receipt, 'Deposited', { - payee: payee1, - weiAmount: amount, - }); - }); - - it('can add multiple deposits on a single account', async function () { - await this.escrow.deposit(payee1, { from: owner, value: amount }); - await this.escrow.deposit(payee1, { from: owner, value: amount.muln(2) }); - - expect(await balance.current(this.escrow.address)).to.be.bignumber.equal(amount.muln(3)); - - expect(await this.escrow.depositsOf(payee1)).to.be.bignumber.equal(amount.muln(3)); - }); - - it('can track deposits to multiple accounts', async function () { - await this.escrow.deposit(payee1, { from: owner, value: amount }); - await this.escrow.deposit(payee2, { from: owner, value: amount.muln(2) }); - - expect(await balance.current(this.escrow.address)).to.be.bignumber.equal(amount.muln(3)); - - expect(await this.escrow.depositsOf(payee1)).to.be.bignumber.equal(amount); - - expect(await this.escrow.depositsOf(payee2)).to.be.bignumber.equal(amount.muln(2)); - }); - }); - - describe('withdrawals', async function () { - it('can withdraw payments', async function () { - const balanceTracker = await balance.tracker(payee1); - - await this.escrow.deposit(payee1, { from: owner, value: amount }); - await this.escrow.withdraw(payee1, { from: owner }); - - expect(await balanceTracker.delta()).to.be.bignumber.equal(amount); - - expect(await balance.current(this.escrow.address)).to.be.bignumber.equal('0'); - expect(await this.escrow.depositsOf(payee1)).to.be.bignumber.equal('0'); - }); - - it('can do an empty withdrawal', async function () { - await this.escrow.withdraw(payee1, { from: owner }); - }); - - it('only the owner can withdraw', async function () { - await expectRevert(this.escrow.withdraw(payee1, { from: payee1 }), 'Ownable: caller is not the owner'); - }); - - it('emits a withdrawn event', async function () { - await this.escrow.deposit(payee1, { from: owner, value: amount }); - const receipt = await this.escrow.withdraw(payee1, { from: owner }); - expectEvent(receipt, 'Withdrawn', { - payee: payee1, - weiAmount: amount, - }); - }); - }); - }); -} - -module.exports = { - shouldBehaveLikeEscrow, -}; diff --git a/test/utils/escrow/Escrow.test.js b/test/utils/escrow/Escrow.test.js deleted file mode 100644 index 47627904bed..00000000000 --- a/test/utils/escrow/Escrow.test.js +++ /dev/null @@ -1,14 +0,0 @@ -require('@openzeppelin/test-helpers'); -const { shouldBehaveLikeEscrow } = require('./Escrow.behavior'); - -const Escrow = artifacts.require('Escrow'); - -contract('Escrow', function (accounts) { - const [owner, ...otherAccounts] = accounts; - - beforeEach(async function () { - this.escrow = await Escrow.new({ from: owner }); - }); - - shouldBehaveLikeEscrow(owner, otherAccounts); -}); diff --git a/test/utils/escrow/RefundEscrow.test.js b/test/utils/escrow/RefundEscrow.test.js deleted file mode 100644 index c5344db16ca..00000000000 --- a/test/utils/escrow/RefundEscrow.test.js +++ /dev/null @@ -1,143 +0,0 @@ -const { balance, constants, ether, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); -const { ZERO_ADDRESS } = constants; - -const { expect } = require('chai'); - -const RefundEscrow = artifacts.require('RefundEscrow'); - -contract('RefundEscrow', function (accounts) { - const [owner, beneficiary, refundee1, refundee2] = accounts; - - const amount = ether('54'); - const refundees = [refundee1, refundee2]; - - it('requires a non-null beneficiary', async function () { - await expectRevert( - RefundEscrow.new(ZERO_ADDRESS, { from: owner }), - 'RefundEscrow: beneficiary is the zero address', - ); - }); - - context('once deployed', function () { - beforeEach(async function () { - this.escrow = await RefundEscrow.new(beneficiary, { from: owner }); - }); - - context('active state', function () { - it('has beneficiary and state', async function () { - expect(await this.escrow.beneficiary()).to.equal(beneficiary); - expect(await this.escrow.state()).to.be.bignumber.equal('0'); - }); - - it('accepts deposits', async function () { - await this.escrow.deposit(refundee1, { from: owner, value: amount }); - - expect(await this.escrow.depositsOf(refundee1)).to.be.bignumber.equal(amount); - }); - - it('does not refund refundees', async function () { - await this.escrow.deposit(refundee1, { from: owner, value: amount }); - await expectRevert(this.escrow.withdraw(refundee1), 'ConditionalEscrow: payee is not allowed to withdraw'); - }); - - it('does not allow beneficiary withdrawal', async function () { - await this.escrow.deposit(refundee1, { from: owner, value: amount }); - await expectRevert( - this.escrow.beneficiaryWithdraw(), - 'RefundEscrow: beneficiary can only withdraw while closed', - ); - }); - }); - - it('only the owner can enter closed state', async function () { - await expectRevert(this.escrow.close({ from: beneficiary }), 'Ownable: caller is not the owner'); - - const receipt = await this.escrow.close({ from: owner }); - expectEvent(receipt, 'RefundsClosed'); - }); - - context('closed state', function () { - beforeEach(async function () { - await Promise.all(refundees.map(refundee => this.escrow.deposit(refundee, { from: owner, value: amount }))); - - await this.escrow.close({ from: owner }); - }); - - it('rejects deposits', async function () { - await expectRevert( - this.escrow.deposit(refundee1, { from: owner, value: amount }), - 'RefundEscrow: can only deposit while active', - ); - }); - - it('does not refund refundees', async function () { - await expectRevert(this.escrow.withdraw(refundee1), 'ConditionalEscrow: payee is not allowed to withdraw'); - }); - - it('allows beneficiary withdrawal', async function () { - const balanceTracker = await balance.tracker(beneficiary); - await this.escrow.beneficiaryWithdraw(); - expect(await balanceTracker.delta()).to.be.bignumber.equal(amount.muln(refundees.length)); - }); - - it('prevents entering the refund state', async function () { - await expectRevert( - this.escrow.enableRefunds({ from: owner }), - 'RefundEscrow: can only enable refunds while active', - ); - }); - - it('prevents re-entering the closed state', async function () { - await expectRevert(this.escrow.close({ from: owner }), 'RefundEscrow: can only close while active'); - }); - }); - - it('only the owner can enter refund state', async function () { - await expectRevert(this.escrow.enableRefunds({ from: beneficiary }), 'Ownable: caller is not the owner'); - - const receipt = await this.escrow.enableRefunds({ from: owner }); - expectEvent(receipt, 'RefundsEnabled'); - }); - - context('refund state', function () { - beforeEach(async function () { - await Promise.all(refundees.map(refundee => this.escrow.deposit(refundee, { from: owner, value: amount }))); - - await this.escrow.enableRefunds({ from: owner }); - }); - - it('rejects deposits', async function () { - await expectRevert( - this.escrow.deposit(refundee1, { from: owner, value: amount }), - 'RefundEscrow: can only deposit while active', - ); - }); - - it('refunds refundees', async function () { - for (const refundee of [refundee1, refundee2]) { - const balanceTracker = await balance.tracker(refundee); - await this.escrow.withdraw(refundee, { from: owner }); - expect(await balanceTracker.delta()).to.be.bignumber.equal(amount); - } - }); - - it('does not allow beneficiary withdrawal', async function () { - await expectRevert( - this.escrow.beneficiaryWithdraw(), - 'RefundEscrow: beneficiary can only withdraw while closed', - ); - }); - - it('prevents entering the closed state', async function () { - await expectRevert(this.escrow.close({ from: owner }), 'RefundEscrow: can only close while active'); - }); - - it('prevents re-entering the refund state', async function () { - await expectRevert( - this.escrow.enableRefunds({ from: owner }), - 'RefundEscrow: can only enable refunds while active', - ); - }); - }); - }); -}); diff --git a/test/utils/introspection/ERC1820Implementer.test.js b/test/utils/introspection/ERC1820Implementer.test.js deleted file mode 100644 index 83d0a6534c7..00000000000 --- a/test/utils/introspection/ERC1820Implementer.test.js +++ /dev/null @@ -1,71 +0,0 @@ -const { expectRevert, singletons } = require('@openzeppelin/test-helpers'); -const { bufferToHex, keccakFromString } = require('ethereumjs-util'); - -const { expect } = require('chai'); - -const ERC1820Implementer = artifacts.require('$ERC1820Implementer'); - -contract('ERC1820Implementer', function (accounts) { - const [registryFunder, implementee, other] = accounts; - - const ERC1820_ACCEPT_MAGIC = bufferToHex(keccakFromString('ERC1820_ACCEPT_MAGIC')); - - beforeEach(async function () { - this.implementer = await ERC1820Implementer.new(); - this.registry = await singletons.ERC1820Registry(registryFunder); - - this.interfaceA = bufferToHex(keccakFromString('interfaceA')); - this.interfaceB = bufferToHex(keccakFromString('interfaceB')); - }); - - context('with no registered interfaces', function () { - it('returns false when interface implementation is queried', async function () { - expect(await this.implementer.canImplementInterfaceForAddress(this.interfaceA, implementee)).to.not.equal( - ERC1820_ACCEPT_MAGIC, - ); - }); - - it('reverts when attempting to set as implementer in the registry', async function () { - await expectRevert( - this.registry.setInterfaceImplementer(implementee, this.interfaceA, this.implementer.address, { - from: implementee, - }), - 'Does not implement the interface', - ); - }); - }); - - context('with registered interfaces', function () { - beforeEach(async function () { - await this.implementer.$_registerInterfaceForAddress(this.interfaceA, implementee); - }); - - it('returns true when interface implementation is queried', async function () { - expect(await this.implementer.canImplementInterfaceForAddress(this.interfaceA, implementee)).to.equal( - ERC1820_ACCEPT_MAGIC, - ); - }); - - it('returns false when interface implementation for non-supported interfaces is queried', async function () { - expect(await this.implementer.canImplementInterfaceForAddress(this.interfaceB, implementee)).to.not.equal( - ERC1820_ACCEPT_MAGIC, - ); - }); - - it('returns false when interface implementation for non-supported addresses is queried', async function () { - expect(await this.implementer.canImplementInterfaceForAddress(this.interfaceA, other)).to.not.equal( - ERC1820_ACCEPT_MAGIC, - ); - }); - - it('can be set as an implementer for supported interfaces in the registry', async function () { - await this.registry.setInterfaceImplementer(implementee, this.interfaceA, this.implementer.address, { - from: implementee, - }); - - expect(await this.registry.getInterfaceImplementer(implementee, this.interfaceA)).to.equal( - this.implementer.address, - ); - }); - }); -}); diff --git a/test/utils/math/Math.t.sol b/test/utils/math/Math.t.sol index 916136cdd91..9e7e118dfef 100644 --- a/test/utils/math/Math.t.sol +++ b/test/utils/math/Math.t.sol @@ -5,7 +5,6 @@ pragma solidity ^0.8.0; import "forge-std/Test.sol"; import "../../../contracts/utils/math/Math.sol"; -import "../../../contracts/utils/math/SafeMath.sol"; contract MathTest is Test { // CEILDIV @@ -47,7 +46,7 @@ contract MathTest is Test { } function _squareBigger(uint256 value, uint256 ref) private pure returns (bool) { - (bool noOverflow, uint256 square) = SafeMath.tryMul(value, value); + (bool noOverflow, uint256 square) = Math.tryMul(value, value); return !noOverflow || square > ref; } diff --git a/test/utils/math/Math.test.js b/test/utils/math/Math.test.js index 6b97a646f0e..df459c5f82c 100644 --- a/test/utils/math/Math.test.js +++ b/test/utils/math/Math.test.js @@ -5,6 +5,21 @@ const { Rounding } = require('../../helpers/enums.js'); const Math = artifacts.require('$Math'); +function expectStruct(value, expected) { + for (const key in expected) { + if (BN.isBN(value[key])) { + expect(value[key]).to.be.bignumber.equal(expected[key]); + } else { + expect(value[key]).to.be.equal(expected[key]); + } + } +} + +async function testCommutativeIterable(fn, lhs, rhs, expected, ...extra) { + expectStruct(await fn(lhs, rhs, ...extra), expected); + expectStruct(await fn(rhs, lhs, ...extra), expected); +} + contract('Math', function () { const min = new BN('1234'); const max = new BN('5678'); @@ -15,6 +30,130 @@ contract('Math', function () { this.math = await Math.new(); }); + describe('tryAdd', function () { + it('adds correctly', async function () { + const a = new BN('5678'); + const b = new BN('1234'); + + await testCommutativeIterable(this.math.$tryAdd, a, b, [true, a.add(b)]); + }); + + it('reverts on addition overflow', async function () { + const a = MAX_UINT256; + const b = new BN('1'); + + await testCommutativeIterable(this.math.$tryAdd, a, b, [false, '0']); + }); + }); + + describe('trySub', function () { + it('subtracts correctly', async function () { + const a = new BN('5678'); + const b = new BN('1234'); + + expectStruct(await this.math.$trySub(a, b), [true, a.sub(b)]); + }); + + it('reverts if subtraction result would be negative', async function () { + const a = new BN('1234'); + const b = new BN('5678'); + + expectStruct(await this.math.$trySub(a, b), [false, '0']); + }); + }); + + describe('tryMul', function () { + it('multiplies correctly', async function () { + const a = new BN('1234'); + const b = new BN('5678'); + + await testCommutativeIterable(this.math.$tryMul, a, b, [true, a.mul(b)]); + }); + + it('multiplies by zero correctly', async function () { + const a = new BN('0'); + const b = new BN('5678'); + + await testCommutativeIterable(this.math.$tryMul, a, b, [true, a.mul(b)]); + }); + + it('reverts on multiplication overflow', async function () { + const a = MAX_UINT256; + const b = new BN('2'); + + await testCommutativeIterable(this.math.$tryMul, a, b, [false, '0']); + }); + }); + + describe('tryDiv', function () { + it('divides correctly', async function () { + const a = new BN('5678'); + const b = new BN('5678'); + + expectStruct(await this.math.$tryDiv(a, b), [true, a.div(b)]); + }); + + it('divides zero correctly', async function () { + const a = new BN('0'); + const b = new BN('5678'); + + expectStruct(await this.math.$tryDiv(a, b), [true, a.div(b)]); + }); + + it('returns complete number result on non-even division', async function () { + const a = new BN('7000'); + const b = new BN('5678'); + + expectStruct(await this.math.$tryDiv(a, b), [true, a.div(b)]); + }); + + it('reverts on division by zero', async function () { + const a = new BN('5678'); + const b = new BN('0'); + + expectStruct(await this.math.$tryDiv(a, b), [false, '0']); + }); + }); + + describe('tryMod', function () { + describe('modulos correctly', async function () { + it('when the dividend is smaller than the divisor', async function () { + const a = new BN('284'); + const b = new BN('5678'); + + expectStruct(await this.math.$tryMod(a, b), [true, a.mod(b)]); + }); + + it('when the dividend is equal to the divisor', async function () { + const a = new BN('5678'); + const b = new BN('5678'); + + expectStruct(await this.math.$tryMod(a, b), [true, a.mod(b)]); + }); + + it('when the dividend is larger than the divisor', async function () { + const a = new BN('7000'); + const b = new BN('5678'); + + expectStruct(await this.math.$tryMod(a, b), [true, a.mod(b)]); + }); + + it('when the dividend is a multiple of the divisor', async function () { + const a = new BN('17034'); // 17034 == 5678 * 3 + const b = new BN('5678'); + + expectStruct(await this.math.$tryMod(a, b), [true, a.mod(b)]); + }); + }); + + it('reverts with a 0 divisor', async function () { + const a = new BN('5678'); + const b = new BN('0'); + + expectStruct(await this.math.$tryMod(a, b), [false, '0']); + }); + }); + describe('max', function () { it('is correctly detected in first argument position', async function () { expect(await this.math.$max(max, min)).to.be.bignumber.equal(max); diff --git a/test/utils/math/SafeMath.test.js b/test/utils/math/SafeMath.test.js deleted file mode 100644 index 9598ac57c5d..00000000000 --- a/test/utils/math/SafeMath.test.js +++ /dev/null @@ -1,433 +0,0 @@ -const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers'); -const { MAX_UINT256 } = constants; - -const { expect } = require('chai'); - -const SafeMath = artifacts.require('$SafeMath'); -const SafeMathMemoryCheck = artifacts.require('$SafeMathMemoryCheck'); - -function expectStruct(value, expected) { - for (const key in expected) { - if (BN.isBN(value[key])) { - expect(value[key]).to.be.bignumber.equal(expected[key]); - } else { - expect(value[key]).to.be.equal(expected[key]); - } - } -} - -contract('SafeMath', function () { - beforeEach(async function () { - this.safeMath = await SafeMath.new(); - }); - - async function testCommutative(fn, lhs, rhs, expected, ...extra) { - expect(await fn(lhs, rhs, ...extra)).to.be.bignumber.equal(expected); - expect(await fn(rhs, lhs, ...extra)).to.be.bignumber.equal(expected); - } - - async function testFailsCommutative(fn, lhs, rhs, reason, ...extra) { - if (reason === undefined) { - await expectRevert.unspecified(fn(lhs, rhs, ...extra)); - await expectRevert.unspecified(fn(rhs, lhs, ...extra)); - } else { - await expectRevert(fn(lhs, rhs, ...extra), reason); - await expectRevert(fn(rhs, lhs, ...extra), reason); - } - } - - async function testCommutativeIterable(fn, lhs, rhs, expected, ...extra) { - expectStruct(await fn(lhs, rhs, ...extra), expected); - expectStruct(await fn(rhs, lhs, ...extra), expected); - } - - describe('with flag', function () { - describe('add', function () { - it('adds correctly', async function () { - const a = new BN('5678'); - const b = new BN('1234'); - - testCommutativeIterable(this.safeMath.$tryAdd, a, b, [true, a.add(b)]); - }); - - it('reverts on addition overflow', async function () { - const a = MAX_UINT256; - const b = new BN('1'); - - testCommutativeIterable(this.safeMath.$tryAdd, a, b, [false, '0']); - }); - }); - - describe('sub', function () { - it('subtracts correctly', async function () { - const a = new BN('5678'); - const b = new BN('1234'); - - expectStruct(await this.safeMath.$trySub(a, b), [true, a.sub(b)]); - }); - - it('reverts if subtraction result would be negative', async function () { - const a = new BN('1234'); - const b = new BN('5678'); - - expectStruct(await this.safeMath.$trySub(a, b), [false, '0']); - }); - }); - - describe('mul', function () { - it('multiplies correctly', async function () { - const a = new BN('1234'); - const b = new BN('5678'); - - testCommutativeIterable(this.safeMath.$tryMul, a, b, [true, a.mul(b)]); - }); - - it('multiplies by zero correctly', async function () { - const a = new BN('0'); - const b = new BN('5678'); - - testCommutativeIterable(this.safeMath.$tryMul, a, b, [true, a.mul(b)]); - }); - - it('reverts on multiplication overflow', async function () { - const a = MAX_UINT256; - const b = new BN('2'); - - testCommutativeIterable(this.safeMath.$tryMul, a, b, [false, '0']); - }); - }); - - describe('div', function () { - it('divides correctly', async function () { - const a = new BN('5678'); - const b = new BN('5678'); - - expectStruct(await this.safeMath.$tryDiv(a, b), [true, a.div(b)]); - }); - - it('divides zero correctly', async function () { - const a = new BN('0'); - const b = new BN('5678'); - - expectStruct(await this.safeMath.$tryDiv(a, b), [true, a.div(b)]); - }); - - it('returns complete number result on non-even division', async function () { - const a = new BN('7000'); - const b = new BN('5678'); - - expectStruct(await this.safeMath.$tryDiv(a, b), [true, a.div(b)]); - }); - - it('reverts on division by zero', async function () { - const a = new BN('5678'); - const b = new BN('0'); - - expectStruct(await this.safeMath.$tryDiv(a, b), [false, '0']); - }); - }); - - describe('mod', function () { - describe('modulos correctly', async function () { - it('when the dividend is smaller than the divisor', async function () { - const a = new BN('284'); - const b = new BN('5678'); - - expectStruct(await this.safeMath.$tryMod(a, b), [true, a.mod(b)]); - }); - - it('when the dividend is equal to the divisor', async function () { - const a = new BN('5678'); - const b = new BN('5678'); - - expectStruct(await this.safeMath.$tryMod(a, b), [true, a.mod(b)]); - }); - - it('when the dividend is larger than the divisor', async function () { - const a = new BN('7000'); - const b = new BN('5678'); - - expectStruct(await this.safeMath.$tryMod(a, b), [true, a.mod(b)]); - }); - - it('when the dividend is a multiple of the divisor', async function () { - const a = new BN('17034'); // 17034 == 5678 * 3 - const b = new BN('5678'); - - expectStruct(await this.safeMath.$tryMod(a, b), [true, a.mod(b)]); - }); - }); - - it('reverts with a 0 divisor', async function () { - const a = new BN('5678'); - const b = new BN('0'); - - expectStruct(await this.safeMath.$tryMod(a, b), [false, '0']); - }); - }); - }); - - describe('with default revert message', function () { - describe('add', function () { - it('adds correctly', async function () { - const a = new BN('5678'); - const b = new BN('1234'); - - await testCommutative(this.safeMath.$add, a, b, a.add(b)); - }); - - it('reverts on addition overflow', async function () { - const a = MAX_UINT256; - const b = new BN('1'); - - await testFailsCommutative(this.safeMath.$add, a, b, undefined); - }); - }); - - describe('sub', function () { - it('subtracts correctly', async function () { - const a = new BN('5678'); - const b = new BN('1234'); - - expect(await this.safeMath.$sub(a, b)).to.be.bignumber.equal(a.sub(b)); - }); - - it('reverts if subtraction result would be negative', async function () { - const a = new BN('1234'); - const b = new BN('5678'); - - await expectRevert.unspecified(this.safeMath.$sub(a, b)); - }); - }); - - describe('mul', function () { - it('multiplies correctly', async function () { - const a = new BN('1234'); - const b = new BN('5678'); - - await testCommutative(this.safeMath.$mul, a, b, a.mul(b)); - }); - - it('multiplies by zero correctly', async function () { - const a = new BN('0'); - const b = new BN('5678'); - - await testCommutative(this.safeMath.$mul, a, b, '0'); - }); - - it('reverts on multiplication overflow', async function () { - const a = MAX_UINT256; - const b = new BN('2'); - - await testFailsCommutative(this.safeMath.$mul, a, b, undefined); - }); - }); - - describe('div', function () { - it('divides correctly', async function () { - const a = new BN('5678'); - const b = new BN('5678'); - - expect(await this.safeMath.$div(a, b)).to.be.bignumber.equal(a.div(b)); - }); - - it('divides zero correctly', async function () { - const a = new BN('0'); - const b = new BN('5678'); - - expect(await this.safeMath.$div(a, b)).to.be.bignumber.equal('0'); - }); - - it('returns complete number result on non-even division', async function () { - const a = new BN('7000'); - const b = new BN('5678'); - - expect(await this.safeMath.$div(a, b)).to.be.bignumber.equal('1'); - }); - - it('reverts on division by zero', async function () { - const a = new BN('5678'); - const b = new BN('0'); - - await expectRevert.unspecified(this.safeMath.$div(a, b)); - }); - }); - - describe('mod', function () { - describe('modulos correctly', async function () { - it('when the dividend is smaller than the divisor', async function () { - const a = new BN('284'); - const b = new BN('5678'); - - expect(await this.safeMath.$mod(a, b)).to.be.bignumber.equal(a.mod(b)); - }); - - it('when the dividend is equal to the divisor', async function () { - const a = new BN('5678'); - const b = new BN('5678'); - - expect(await this.safeMath.$mod(a, b)).to.be.bignumber.equal(a.mod(b)); - }); - - it('when the dividend is larger than the divisor', async function () { - const a = new BN('7000'); - const b = new BN('5678'); - - expect(await this.safeMath.$mod(a, b)).to.be.bignumber.equal(a.mod(b)); - }); - - it('when the dividend is a multiple of the divisor', async function () { - const a = new BN('17034'); // 17034 == 5678 * 3 - const b = new BN('5678'); - - expect(await this.safeMath.$mod(a, b)).to.be.bignumber.equal(a.mod(b)); - }); - }); - - it('reverts with a 0 divisor', async function () { - const a = new BN('5678'); - const b = new BN('0'); - - await expectRevert.unspecified(this.safeMath.$mod(a, b)); - }); - }); - }); - - describe('with custom revert message', function () { - describe('sub', function () { - it('subtracts correctly', async function () { - const a = new BN('5678'); - const b = new BN('1234'); - - expect( - await this.safeMath.methods['$sub(uint256,uint256,string)'](a, b, 'MyErrorMessage'), - ).to.be.bignumber.equal(a.sub(b)); - }); - - it('reverts if subtraction result would be negative', async function () { - const a = new BN('1234'); - const b = new BN('5678'); - - await expectRevert( - this.safeMath.methods['$sub(uint256,uint256,string)'](a, b, 'MyErrorMessage'), - 'MyErrorMessage', - ); - }); - }); - - describe('div', function () { - it('divides correctly', async function () { - const a = new BN('5678'); - const b = new BN('5678'); - - expect( - await this.safeMath.methods['$div(uint256,uint256,string)'](a, b, 'MyErrorMessage'), - ).to.be.bignumber.equal(a.div(b)); - }); - - it('divides zero correctly', async function () { - const a = new BN('0'); - const b = new BN('5678'); - - expect( - await this.safeMath.methods['$div(uint256,uint256,string)'](a, b, 'MyErrorMessage'), - ).to.be.bignumber.equal('0'); - }); - - it('returns complete number result on non-even division', async function () { - const a = new BN('7000'); - const b = new BN('5678'); - - expect( - await this.safeMath.methods['$div(uint256,uint256,string)'](a, b, 'MyErrorMessage'), - ).to.be.bignumber.equal('1'); - }); - - it('reverts on division by zero', async function () { - const a = new BN('5678'); - const b = new BN('0'); - - await expectRevert( - this.safeMath.methods['$div(uint256,uint256,string)'](a, b, 'MyErrorMessage'), - 'MyErrorMessage', - ); - }); - }); - - describe('mod', function () { - describe('modulos correctly', async function () { - it('when the dividend is smaller than the divisor', async function () { - const a = new BN('284'); - const b = new BN('5678'); - - expect( - await this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'), - ).to.be.bignumber.equal(a.mod(b)); - }); - - it('when the dividend is equal to the divisor', async function () { - const a = new BN('5678'); - const b = new BN('5678'); - - expect( - await this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'), - ).to.be.bignumber.equal(a.mod(b)); - }); - - it('when the dividend is larger than the divisor', async function () { - const a = new BN('7000'); - const b = new BN('5678'); - - expect( - await this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'), - ).to.be.bignumber.equal(a.mod(b)); - }); - - it('when the dividend is a multiple of the divisor', async function () { - const a = new BN('17034'); // 17034 == 5678 * 3 - const b = new BN('5678'); - - expect( - await this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'), - ).to.be.bignumber.equal(a.mod(b)); - }); - }); - - it('reverts with a 0 divisor', async function () { - const a = new BN('5678'); - const b = new BN('0'); - - await expectRevert( - this.safeMath.methods['$mod(uint256,uint256,string)'](a, b, 'MyErrorMessage'), - 'MyErrorMessage', - ); - }); - }); - }); - - describe('memory leakage', function () { - beforeEach(async function () { - this.safeMathMemoryCheck = await SafeMathMemoryCheck.new(); - }); - - it('add', async function () { - expect(await this.safeMathMemoryCheck.$addMemoryCheck()).to.be.bignumber.equal('0'); - }); - - it('sub', async function () { - expect(await this.safeMathMemoryCheck.$subMemoryCheck()).to.be.bignumber.equal('0'); - }); - - it('mul', async function () { - expect(await this.safeMathMemoryCheck.$mulMemoryCheck()).to.be.bignumber.equal('0'); - }); - - it('div', async function () { - expect(await this.safeMathMemoryCheck.$divMemoryCheck()).to.be.bignumber.equal('0'); - }); - - it('mod', async function () { - expect(await this.safeMathMemoryCheck.$modMemoryCheck()).to.be.bignumber.equal('0'); - }); - }); -}); diff --git a/test/utils/math/SignedSafeMath.test.js b/test/utils/math/SignedSafeMath.test.js deleted file mode 100644 index 3b6530cf523..00000000000 --- a/test/utils/math/SignedSafeMath.test.js +++ /dev/null @@ -1,152 +0,0 @@ -const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers'); -const { MAX_INT256, MIN_INT256 } = constants; - -const { expect } = require('chai'); - -const SignedSafeMath = artifacts.require('$SignedSafeMath'); - -contract('SignedSafeMath', function () { - beforeEach(async function () { - this.safeMath = await SignedSafeMath.new(); - }); - - async function testCommutative(fn, lhs, rhs, expected) { - expect(await fn(lhs, rhs)).to.be.bignumber.equal(expected); - expect(await fn(rhs, lhs)).to.be.bignumber.equal(expected); - } - - async function testFailsCommutative(fn, lhs, rhs) { - await expectRevert.unspecified(fn(lhs, rhs)); - await expectRevert.unspecified(fn(rhs, lhs)); - } - - describe('add', function () { - it('adds correctly if it does not overflow and the result is positive', async function () { - const a = new BN('1234'); - const b = new BN('5678'); - - await testCommutative(this.safeMath.$add, a, b, a.add(b)); - }); - - it('adds correctly if it does not overflow and the result is negative', async function () { - const a = MAX_INT256; - const b = MIN_INT256; - - await testCommutative(this.safeMath.$add, a, b, a.add(b)); - }); - - it('reverts on positive addition overflow', async function () { - const a = MAX_INT256; - const b = new BN('1'); - - await testFailsCommutative(this.safeMath.$add, a, b); - }); - - it('reverts on negative addition overflow', async function () { - const a = MIN_INT256; - const b = new BN('-1'); - - await testFailsCommutative(this.safeMath.$add, a, b); - }); - }); - - describe('sub', function () { - it('subtracts correctly if it does not overflow and the result is positive', async function () { - const a = new BN('5678'); - const b = new BN('1234'); - - const result = await this.safeMath.$sub(a, b); - expect(result).to.be.bignumber.equal(a.sub(b)); - }); - - it('subtracts correctly if it does not overflow and the result is negative', async function () { - const a = new BN('1234'); - const b = new BN('5678'); - - const result = await this.safeMath.$sub(a, b); - expect(result).to.be.bignumber.equal(a.sub(b)); - }); - - it('reverts on positive subtraction overflow', async function () { - const a = MAX_INT256; - const b = new BN('-1'); - - await expectRevert.unspecified(this.safeMath.$sub(a, b)); - }); - - it('reverts on negative subtraction overflow', async function () { - const a = MIN_INT256; - const b = new BN('1'); - - await expectRevert.unspecified(this.safeMath.$sub(a, b)); - }); - }); - - describe('mul', function () { - it('multiplies correctly', async function () { - const a = new BN('5678'); - const b = new BN('-1234'); - - await testCommutative(this.safeMath.$mul, a, b, a.mul(b)); - }); - - it('multiplies by zero correctly', async function () { - const a = new BN('0'); - const b = new BN('5678'); - - await testCommutative(this.safeMath.$mul, a, b, '0'); - }); - - it('reverts on multiplication overflow, positive operands', async function () { - const a = MAX_INT256; - const b = new BN('2'); - - await testFailsCommutative(this.safeMath.$mul, a, b); - }); - - it('reverts when minimum integer is multiplied by -1', async function () { - const a = MIN_INT256; - const b = new BN('-1'); - - await testFailsCommutative(this.safeMath.$mul, a, b); - }); - }); - - describe('div', function () { - it('divides correctly', async function () { - const a = new BN('-5678'); - const b = new BN('5678'); - - const result = await this.safeMath.$div(a, b); - expect(result).to.be.bignumber.equal(a.div(b)); - }); - - it('divides zero correctly', async function () { - const a = new BN('0'); - const b = new BN('5678'); - - expect(await this.safeMath.$div(a, b)).to.be.bignumber.equal('0'); - }); - - it('returns complete number result on non-even division', async function () { - const a = new BN('7000'); - const b = new BN('5678'); - - expect(await this.safeMath.$div(a, b)).to.be.bignumber.equal('1'); - }); - - it('reverts on division by zero', async function () { - const a = new BN('-5678'); - const b = new BN('0'); - - await expectRevert.unspecified(this.safeMath.$div(a, b)); - }); - - it('reverts on overflow, negative second', async function () { - const a = new BN(MIN_INT256); - const b = new BN('-1'); - - await expectRevert.unspecified(this.safeMath.$div(a, b)); - }); - }); -}); diff --git a/test/utils/structs/EnumerableMap.behavior.js b/test/utils/structs/EnumerableMap.behavior.js index b4b5ee37219..3db45df6aa9 100644 --- a/test/utils/structs/EnumerableMap.behavior.js +++ b/test/utils/structs/EnumerableMap.behavior.js @@ -154,17 +154,6 @@ function shouldBehaveLikeMap(keys, values, zeroValue, methods, events) { }); }); - describe('get with message', function () { - it('existing value', async function () { - expect(await methods.getWithMessage(this.map, keyA, 'custom error string').then(r => r.toString())).to.be.equal( - valueA.toString(), - ); - }); - it('missing value', async function () { - await expectRevert(methods.getWithMessage(this.map, keyB, 'custom error string'), 'custom error string'); - }); - }); - describe('tryGet', function () { it('existing value', async function () { const result = await methods.tryGet(this.map, keyA); diff --git a/test/utils/structs/EnumerableMap.test.js b/test/utils/structs/EnumerableMap.test.js index f540ec99e0b..8861fa73179 100644 --- a/test/utils/structs/EnumerableMap.test.js +++ b/test/utils/structs/EnumerableMap.test.js @@ -41,7 +41,6 @@ contract('EnumerableMap', function (accounts) { getMethods({ set: '$set(uint256,address,uint256)', get: '$get(uint256,address)', - getWithMessage: '$get(uint256,address,string)', tryGet: '$tryGet(uint256,address)', remove: '$remove(uint256,address)', length: `$length_${library}_AddressToUintMap(uint256)`, @@ -65,7 +64,6 @@ contract('EnumerableMap', function (accounts) { getMethods({ set: '$set(uint256,uint256,address)', get: `$get_${library}_UintToAddressMap(uint256,uint256)`, - getWithMessage: `$get_${library}_UintToAddressMap(uint256,uint256,string)`, tryGet: `$tryGet_${library}_UintToAddressMap(uint256,uint256)`, remove: `$remove_${library}_UintToAddressMap(uint256,uint256)`, length: `$length_${library}_UintToAddressMap(uint256)`, @@ -89,7 +87,6 @@ contract('EnumerableMap', function (accounts) { getMethods({ set: '$set(uint256,bytes32,bytes32)', get: `$get_${library}_Bytes32ToBytes32Map(uint256,bytes32)`, - getWithMessage: `$get_${library}_Bytes32ToBytes32Map(uint256,bytes32,string)`, tryGet: `$tryGet_${library}_Bytes32ToBytes32Map(uint256,bytes32)`, remove: `$remove_${library}_Bytes32ToBytes32Map(uint256,bytes32)`, length: `$length_${library}_Bytes32ToBytes32Map(uint256)`, @@ -113,7 +110,6 @@ contract('EnumerableMap', function (accounts) { getMethods({ set: '$set(uint256,uint256,uint256)', get: `$get_${library}_UintToUintMap(uint256,uint256)`, - getWithMessage: `$get_${library}_UintToUintMap(uint256,uint256,string)`, tryGet: `$tryGet_${library}_UintToUintMap(uint256,uint256)`, remove: `$remove_${library}_UintToUintMap(uint256,uint256)`, length: `$length_${library}_UintToUintMap(uint256)`, @@ -137,7 +133,6 @@ contract('EnumerableMap', function (accounts) { getMethods({ set: '$set(uint256,bytes32,uint256)', get: `$get_${library}_Bytes32ToUintMap(uint256,bytes32)`, - getWithMessage: `$get_${library}_Bytes32ToUintMap(uint256,bytes32,string)`, tryGet: `$tryGet_${library}_Bytes32ToUintMap(uint256,bytes32)`, remove: `$remove_${library}_Bytes32ToUintMap(uint256,bytes32)`, length: `$length_${library}_Bytes32ToUintMap(uint256)`,