diff --git a/ROLES.md b/ROLES.md index 26914d16..1c8868ef 100644 --- a/ROLES.md +++ b/ROLES.md @@ -17,6 +17,7 @@ This document describes the roles that are used in the Olympus protocol. | emergency_restart | Emergency | Reactivates the TRSRY and/or MINTR modules | | emergency_restart | EmissionManager | Reactivates the EmissionManager | | emergency_shutdown | CDAuctioneer | Activate/deactivate the CDAuctioneer | +| emergency_shutdown | CDFacility | Activate/deactivate the CDFacility | | emergency_shutdown | Clearinghouse | Allows shutting down the protocol in an emergency | | emergency_shutdown | Emergency | Deactivates the TRSRY and/or MINTR modules | | emergency_shutdown | EmissionManager | Deactivates the EmissionManager | diff --git a/src/policies/CDFacility.sol b/src/policies/CDFacility.sol index ddecd34d..e4e7fc4d 100644 --- a/src/policies/CDFacility.sol +++ b/src/policies/CDFacility.sol @@ -24,7 +24,7 @@ contract CDFacility is Policy, RolesConsumer, IConvertibleDepositFacility, Reent // Constants /// @notice The scale of the convertible deposit token - /// @dev This will typically be 10 ** decimals + /// @dev This will typically be 10 ** decimals, and is set by the `configureDependencies()` function uint256 public SCALE; // Modules @@ -33,6 +33,13 @@ contract CDFacility is Policy, RolesConsumer, IConvertibleDepositFacility, Reent CDEPOv1 public CDEPO; CDPOSv1 public CDPOS; + /// @notice Whether the contract functionality has been activated + bool public locallyActive; + + bytes32 public constant ROLE_EMERGENCY_SHUTDOWN = "emergency_shutdown"; + + bytes32 public constant ROLE_AUCTIONEER = "cd_auctioneer"; + // ========== ERRORS ========== // /// @notice An error that is thrown when the parameters are invalid @@ -41,7 +48,8 @@ contract CDFacility is Policy, RolesConsumer, IConvertibleDepositFacility, Reent // ========== SETUP ========== // constructor(address kernel_) Policy(Kernel(kernel_)) { - // TODO disable until activated + // Disable functionality until initialized + locallyActive = false; } function configureDependencies() external override returns (Keycode[] memory dependencies) { @@ -84,13 +92,16 @@ contract CDFacility is Policy, RolesConsumer, IConvertibleDepositFacility, Reent // ========== CONVERTIBLE DEPOSIT ACTIONS ========== // /// @inheritdoc IConvertibleDepositFacility + /// @dev This function reverts if: + /// - The caller does not have the ROLE_AUCTIONEER role + /// - The contract is not active function create( address account_, uint256 amount_, uint256 conversionPrice_, uint48 expiry_, bool wrap_ - ) external onlyRole("cd_auctioneer") nonReentrant returns (uint256 positionId) { + ) external onlyRole(ROLE_AUCTIONEER) nonReentrant onlyActive returns (uint256 positionId) { // Mint the CD token to the account // This will also transfer the reserve token CDEPO.mintFor(account_, amount_); @@ -144,6 +155,7 @@ contract CDFacility is Policy, RolesConsumer, IConvertibleDepositFacility, Reent /// @inheritdoc IConvertibleDepositFacility /// @dev This function reverts if: + /// - The contract is not active /// - The length of the positionIds_ array does not match the length of the amounts_ array /// - account_ is not the owner of all of the positions /// - The position is not valid @@ -156,7 +168,12 @@ contract CDFacility is Policy, RolesConsumer, IConvertibleDepositFacility, Reent address account_, uint256[] memory positionIds_, uint256[] memory amounts_ - ) external view returns (uint256 cdTokenIn, uint256 convertedTokenOut, address cdTokenSpender) { + ) + external + view + onlyActive + returns (uint256 cdTokenIn, uint256 convertedTokenOut, address cdTokenSpender) + { // Make sure the lengths of the arrays are the same if (positionIds_.length != amounts_.length) revert CDF_InvalidArgs("array length"); @@ -178,6 +195,7 @@ contract CDFacility is Policy, RolesConsumer, IConvertibleDepositFacility, Reent /// @inheritdoc IConvertibleDepositFacility /// @dev This function reverts if: + /// - The contract is not active /// - The length of the positionIds_ array does not match the length of the amounts_ array /// - The caller is not the owner of all of the positions /// - The position is not valid @@ -189,7 +207,7 @@ contract CDFacility is Policy, RolesConsumer, IConvertibleDepositFacility, Reent function convert( uint256[] memory positionIds_, uint256[] memory amounts_ - ) external nonReentrant returns (uint256 cdTokenIn, uint256 convertedTokenOut) { + ) external nonReentrant onlyActive returns (uint256 cdTokenIn, uint256 convertedTokenOut) { // Make sure the lengths of the arrays are the same if (positionIds_.length != amounts_.length) revert CDF_InvalidArgs("array length"); @@ -254,11 +272,20 @@ contract CDFacility is Policy, RolesConsumer, IConvertibleDepositFacility, Reent } /// @inheritdoc IConvertibleDepositFacility + /// @dev This function reverts if: + /// - The contract is not active + /// - The length of the positionIds_ array does not match the length of the amounts_ array + /// - The caller is not the owner of all of the positions + /// - The position is not valid + /// - The position is not CDEPO + /// - The position has not expired + /// - The deposit amount is greater than the remaining deposit + /// - The deposit amount is 0 function previewReclaim( address account_, uint256[] memory positionIds_, uint256[] memory amounts_ - ) external view returns (uint256 reclaimed, address cdTokenSpender) { + ) external view onlyActive returns (uint256 reclaimed, address cdTokenSpender) { // Make sure the lengths of the arrays are the same if (positionIds_.length != amounts_.length) revert CDF_InvalidArgs("array length"); @@ -276,6 +303,7 @@ contract CDFacility is Policy, RolesConsumer, IConvertibleDepositFacility, Reent /// @inheritdoc IConvertibleDepositFacility /// @dev This function reverts if: + /// - The contract is not active /// - The length of the positionIds_ array does not match the length of the amounts_ array /// - The caller is not the owner of all of the positions /// - The position is not valid @@ -286,7 +314,7 @@ contract CDFacility is Policy, RolesConsumer, IConvertibleDepositFacility, Reent function reclaim( uint256[] memory positionIds_, uint256[] memory amounts_ - ) external override nonReentrant returns (uint256 reclaimed) { + ) external nonReentrant onlyActive returns (uint256 reclaimed) { // Make sure the lengths of the arrays are the same if (positionIds_.length != amounts_.length) revert CDF_InvalidArgs("array length"); @@ -348,4 +376,45 @@ contract CDFacility is Policy, RolesConsumer, IConvertibleDepositFacility, Reent function convertedToken() external view returns (address) { return address(MINTR.ohm()); } + + // ========== ADMIN FUNCTIONS ========== // + + /// @notice Activate the contract functionality + /// @dev This function will revert if: + /// - The caller does not have the ROLE_EMERGENCY_SHUTDOWN role + /// + /// Note that if the contract is already active, this function will do nothing. + function activate() external onlyRole(ROLE_EMERGENCY_SHUTDOWN) { + // If the contract is already active, do nothing + if (locallyActive) return; + + // Set the contract to active + locallyActive = true; + + // Emit event + emit Activated(); + } + + /// @notice Deactivate the contract functionality + /// @dev This function will revert if: + /// - The caller does not have the ROLE_EMERGENCY_SHUTDOWN role + /// + /// Note that if the contract is already inactive, this function will do nothing. + function deactivate() external onlyRole(ROLE_EMERGENCY_SHUTDOWN) { + // If the contract is already inactive, do nothing + if (!locallyActive) return; + + // Set the contract to inactive + locallyActive = false; + + // Emit event + emit Deactivated(); + } + + // ========== MODIFIERS ========== // + + modifier onlyActive() { + if (!locallyActive) revert CDF_NotActive(); + _; + } } diff --git a/src/policies/interfaces/IConvertibleDepositFacility.sol b/src/policies/interfaces/IConvertibleDepositFacility.sol index a74ecf09..058645b1 100644 --- a/src/policies/interfaces/IConvertibleDepositFacility.sol +++ b/src/policies/interfaces/IConvertibleDepositFacility.sol @@ -1,9 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; -import {IERC20} from "openzeppelin-contracts/contracts/interfaces/IERC20.sol"; -import {IERC4626} from "openzeppelin-contracts/contracts/interfaces/IERC4626.sol"; - /// @title IConvertibleDepositFacility /// @notice Interface for a contract that can perform functions related to convertible deposit tokens interface IConvertibleDepositFacility { @@ -13,6 +10,9 @@ interface IConvertibleDepositFacility { event ConvertedDeposit(address indexed user, uint256 depositAmount, uint256 convertedAmount); event ReclaimedDeposit(address indexed user, uint256 reclaimedAmount); + event Activated(); + event Deactivated(); + // ========== ERRORS ========== // error CDF_InvalidArgs(string reason_); @@ -27,6 +27,8 @@ interface IConvertibleDepositFacility { error CDF_InvalidToken(uint256 positionId_, address token_); + error CDF_NotActive(); + // ========== CONVERTIBLE DEPOSIT ACTIONS ========== // /// @notice Creates a new convertible deposit position diff --git a/src/test/policies/ConvertibleDepositAuctioneer/ConvertibleDepositAuctioneerTest.sol b/src/test/policies/ConvertibleDepositAuctioneer/ConvertibleDepositAuctioneerTest.sol index d640b6b5..29ce9f9e 100644 --- a/src/test/policies/ConvertibleDepositAuctioneer/ConvertibleDepositAuctioneerTest.sol +++ b/src/test/policies/ConvertibleDepositAuctioneer/ConvertibleDepositAuctioneerTest.sol @@ -88,6 +88,10 @@ contract ConvertibleDepositAuctioneerTest is Test { rolesAdmin.grantRole(bytes32("cd_admin"), admin); rolesAdmin.grantRole(bytes32("emergency_shutdown"), emergency); rolesAdmin.grantRole(bytes32("cd_auctioneer"), address(auctioneer)); + + // Activate policy dependencies + vm.prank(emergency); + facility.activate(); } // ========== HELPERS ========== // diff --git a/src/test/policies/ConvertibleDepositFacility/ConvertibleDepositFacilityTest.sol b/src/test/policies/ConvertibleDepositFacility/ConvertibleDepositFacilityTest.sol index 4149f211..c1a46469 100644 --- a/src/test/policies/ConvertibleDepositFacility/ConvertibleDepositFacilityTest.sol +++ b/src/test/policies/ConvertibleDepositFacility/ConvertibleDepositFacilityTest.sol @@ -13,6 +13,7 @@ import {OlympusRoles} from "src/modules/ROLES/OlympusRoles.sol"; import {OlympusConvertibleDepository} from "src/modules/CDEPO/OlympusConvertibleDepository.sol"; import {OlympusConvertibleDepositPositions} from "src/modules/CDPOS/OlympusConvertibleDepositPositions.sol"; import {RolesAdmin} from "src/policies/RolesAdmin.sol"; +import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; contract ConvertibleDepositFacilityTest is Test { Kernel public kernel; @@ -31,6 +32,7 @@ contract ConvertibleDepositFacilityTest is Test { address public recipient = address(0x1); address public auctioneer = address(0x2); address public recipientTwo = address(0x3); + address public emergency = address(0x4); uint48 public constant INITIAL_BLOCK = 1_000_000; uint256 public constant CONVERSION_PRICE = 2e18; @@ -65,6 +67,7 @@ contract ConvertibleDepositFacilityTest is Test { // Grant roles rolesAdmin.grantRole(bytes32("cd_auctioneer"), auctioneer); + rolesAdmin.grantRole(bytes32("emergency_shutdown"), emergency); } // ========== MODIFIERS ========== // @@ -109,4 +112,22 @@ contract ConvertibleDepositFacilityTest is Test { convertibleDepository.approve(spender_, amount_); _; } + + modifier givenLocallyActive() { + vm.prank(emergency); + facility.activate(); + _; + } + + modifier givenLocallyInactive() { + vm.prank(emergency); + facility.deactivate(); + _; + } + + // ========== ASSERTIONS ========== // + + function _expectRoleRevert(bytes32 role_) internal { + vm.expectRevert(abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, role_)); + } } diff --git a/src/test/policies/ConvertibleDepositFacility/activate.t.sol b/src/test/policies/ConvertibleDepositFacility/activate.t.sol new file mode 100644 index 00000000..d327521e --- /dev/null +++ b/src/test/policies/ConvertibleDepositFacility/activate.t.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Unlicensed +pragma solidity 0.8.15; + +import {ConvertibleDepositFacilityTest} from "./ConvertibleDepositFacilityTest.sol"; + +contract ActivateCDFTest is ConvertibleDepositFacilityTest { + event Activated(); + + // given the caller does not have the emergency_shutdown role + // [X] it reverts + // given the contract is already active + // [X] it does nothing + // [X] it sets the contract to active + // [X] it emits an Activated event + + function test_callerDoesNotHaveRole_reverts() public { + _expectRoleRevert("emergency_shutdown"); + + // Call function + facility.activate(); + } + + function test_contractActive() public givenLocallyActive { + // Call function + vm.prank(emergency); + facility.activate(); + + // Assert state + assertEq(facility.locallyActive(), true, "active"); + } + + function test_success() public { + // Emits event + vm.expectEmit(true, true, true, true); + emit Activated(); + + // Call function + vm.prank(emergency); + facility.activate(); + + // Assert state + assertEq(facility.locallyActive(), true, "active"); + } +} diff --git a/src/test/policies/ConvertibleDepositFacility/constructor.t.sol b/src/test/policies/ConvertibleDepositFacility/constructor.t.sol new file mode 100644 index 00000000..fe7ce9c4 --- /dev/null +++ b/src/test/policies/ConvertibleDepositFacility/constructor.t.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Unlicensed +pragma solidity 0.8.15; + +import {ConvertibleDepositFacilityTest} from "./ConvertibleDepositFacilityTest.sol"; + +import {CDFacility} from "src/policies/CDFacility.sol"; + +contract ConstructorCDFTest is ConvertibleDepositFacilityTest { + // [X] it sets the contract to inactive + + function test_success() public { + facility = new CDFacility(address(kernel)); + + // Assert state + assertEq(facility.locallyActive(), false, "inactive"); + } +} diff --git a/src/test/policies/ConvertibleDepositFacility/convert.t.sol b/src/test/policies/ConvertibleDepositFacility/convert.t.sol index 48e4ed4a..187df565 100644 --- a/src/test/policies/ConvertibleDepositFacility/convert.t.sol +++ b/src/test/policies/ConvertibleDepositFacility/convert.t.sol @@ -10,6 +10,8 @@ import {MINTRv1} from "src/modules/MINTR/MINTR.v1.sol"; contract ConvertCDFTest is ConvertibleDepositFacilityTest { event ConvertedDeposit(address indexed user, uint256 depositAmount, uint256 convertedAmount); + // given the contract is inactive + // [X] it reverts // when the length of the positionIds_ array does not match the length of the amounts_ array // [X] it reverts // when any position is not valid @@ -30,7 +32,15 @@ contract ConvertCDFTest is ConvertibleDepositFacilityTest { // [X] it returns the total deposit amount and the converted amount // [X] it emits a ConvertedDeposit event - function test_arrayLengthMismatch_reverts() public { + function test_contractInactive_reverts() public { + // Expect revert + vm.expectRevert(abi.encodeWithSelector(IConvertibleDepositFacility.CDF_NotActive.selector)); + + // Call function + facility.convert(new uint256[](0), new uint256[](0)); + } + + function test_arrayLengthMismatch_reverts() public givenLocallyActive { uint256[] memory positionIds_ = new uint256[](1); uint256[] memory amounts_ = new uint256[](2); @@ -51,6 +61,7 @@ contract ConvertCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) givenReserveTokenSpendingIsApproved( recipient, @@ -90,6 +101,7 @@ contract ConvertCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 10e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 10e18) givenAddressHasReserveToken(recipientTwo, 5e18) @@ -124,6 +136,7 @@ contract ConvertCDFTest is ConvertibleDepositFacilityTest { function test_allPositionsHaveDifferentOwner_reverts() public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) @@ -154,6 +167,7 @@ contract ConvertCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) { @@ -195,6 +209,7 @@ contract ConvertCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) @@ -235,6 +250,7 @@ contract ConvertCDFTest is ConvertibleDepositFacilityTest { function test_spendingIsNotApproved_reverts() public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) givenReserveTokenSpendingIsApproved( recipient, @@ -267,6 +283,7 @@ contract ConvertCDFTest is ConvertibleDepositFacilityTest { function test_convertedAmountIsZero_reverts() public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) givenReserveTokenSpendingIsApproved( recipient, @@ -296,6 +313,7 @@ contract ConvertCDFTest is ConvertibleDepositFacilityTest { function test_success() public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) givenReserveTokenSpendingIsApproved( recipient, @@ -392,6 +410,7 @@ contract ConvertCDFTest is ConvertibleDepositFacilityTest { uint256 amountTwo_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) givenReserveTokenSpendingIsApproved( recipient, diff --git a/src/test/policies/ConvertibleDepositFacility/create.t.sol b/src/test/policies/ConvertibleDepositFacility/create.t.sol index 723ef195..216e8c4d 100644 --- a/src/test/policies/ConvertibleDepositFacility/create.t.sol +++ b/src/test/policies/ConvertibleDepositFacility/create.t.sol @@ -2,11 +2,15 @@ pragma solidity 0.8.15; import {ConvertibleDepositFacilityTest} from "./ConvertibleDepositFacilityTest.sol"; + import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; +import {IConvertibleDepositFacility} from "src/policies/interfaces/IConvertibleDepositFacility.sol"; contract CreateCDFTest is ConvertibleDepositFacilityTest { event CreatedDeposit(address indexed user, uint256 indexed termId, uint256 amount); + // given the contract is inactive + // [X] it reverts // when the caller does not have the cd_auctioneer role // [X] it reverts // when the recipient has not approved CDEPO to spend the reserve tokens @@ -19,8 +23,18 @@ contract CreateCDFTest is ConvertibleDepositFacilityTest { // [X] it returns the position ID // [X] it emits a CreatedDeposit event + function test_contractInactive_reverts() public { + // Expect revert + vm.expectRevert(abi.encodeWithSelector(IConvertibleDepositFacility.CDF_NotActive.selector)); + + // Call function + vm.prank(auctioneer); + facility.create(recipient, RESERVE_TOKEN_AMOUNT, CONVERSION_PRICE, EXPIRY, false); + } + function test_callerNotAuctioneer_reverts() public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) { // Expect revert @@ -34,6 +48,7 @@ contract CreateCDFTest is ConvertibleDepositFacilityTest { function test_spendingNotApproved_reverts() public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) { // Expect revert @@ -45,6 +60,7 @@ contract CreateCDFTest is ConvertibleDepositFacilityTest { function test_success() public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) givenReserveTokenSpendingIsApproved( recipient, @@ -96,6 +112,7 @@ contract CreateCDFTest is ConvertibleDepositFacilityTest { function test_success_multiple() public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) givenReserveTokenSpendingIsApproved( recipient, diff --git a/src/test/policies/ConvertibleDepositFacility/deactivate.t.sol b/src/test/policies/ConvertibleDepositFacility/deactivate.t.sol new file mode 100644 index 00000000..cc7650d5 --- /dev/null +++ b/src/test/policies/ConvertibleDepositFacility/deactivate.t.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Unlicensed +pragma solidity 0.8.15; + +import {ConvertibleDepositFacilityTest} from "./ConvertibleDepositFacilityTest.sol"; + +contract DeactivateCDFTest is ConvertibleDepositFacilityTest { + event Deactivated(); + + // given the caller does not have the emergency_shutdown role + // [X] it reverts + // given the contract is already inactive + // [X] it does nothing + // [X] it sets the contract to inactive + // [X] it emits a Deactivated event + + function test_callerDoesNotHaveRole_reverts() public { + _expectRoleRevert("emergency_shutdown"); + + facility.deactivate(); + } + + function test_contractInactive() public { + vm.prank(emergency); + facility.deactivate(); + + assertEq(facility.locallyActive(), false, "inactive"); + } + + function test_success() public givenLocallyActive { + vm.prank(emergency); + facility.deactivate(); + + assertEq(facility.locallyActive(), false, "inactive"); + } +} diff --git a/src/test/policies/ConvertibleDepositFacility/previewConvert.t.sol b/src/test/policies/ConvertibleDepositFacility/previewConvert.t.sol index b5204de6..8021f1a7 100644 --- a/src/test/policies/ConvertibleDepositFacility/previewConvert.t.sol +++ b/src/test/policies/ConvertibleDepositFacility/previewConvert.t.sol @@ -6,6 +6,8 @@ import {IConvertibleDepositFacility} from "src/policies/interfaces/IConvertibleD import {CDPOSv1} from "src/modules/CDPOS/CDPOS.v1.sol"; contract PreviewConvertCDFTest is ConvertibleDepositFacilityTest { + // given the contract is inactive + // [X] it reverts // when the length of the positionIds_ array does not match the length of the amounts_ array // [X] it reverts // when any position is not valid @@ -24,7 +26,15 @@ contract PreviewConvertCDFTest is ConvertibleDepositFacilityTest { // [X] it returns the amount of OHM that would be minted // [X] it returns the address that will spend the convertible deposit tokens - function test_arrayLengthMismatch_reverts() public { + function test_contractInactive_reverts() public { + // Expect revert + vm.expectRevert(abi.encodeWithSelector(IConvertibleDepositFacility.CDF_NotActive.selector)); + + // Call function + facility.previewConvert(recipient, new uint256[](0), new uint256[](0)); + } + + function test_arrayLengthMismatch_reverts() public givenLocallyActive { uint256[] memory positionIds_ = new uint256[](1); uint256[] memory amounts_ = new uint256[](2); @@ -44,6 +54,7 @@ contract PreviewConvertCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) givenReserveTokenSpendingIsApproved( recipient, @@ -82,6 +93,7 @@ contract PreviewConvertCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasReserveToken(recipientTwo, 9e18) @@ -115,6 +127,7 @@ contract PreviewConvertCDFTest is ConvertibleDepositFacilityTest { function test_allPositionsHaveDifferentOwner_reverts() public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) @@ -144,6 +157,7 @@ contract PreviewConvertCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) { @@ -184,6 +198,7 @@ contract PreviewConvertCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) @@ -223,6 +238,7 @@ contract PreviewConvertCDFTest is ConvertibleDepositFacilityTest { function test_amountIsZero_reverts() public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) @@ -244,6 +260,7 @@ contract PreviewConvertCDFTest is ConvertibleDepositFacilityTest { function test_convertedAmountIsZero_reverts() public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) @@ -272,6 +289,7 @@ contract PreviewConvertCDFTest is ConvertibleDepositFacilityTest { uint256 amountThree_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) diff --git a/src/test/policies/ConvertibleDepositFacility/previewReclaim.t.sol b/src/test/policies/ConvertibleDepositFacility/previewReclaim.t.sol index 7c6d9b74..3f9061f6 100644 --- a/src/test/policies/ConvertibleDepositFacility/previewReclaim.t.sol +++ b/src/test/policies/ConvertibleDepositFacility/previewReclaim.t.sol @@ -6,6 +6,8 @@ import {IConvertibleDepositFacility} from "src/policies/interfaces/IConvertibleD import {CDPOSv1} from "src/modules/CDPOS/CDPOS.v1.sol"; contract PreviewReclaimCDFTest is ConvertibleDepositFacilityTest { + // given the contract is inactive + // [X] it reverts // when the length of the positionIds_ array does not match the length of the amounts_ array // [X] it reverts // when the account_ is not the owner of all of the positions @@ -21,7 +23,15 @@ contract PreviewReclaimCDFTest is ConvertibleDepositFacilityTest { // [X] it returns the total amount of deposit token that would be reclaimed // [X] it returns the address that will spend the convertible deposit tokens - function test_arrayLengthMismatch_reverts() public { + function test_contractInactive_reverts() public { + // Expect revert + vm.expectRevert(abi.encodeWithSelector(IConvertibleDepositFacility.CDF_NotActive.selector)); + + // Call function + facility.previewReclaim(recipient, new uint256[](0), new uint256[](0)); + } + + function test_arrayLengthMismatch_reverts() public givenLocallyActive { uint256[] memory positionIds_ = new uint256[](1); uint256[] memory amounts_ = new uint256[](2); @@ -41,6 +51,7 @@ contract PreviewReclaimCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 10e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 10e18) givenAddressHasReserveToken(recipientTwo, 5e18) @@ -77,6 +88,7 @@ contract PreviewReclaimCDFTest is ConvertibleDepositFacilityTest { function test_allPositionsHaveDifferentOwner_reverts() public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) @@ -109,6 +121,7 @@ contract PreviewReclaimCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) givenReserveTokenSpendingIsApproved( recipient, @@ -150,6 +163,7 @@ contract PreviewReclaimCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) { @@ -190,6 +204,7 @@ contract PreviewReclaimCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) @@ -232,6 +247,7 @@ contract PreviewReclaimCDFTest is ConvertibleDepositFacilityTest { function test_amountIsZero_reverts() public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) @@ -260,6 +276,7 @@ contract PreviewReclaimCDFTest is ConvertibleDepositFacilityTest { uint256 amountThree_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) diff --git a/src/test/policies/ConvertibleDepositFacility/reclaim.t.sol b/src/test/policies/ConvertibleDepositFacility/reclaim.t.sol index b589a538..71165b0e 100644 --- a/src/test/policies/ConvertibleDepositFacility/reclaim.t.sol +++ b/src/test/policies/ConvertibleDepositFacility/reclaim.t.sol @@ -9,6 +9,8 @@ import {CDEPOv1} from "src/modules/CDEPO/CDEPO.v1.sol"; contract ReclaimCDFTest is ConvertibleDepositFacilityTest { event ReclaimedDeposit(address indexed user, uint256 reclaimedAmount); + // given the contract is inactive + // [X] it reverts // when the length of the positionIds_ array does not match the length of the amounts_ array // [X] it reverts // when any position is not valid @@ -29,7 +31,15 @@ contract ReclaimCDFTest is ConvertibleDepositFacilityTest { // [X] it returns the reclaimed amount // [X] it emits a ReclaimedDeposit event - function test_arrayLengthMismatch_reverts() public { + function test_contractInactive_reverts() public { + // Expect revert + vm.expectRevert(abi.encodeWithSelector(IConvertibleDepositFacility.CDF_NotActive.selector)); + + // Call function + facility.reclaim(new uint256[](0), new uint256[](0)); + } + + function test_arrayLengthMismatch_reverts() public givenLocallyActive { uint256[] memory positionIds_ = new uint256[](1); uint256[] memory amounts_ = new uint256[](2); @@ -50,6 +60,7 @@ contract ReclaimCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 10e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 10e18) givenAddressHasReserveToken(recipientTwo, 5e18) @@ -87,6 +98,7 @@ contract ReclaimCDFTest is ConvertibleDepositFacilityTest { function test_allPositionsHaveDifferentOwner_reverts() public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) @@ -120,6 +132,7 @@ contract ReclaimCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) givenReserveTokenSpendingIsApproved( recipient, @@ -162,6 +175,7 @@ contract ReclaimCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) { @@ -203,6 +217,7 @@ contract ReclaimCDFTest is ConvertibleDepositFacilityTest { uint256 positionIndex_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) @@ -246,6 +261,7 @@ contract ReclaimCDFTest is ConvertibleDepositFacilityTest { function test_amountIsZero_reverts() public + givenLocallyActive givenAddressHasReserveToken(recipient, 9e18) givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 9e18) givenAddressHasPosition(recipient, 3e18) @@ -269,6 +285,7 @@ contract ReclaimCDFTest is ConvertibleDepositFacilityTest { function test_spendingIsNotApproved_reverts() public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) givenReserveTokenSpendingIsApproved( recipient, @@ -296,6 +313,7 @@ contract ReclaimCDFTest is ConvertibleDepositFacilityTest { function test_success() public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) givenReserveTokenSpendingIsApproved( recipient, @@ -389,6 +407,7 @@ contract ReclaimCDFTest is ConvertibleDepositFacilityTest { uint256 amountTwo_ ) public + givenLocallyActive givenAddressHasReserveToken(recipient, RESERVE_TOKEN_AMOUNT) givenReserveTokenSpendingIsApproved( recipient,