From 0b142d46a569e4e150d6513285cc2d34d5323f15 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 26 Sep 2024 16:17:05 +0400 Subject: [PATCH 001/160] Add tests for third-party clearinghouse/cooler and active status --- src/external/cooler/CoolerUtils.sol | 30 +++++++++- src/test/external/cooler/CoolerUtils.t.sol | 64 ++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/external/cooler/CoolerUtils.sol b/src/external/cooler/CoolerUtils.sol index c9eae0e13..b325c6627 100644 --- a/src/external/cooler/CoolerUtils.sol +++ b/src/external/cooler/CoolerUtils.sol @@ -31,6 +31,9 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { /// @notice Thrown when the caller is not the Cooler owner. error OnlyCoolerOwner(); + /// @notice Thrown when the contract is not active. + error OnlyActive(); + /// @notice Thrown when the fee percentage is out of range. /// @dev Valid values are 0 <= feePercentage <= 100e2 error Params_FeePercentageOutOfRange(); @@ -44,6 +47,10 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { /// @notice Thrown when the caller attempts to consolidate too few cooler loans. The minimum is two. error InsufficientCoolerCount(); + error Params_InvalidClearinghouse(); + + error Params_InvalidCooler(); + // --- DATA STRUCTURES --------------------------------------------------------- struct Batch { @@ -69,12 +76,16 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { uint256 public constant ONE_HUNDRED_PERCENT = 100e2; - // protocol fees + /// @notice Percentage of the debt to be paid as a fee + /// @dev In terms of `ONE_HUNDRED_PERCENT` uint256 public feePercentage; /// @notice Address permitted to collect protocol fees address public collector; + /// @notice Whether the contract is active + bool public active; + // --- INITIALIZATION ---------------------------------------------------------- constructor( @@ -394,4 +405,21 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { function VERSION() external pure returns (uint256) { return 3; } + + // --- ADMIN --------------------------------------------------------------- + + // TODO add admin addresses + + function activate() external onlyOwner { + active = true; + } + + function deactivate() external onlyOwner { + active = false; + } + + modifier onlyActive() { + if (!active) revert OnlyActive(); + _; + } } diff --git a/src/test/external/cooler/CoolerUtils.t.sol b/src/test/external/cooler/CoolerUtils.t.sol index 496545af3..a2876e229 100644 --- a/src/test/external/cooler/CoolerUtils.t.sol +++ b/src/test/external/cooler/CoolerUtils.t.sol @@ -17,6 +17,7 @@ import {CoolerUtils} from "src/external/cooler/CoolerUtils.sol"; contract CoolerUtilsTest is Test { CoolerUtils public utils; + ERC20 public ohm; ERC20 public gohm; ERC20 public dai; IERC4626 public sdai; @@ -24,6 +25,8 @@ contract CoolerUtilsTest is Test { CoolerFactory public coolerFactory; Clearinghouse public clearinghouse; + address public staking; + address public kernel; address public owner; address public lender; address public collector; @@ -43,10 +46,14 @@ contract CoolerUtilsTest is Test { // Required Contracts coolerFactory = CoolerFactory(0x30Ce56e80aA96EbbA1E1a74bC5c0FEB5B0dB4216); clearinghouse = Clearinghouse(0xE6343ad0675C9b8D3f32679ae6aDbA0766A2ab4c); + + ohm = ERC20(0x64aa3364F17a4D01c6f1751Fd97C2BD3D7e7f1D5); gohm = ERC20(0x0ab87046fBb341D058F17CBC4c1133F25a20a52f); dai = ERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); sdai = IERC4626(0x83F20F44975D03b1b09e64809B757c47f942BEeA); lender = 0x60744434d6339a6B27d73d9Eda62b6F66a0a04FA; + kernel = 0x2286d7f9639e8158FaD1169e76d1FbC38247f54b; + staking = 0xB63cac384247597756545b500253ff8E607a8020; owner = vm.addr(0x1); collector = vm.addr(0xC); @@ -243,6 +250,12 @@ contract CoolerUtilsTest is Test { // ===== TESTS ===== // // consolidateWithFlashLoan + // given the contract has been disabled + // [X] it reverts + // when the clearinghouse is not registered with CHREG + // [X] it reverts + // when the cooler was not created by a valid CoolerFactory + // [X] it reverts // given the caller has no loans // [X] it reverts // given the caller has 1 loan @@ -278,6 +291,57 @@ contract CoolerUtilsTest is Test { // --- consolidateWithFlashLoan -------------------------------------------- + function test_consolidate_deactivated_reverts() public { + // Deactivate the CoolerUtils contract + vm.prank(owner); + utils.deactivate(); + + // Expect revert + bytes memory err = abi.encodeWithSelector(CoolerUtils.OnlyActive.selector); + vm.expectRevert(err); + + // Consolidate loans for coolerA + uint256[] memory idsA = _idsA(); + _consolidate(idsA); + } + + function test_consolidate_thirdPartyClearinghouse_reverts() public { + // Create a new Clearinghouse + // It is not registered with CHREG, so should be rejected + Clearinghouse newClearinghouse = new Clearinghouse( + address(ohm), + address(gohm), + staking, + address(sdai), + address(coolerFactory), + kernel + ); + + // Expect revert + bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidClearinghouse.selector); + vm.expectRevert(err); + + // Consolidate loans for coolers A, B, and C into coolerC + uint256[] memory idsA = _idsA(); + vm.prank(walletA); + utils.consolidateWithFlashLoan(address(newClearinghouse), address(coolerA), idsA, 0, false); + } + + function test_consolidate_thirdPartyCooler_reverts() public { + // Create a new Cooler + // It was not created by the Clearinghouse's CoolerFactory, so should be rejected + Cooler newCooler = new Cooler(); + + // Expect revert + bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidCooler.selector); + vm.expectRevert(err); + + // Consolidate loans for coolerA into newCooler + uint256[] memory idsA = _idsA(); + vm.prank(walletA); + utils.consolidateWithFlashLoan(address(clearinghouse), address(newCooler), idsA, 0, false); + } + function test_consolidate_noLoans_reverts() public { // Grant approvals _grantCallerApprovals(type(uint256).max, type(uint256).max); From 51b25b44f9053d895db68c2c2ec0f7b396cd2620 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 26 Sep 2024 16:41:23 +0400 Subject: [PATCH 002/160] Compile fix due to new constructor argument --- src/scripts/deploy/DeployV2.sol | 2 ++ src/test/external/cooler/CoolerUtils.t.sol | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/scripts/deploy/DeployV2.sol b/src/scripts/deploy/DeployV2.sol index c7dd96d41..c340ceae8 100644 --- a/src/scripts/deploy/DeployV2.sol +++ b/src/scripts/deploy/DeployV2.sol @@ -1041,6 +1041,7 @@ contract OlympusDeploy is Script { console2.log(" Collector:", collector); console2.log(" Fee Percentage:", feePercentage); console2.log(" Lender:", lender); + console2.log(" Kernel:", address(kernel)); console2.log(" Owner:", owner); // Deploy CoolerUtils @@ -1052,6 +1053,7 @@ contract OlympusDeploy is Script { owner, lender, collector, + address(kernel), feePercentage ); console2.log(" CoolerUtils deployed at:", address(coolerUtils)); diff --git a/src/test/external/cooler/CoolerUtils.t.sol b/src/test/external/cooler/CoolerUtils.t.sol index a2876e229..ce2a2e56c 100644 --- a/src/test/external/cooler/CoolerUtils.t.sol +++ b/src/test/external/cooler/CoolerUtils.t.sol @@ -66,6 +66,7 @@ contract CoolerUtilsTest is Test { owner, lender, collector, + kernel, 0 ); @@ -1208,7 +1209,7 @@ contract CoolerUtilsTest is Test { bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils(address(0), address(sdai), address(dai), owner, lender, collector, 0); + new CoolerUtils(address(0), address(sdai), address(dai), owner, lender, collector, kernel, 0); } function test_constructor_zeroSDai_reverts() public { @@ -1216,7 +1217,7 @@ contract CoolerUtilsTest is Test { bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils(address(gohm), address(0), address(dai), owner, lender, collector, 0); + new CoolerUtils(address(gohm), address(0), address(dai), owner, lender, collector, kernel, 0); } function test_constructor_zeroDai_reverts() public { @@ -1224,7 +1225,7 @@ contract CoolerUtilsTest is Test { bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils(address(gohm), address(sdai), address(0), owner, lender, collector, 0); + new CoolerUtils(address(gohm), address(sdai), address(0), owner, lender, collector, kernel, 0); } function test_constructor_zeroOwner_reverts() public { @@ -1239,6 +1240,7 @@ contract CoolerUtilsTest is Test { address(0), lender, collector, + kernel, 0 ); } @@ -1255,6 +1257,7 @@ contract CoolerUtilsTest is Test { owner, address(0), collector, + kernel, 0 ); } @@ -1264,7 +1267,7 @@ contract CoolerUtilsTest is Test { bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils(address(gohm), address(sdai), address(dai), owner, lender, address(0), 0); + new CoolerUtils(address(gohm), address(sdai), address(dai), owner, lender, address(0), kernel, 0); } function test_constructor_feePercentageAboveMax_reverts() public { @@ -1281,6 +1284,7 @@ contract CoolerUtilsTest is Test { owner, lender, collector, + kernel, _ONE_HUNDRED_PERCENT + 1 ); } @@ -1295,6 +1299,7 @@ contract CoolerUtilsTest is Test { owner, lender, collector, + kernel, feePercentage ); From e97af509b9868be72bdad67e240f5c8b5621fd81 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 26 Sep 2024 16:41:54 +0400 Subject: [PATCH 003/160] Add checks for Clearinghouse, Cooler and active status. Renames some events. --- src/external/cooler/CoolerUtils.sol | 129 ++++++++++++++++----- src/test/external/cooler/CoolerUtils.t.sol | 8 +- 2 files changed, 107 insertions(+), 30 deletions(-) diff --git a/src/external/cooler/CoolerUtils.sol b/src/external/cooler/CoolerUtils.sol index b325c6627..584a73a94 100644 --- a/src/external/cooler/CoolerUtils.sol +++ b/src/external/cooler/CoolerUtils.sol @@ -4,11 +4,14 @@ pragma solidity ^0.8.15; import {IERC20} from "forge-std/interfaces/IERC20.sol"; import {IERC4626} from "forge-std/interfaces/IERC4626.sol"; import {Owned} from "solmate/auth/Owned.sol"; +import {Kernel, Module, toKeycode} from "src/Kernel.sol"; +import {CHREGv1} from "src/modules/CHREG/CHREG.v1.sol"; import {IERC3156FlashBorrower} from "src/interfaces/maker-dao/IERC3156FlashBorrower.sol"; import {IERC3156FlashLender} from "src/interfaces/maker-dao/IERC3156FlashLender.sol"; import {Clearinghouse} from "src/policies/Clearinghouse.sol"; import {Cooler} from "src/external/cooler/Cooler.sol"; +import {CoolerFactory} from "src/external/cooler/CoolerFactory.sol"; // // ██████╗ ██████╗ ██████╗ ██╗ ███████╗██████╗ ██╗ ██╗████████╗██╗██╗ ███████╗ @@ -45,12 +48,28 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { error Params_UseFundsOutOfBounds(); /// @notice Thrown when the caller attempts to consolidate too few cooler loans. The minimum is two. - error InsufficientCoolerCount(); + error Params_InsufficientCoolerCount(); + /// @notice Thrown when the Clearinghouse is not registered with the Bophades kernel error Params_InvalidClearinghouse(); + /// @notice Thrown when the Cooler is not created by the CoolerFactory for the specified Clearinghouse error Params_InvalidCooler(); + // --- EVENTS --------------------------------------------------------- + + /// @notice Emitted when the contract is activated + event Activated(); + + /// @notice Emitted when the contract is deactivated + event Deactivated(); + + /// @notice Emitted when the fee percentage is set + event FeePercentageSet(uint256 feePercentage); + + /// @notice Emitted when the collector is set + event CollectorSet(address collector); + // --- DATA STRUCTURES --------------------------------------------------------- struct Batch { @@ -73,6 +92,7 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { IERC20 public immutable gohm; IERC4626 public immutable sdai; IERC20 public immutable dai; + Kernel public immutable kernel; uint256 public constant ONE_HUNDRED_PERCENT = 100e2; @@ -95,6 +115,7 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { address owner_, address lender_, address collector_, + address kernel_, uint256 feePercentage_ ) Owned(owner_) { // Validation @@ -105,6 +126,7 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { if (gohm_ == address(0)) revert Params_InvalidAddress(); if (sdai_ == address(0)) revert Params_InvalidAddress(); if (dai_ == address(0)) revert Params_InvalidAddress(); + if (kernel_ == address(0)) revert Params_InvalidAddress(); // store contracts gohm = IERC20(gohm_); @@ -117,6 +139,15 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { owner = owner_; collector = collector_; feePercentage = feePercentage_; + kernel = Kernel(kernel_); + + // Activate the contract + active = true; + + // Emit events + emit FeePercentageSet(feePercentage); + emit CollectorSet(collector); + emit Activated(); } // --- OPERATION --------------------------------------------------------------- @@ -140,19 +171,24 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { uint256[] calldata ids_, uint256 useFunds_, bool sdai_ - ) public { - Cooler cooler = Cooler(cooler_); + ) public onlyActive { + // Validate that the Clearinghouse is registered with the Bophades kernel + if (!_isValidClearinghouse(clearinghouse_)) revert Params_InvalidClearinghouse(); + + // Validate that the cooler was created by the CoolerFactory for the Clearinghouse + if (!_isValidCooler(clearinghouse_, cooler_)) revert Params_InvalidCooler(); // Ensure at least two loans are being consolidated - if (ids_.length < 2) revert InsufficientCoolerCount(); + if (ids_.length < 2) revert Params_InsufficientCoolerCount(); // Cache batch debt and principal - (uint256 totalDebt, uint256 totalPrincipal) = _getDebtForLoans(address(cooler), ids_); + (uint256 totalDebt, uint256 totalPrincipal) = _getDebtForLoans(cooler_, ids_); // Grant approval to the Cooler to spend the debt - dai.approve(address(cooler), totalDebt); + dai.approve(cooler_, totalDebt); // Ensure `msg.sender` is allowed to spend cooler funds on behalf of this contract + Cooler cooler = Cooler(cooler_); if (cooler.owner() != msg.sender) revert OnlyCoolerOwner(); // Transfer in necessary funds to repay the fee @@ -250,12 +286,37 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { if (feePercentage_ > ONE_HUNDRED_PERCENT) revert Params_FeePercentageOutOfRange(); feePercentage = feePercentage_; + emit FeePercentageSet(feePercentage_); } function setCollector(address collector_) external onlyOwner { if (collector_ == address(0)) revert Params_InvalidAddress(); collector = collector_; + emit CollectorSet(collector_); + } + + // TODO add admin addresses + + function activate() external onlyOwner { + // Skip if already activated + if (active) return; + + active = true; + emit Activated(); + } + + function deactivate() external onlyOwner { + // Skip if already deactivated + if (!active) return; + + active = false; + emit Deactivated(); + } + + modifier onlyActive() { + if (!active) revert OnlyActive(); + _; } // --- INTERNAL FUNCTIONS ------------------------------------------------------ @@ -301,6 +362,39 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { gohm.transferFrom(cooler.owner(), address(this), totalCollateral); } + function _isValidClearinghouse(address clearinghouse_) internal view returns (bool) { + // Get the Clearinghouse registry from the kernel + Module module = kernel.getModuleForKeycode(toKeycode("CHREG")); + if (address(module) == address(0)) revert Params_InvalidClearinghouse(); + + CHREGv1 chreg = CHREGv1(address(module)); + + // We check against the registry (not just active), as repayments are still allowed when a Clearinghouse is deactivated + uint256 registryCount = chreg.registryCount(); + bool found; + for (uint256 i; i < registryCount; i++) { + if (chreg.registry(i) == clearinghouse_) { + found = true; + break; + } + } + + return found; + } + + /// @notice Check if a given cooler was created by the CoolerFactory for a Clearinghouse + /// @dev This function assumes that the authenticity of the Clearinghouse is already verified + /// + /// @param clearinghouse_ Clearinghouse contract + /// @param cooler_ Cooler contract + /// @return bool Whether the cooler was created by the CoolerFactory for the Clearinghouse + function _isValidCooler(address clearinghouse_, address cooler_) internal view returns (bool) { + Clearinghouse clearinghouse = Clearinghouse(clearinghouse_); + CoolerFactory coolerFactory = CoolerFactory(clearinghouse.factory()); + + return coolerFactory.created(cooler_); + } + // --- AUX FUNCTIONS ----------------------------------------------------------- /// @notice View function to compute the protocol fee for a given total debt. @@ -323,7 +417,7 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { address cooler_, uint256[] calldata ids_ ) external view returns (address, uint256, uint256, uint256, uint256) { - if (ids_.length < 2) revert InsufficientCoolerCount(); + if (ids_.length < 2) revert Params_InsufficientCoolerCount(); uint256 totalPrincipal; uint256 totalDebtWithInterest; @@ -376,7 +470,7 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { uint256 additionalCollateral ) { - if (ids_.length == 0) revert InsufficientCoolerCount(); + if (ids_.length == 0) revert Params_InsufficientCoolerCount(); // Calculate the total principal of the existing loans uint256 totalPrincipal; @@ -403,23 +497,6 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { /// /// @return Version number function VERSION() external pure returns (uint256) { - return 3; - } - - // --- ADMIN --------------------------------------------------------------- - - // TODO add admin addresses - - function activate() external onlyOwner { - active = true; - } - - function deactivate() external onlyOwner { - active = false; - } - - modifier onlyActive() { - if (!active) revert OnlyActive(); - _; + return 4; } } diff --git a/src/test/external/cooler/CoolerUtils.t.sol b/src/test/external/cooler/CoolerUtils.t.sol index ce2a2e56c..ef34ea2cf 100644 --- a/src/test/external/cooler/CoolerUtils.t.sol +++ b/src/test/external/cooler/CoolerUtils.t.sol @@ -348,7 +348,7 @@ contract CoolerUtilsTest is Test { _grantCallerApprovals(type(uint256).max, type(uint256).max); // Expect revert since no loan ids are given - bytes memory err = abi.encodeWithSelector(CoolerUtils.InsufficientCoolerCount.selector); + bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InsufficientCoolerCount.selector); vm.expectRevert(err); // Consolidate loans, but give no ids @@ -361,7 +361,7 @@ contract CoolerUtilsTest is Test { _grantCallerApprovals(type(uint256).max, type(uint256).max); // Expect revert since no loan ids are given - bytes memory err = abi.encodeWithSelector(CoolerUtils.InsufficientCoolerCount.selector); + bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InsufficientCoolerCount.selector); vm.expectRevert(err); // Consolidate loans, but give one id @@ -976,7 +976,7 @@ contract CoolerUtilsTest is Test { uint256[] memory ids = new uint256[](0); // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.InsufficientCoolerCount.selector); + bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InsufficientCoolerCount.selector); vm.expectRevert(err); utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); @@ -987,7 +987,7 @@ contract CoolerUtilsTest is Test { ids[0] = 0; // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.InsufficientCoolerCount.selector); + bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InsufficientCoolerCount.selector); vm.expectRevert(err); utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); From d7eaf09aaf0855e74418b5f144ae378043ae9a2c Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 26 Sep 2024 16:55:11 +0400 Subject: [PATCH 004/160] chore: linting --- src/test/external/cooler/CoolerUtils.t.sol | 60 +++++++++++++++++++--- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/src/test/external/cooler/CoolerUtils.t.sol b/src/test/external/cooler/CoolerUtils.t.sol index ef34ea2cf..6161c2d65 100644 --- a/src/test/external/cooler/CoolerUtils.t.sol +++ b/src/test/external/cooler/CoolerUtils.t.sol @@ -348,7 +348,9 @@ contract CoolerUtilsTest is Test { _grantCallerApprovals(type(uint256).max, type(uint256).max); // Expect revert since no loan ids are given - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InsufficientCoolerCount.selector); + bytes memory err = abi.encodeWithSelector( + CoolerUtils.Params_InsufficientCoolerCount.selector + ); vm.expectRevert(err); // Consolidate loans, but give no ids @@ -361,7 +363,9 @@ contract CoolerUtilsTest is Test { _grantCallerApprovals(type(uint256).max, type(uint256).max); // Expect revert since no loan ids are given - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InsufficientCoolerCount.selector); + bytes memory err = abi.encodeWithSelector( + CoolerUtils.Params_InsufficientCoolerCount.selector + ); vm.expectRevert(err); // Consolidate loans, but give one id @@ -976,7 +980,9 @@ contract CoolerUtilsTest is Test { uint256[] memory ids = new uint256[](0); // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InsufficientCoolerCount.selector); + bytes memory err = abi.encodeWithSelector( + CoolerUtils.Params_InsufficientCoolerCount.selector + ); vm.expectRevert(err); utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); @@ -987,7 +993,9 @@ contract CoolerUtilsTest is Test { ids[0] = 0; // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InsufficientCoolerCount.selector); + bytes memory err = abi.encodeWithSelector( + CoolerUtils.Params_InsufficientCoolerCount.selector + ); vm.expectRevert(err); utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); @@ -1209,7 +1217,16 @@ contract CoolerUtilsTest is Test { bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils(address(0), address(sdai), address(dai), owner, lender, collector, kernel, 0); + new CoolerUtils( + address(0), + address(sdai), + address(dai), + owner, + lender, + collector, + kernel, + 0 + ); } function test_constructor_zeroSDai_reverts() public { @@ -1217,7 +1234,16 @@ contract CoolerUtilsTest is Test { bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils(address(gohm), address(0), address(dai), owner, lender, collector, kernel, 0); + new CoolerUtils( + address(gohm), + address(0), + address(dai), + owner, + lender, + collector, + kernel, + 0 + ); } function test_constructor_zeroDai_reverts() public { @@ -1225,7 +1251,16 @@ contract CoolerUtilsTest is Test { bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils(address(gohm), address(sdai), address(0), owner, lender, collector, kernel, 0); + new CoolerUtils( + address(gohm), + address(sdai), + address(0), + owner, + lender, + collector, + kernel, + 0 + ); } function test_constructor_zeroOwner_reverts() public { @@ -1267,7 +1302,16 @@ contract CoolerUtilsTest is Test { bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils(address(gohm), address(sdai), address(dai), owner, lender, address(0), kernel, 0); + new CoolerUtils( + address(gohm), + address(sdai), + address(dai), + owner, + lender, + address(0), + kernel, + 0 + ); } function test_constructor_feePercentageAboveMax_reverts() public { From f06fef537e080469ec2a68de985f801297558bb8 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 26 Sep 2024 16:55:39 +0400 Subject: [PATCH 005/160] Test stubs. Define additional admin address. --- src/external/cooler/CoolerUtils.sol | 54 ++++++++++++++++++++-- src/test/external/cooler/CoolerUtils.t.sol | 34 ++++++++++++++ 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/src/external/cooler/CoolerUtils.sol b/src/external/cooler/CoolerUtils.sol index 584a73a94..2fbe81b63 100644 --- a/src/external/cooler/CoolerUtils.sol +++ b/src/external/cooler/CoolerUtils.sol @@ -37,6 +37,9 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { /// @notice Thrown when the contract is not active. error OnlyActive(); + /// @notice Thrown when the caller is not the admin or owner. + error OnlyAdmin(); + /// @notice Thrown when the fee percentage is out of range. /// @dev Valid values are 0 <= feePercentage <= 100e2 error Params_FeePercentageOutOfRange(); @@ -70,6 +73,9 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { /// @notice Emitted when the collector is set event CollectorSet(address collector); + /// @notice Emitted when the admin is set + event AdminSet(address admin); + // --- DATA STRUCTURES --------------------------------------------------------- struct Batch { @@ -106,6 +112,9 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { /// @notice Whether the contract is active bool public active; + /// @notice Address permitted to activate and deactivate the contract + address public admin; + // --- INITIALIZATION ---------------------------------------------------------- constructor( @@ -282,6 +291,10 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { // --- ADMIN --------------------------------------------------- + /// @notice Set the fee percentage + /// @dev This function will revert if: + /// - The fee percentage is above `ONE_HUNDRED_PERCENT` + /// - The caller is not the owner function setFeePercentage(uint256 feePercentage_) external onlyOwner { if (feePercentage_ > ONE_HUNDRED_PERCENT) revert Params_FeePercentageOutOfRange(); @@ -289,6 +302,10 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { emit FeePercentageSet(feePercentage_); } + /// @notice Set the collector address + /// @dev This function will revert if: + /// - The address is the zero address + /// - The caller is not the owner function setCollector(address collector_) external onlyOwner { if (collector_ == address(0)) revert Params_InvalidAddress(); @@ -296,9 +313,12 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { emit CollectorSet(collector_); } - // TODO add admin addresses - - function activate() external onlyOwner { + /// @notice Activate the contract + /// @dev This function will revert if: + /// - The caller is not an admin + /// + /// If the contract is already active, it will do nothing. + function activate() external onlyAdmin { // Skip if already activated if (active) return; @@ -306,7 +326,12 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { emit Activated(); } - function deactivate() external onlyOwner { + /// @notice Deactivate the contract + /// @dev This function will revert if: + /// - The caller is not an admin + /// + /// If the contract is already deactivated, it will do nothing. + function deactivate() external onlyAdmin { // Skip if already deactivated if (!active) return; @@ -314,11 +339,32 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { emit Deactivated(); } + /// @notice Modifier to check that the contract is active modifier onlyActive() { if (!active) revert OnlyActive(); _; } + /// @notice Set the admin address + /// @dev This function will revert if: + /// - The address is the zero address + /// - The caller is not the owner + /// + /// This approach is used (instead of a Bophades role), as the contract is designed to be an independent contract and not a Bophades policy. + function setAdmin(address admin_) external onlyOwner { + if (admin_ == address(0)) revert Params_InvalidAddress(); + + admin = admin_; + emit AdminSet(admin_); + } + + /// @notice Modifier to check that the caller is the owner or admin + modifier onlyAdmin() { + // The caller has to be the owner or the emergency admin + if (msg.sender != owner && msg.sender != admin) revert OnlyAdmin(); + _; + } + // --- INTERNAL FUNCTIONS ------------------------------------------------------ function _getDebtForLoans( diff --git a/src/test/external/cooler/CoolerUtils.t.sol b/src/test/external/cooler/CoolerUtils.t.sol index 6161c2d65..0f9a7c1af 100644 --- a/src/test/external/cooler/CoolerUtils.t.sol +++ b/src/test/external/cooler/CoolerUtils.t.sol @@ -1208,6 +1208,8 @@ contract CoolerUtilsTest is Test { // [X] it reverts // when the collector address is the zero address // [X] it reverts + // when the kernel address is the zero address + // [ ] it reverts // when the fee percentage is > 100e2 // [X] it reverts // [X] it sets the values @@ -1353,9 +1355,41 @@ contract CoolerUtilsTest is Test { assertEq(utils.owner(), owner, "owner"); assertEq(address(utils.lender()), lender, "lender"); assertEq(utils.collector(), collector, "collector"); + assertEq(address(utils.kernel()), kernel, "kernel"); assertEq(utils.feePercentage(), feePercentage, "fee percentage"); } + // activate + // when the caller is not an admin or owner + // [ ] it reverts + // when the caller is the owner + // [ ] it sets the active flag to true + // when the caller is an admin + // [ ] it sets the active flag to true + // when the contract is already active + // [ ] it does nothing + + // deactivate + // when the caller is not an admin or owner + // [ ] it reverts + // when the caller is the owner + // [ ] it sets the active flag to false + // when the caller is an admin + // [ ] it sets the active flag to false + // when the contract is already deactive + // [ ] it does nothing + + // setAdmin + // when the caller is not the owner + // [ ] it reverts + // when the caller is the admin + // [ ] it reverts + // when the new admin is the zero address + // [ ] it reverts + // when the new admin is the owner address + // [ ] it sets the admin + // [ ] it sets the admin + // --- AUX FUNCTIONS ----------------------------------------------------------- function _idsA() internal pure returns (uint256[] memory) { From 6ff1b5973733e6bec1fa15a8e488e2a476ec625a Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 26 Sep 2024 17:16:48 +0400 Subject: [PATCH 006/160] setAdmin: allow for zero address (to disable), prevent duplicating the owner address. Additional tests. --- src/external/cooler/CoolerUtils.sol | 6 +- src/test/external/cooler/CoolerUtils.t.sol | 178 +++++++++++++++++++-- 2 files changed, 167 insertions(+), 17 deletions(-) diff --git a/src/external/cooler/CoolerUtils.sol b/src/external/cooler/CoolerUtils.sol index 2fbe81b63..e5f090db4 100644 --- a/src/external/cooler/CoolerUtils.sol +++ b/src/external/cooler/CoolerUtils.sol @@ -347,12 +347,14 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { /// @notice Set the admin address /// @dev This function will revert if: - /// - The address is the zero address + /// - The address is the owner address (since that would be a duplicate address) /// - The caller is not the owner /// /// This approach is used (instead of a Bophades role), as the contract is designed to be an independent contract and not a Bophades policy. + /// + /// This function will NOT revert if `admin_` is the zero address, so that the admin can be removed. function setAdmin(address admin_) external onlyOwner { - if (admin_ == address(0)) revert Params_InvalidAddress(); + if (admin_ == owner) revert Params_InvalidAddress(); admin = admin_; emit AdminSet(admin_); diff --git a/src/test/external/cooler/CoolerUtils.t.sol b/src/test/external/cooler/CoolerUtils.t.sol index 0f9a7c1af..3e8a9bb8e 100644 --- a/src/test/external/cooler/CoolerUtils.t.sol +++ b/src/test/external/cooler/CoolerUtils.t.sol @@ -30,6 +30,7 @@ contract CoolerUtilsTest is Test { address public owner; address public lender; address public collector; + address public admin; address public walletA; Cooler public coolerA; @@ -56,6 +57,7 @@ contract CoolerUtilsTest is Test { staking = 0xB63cac384247597756545b500253ff8E607a8020; owner = vm.addr(0x1); + admin = vm.addr(0x2); collector = vm.addr(0xC); // Deploy CoolerUtils @@ -175,6 +177,24 @@ contract CoolerUtilsTest is Test { return _getInterestDue(address(coolerA), ids_); } + modifier givenAdminIsSet() { + vm.prank(owner); + utils.setAdmin(admin); + _; + } + + modifier givenActivated() { + vm.prank(owner); + utils.activate(); + _; + } + + modifier givenDeactivated() { + vm.prank(owner); + utils.deactivate(); + _; + } + // ===== ASSERTIONS ===== // function _assertCoolerLoans(uint256 collateral_) internal { @@ -1209,7 +1229,7 @@ contract CoolerUtilsTest is Test { // when the collector address is the zero address // [X] it reverts // when the kernel address is the zero address - // [ ] it reverts + // [X] it reverts // when the fee percentage is > 100e2 // [X] it reverts // [X] it sets the values @@ -1316,6 +1336,23 @@ contract CoolerUtilsTest is Test { ); } + function test_constructor_zeroKernel_reverts() public { + // Expect revert + bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); + vm.expectRevert(err); + + new CoolerUtils( + address(gohm), + address(sdai), + address(dai), + owner, + lender, + collector, + address(0), + 0 + ); + } + function test_constructor_feePercentageAboveMax_reverts() public { // Expect revert bytes memory err = abi.encodeWithSelector( @@ -1361,34 +1398,145 @@ contract CoolerUtilsTest is Test { // activate // when the caller is not an admin or owner - // [ ] it reverts + // [X] it reverts // when the caller is the owner - // [ ] it sets the active flag to true + // [X] it sets the active flag to true // when the caller is an admin - // [ ] it sets the active flag to true + // when the admin is not set + // [X] it reverts + // [X] it sets the active flag to true // when the contract is already active - // [ ] it does nothing + // [X] it does nothing + + function test_activate_notAdminOrOwner_reverts() public givenAdminIsSet givenDeactivated { + // Expect revert + bytes memory err = abi.encodeWithSelector(CoolerUtils.OnlyAdmin.selector); + vm.expectRevert(err); + + utils.activate(); + } + + function test_activate_asOwner_setsActive() public givenAdminIsSet givenDeactivated { + vm.prank(owner); + utils.activate(); + + assertTrue(utils.active(), "active"); + } + + function test_activate_asAdmin_setsActive() public givenAdminIsSet givenDeactivated { + vm.prank(admin); + utils.activate(); + + assertTrue(utils.active(), "active"); + } + + function test_activate_asAdmin_adminNotSet_reverts() public givenDeactivated { + // Expect revert + bytes memory err = abi.encodeWithSelector(CoolerUtils.OnlyAdmin.selector); + vm.expectRevert(err); + + vm.prank(admin); + utils.activate(); + } + + function test_activate_asAdmin_alreadyActive() public givenAdminIsSet { + vm.prank(owner); + utils.activate(); + + assertTrue(utils.active(), "active"); + } // deactivate // when the caller is not an admin or owner - // [ ] it reverts + // [X] it reverts // when the caller is the owner - // [ ] it sets the active flag to false + // [X] it sets the active flag to false // when the caller is an admin - // [ ] it sets the active flag to false - // when the contract is already deactive - // [ ] it does nothing + // when the admin is not set + // [X] it reverts + // [X] it sets the active flag to false + // when the contract is already deactivated + // [X] it does nothing + + function test_deactivate_notAdminOrOwner_reverts() public givenAdminIsSet givenActivated { + // Expect revert + bytes memory err = abi.encodeWithSelector(CoolerUtils.OnlyAdmin.selector); + vm.expectRevert(err); + + utils.deactivate(); + } + + function test_deactivate_asOwner_setsActive() public givenAdminIsSet { + vm.prank(owner); + utils.deactivate(); + + assertFalse(utils.active(), "active"); + } + + function test_deactivate_asAdmin_adminNotSet_reverts() public { + // Expect revert + bytes memory err = abi.encodeWithSelector(CoolerUtils.OnlyAdmin.selector); + vm.expectRevert(err); + + vm.prank(admin); + utils.deactivate(); + } + + function test_deactivate_asAdmin_alreadyDeactivated() public givenAdminIsSet givenDeactivated { + vm.prank(owner); + utils.deactivate(); + + assertFalse(utils.active(), "active"); + } // setAdmin // when the caller is not the owner - // [ ] it reverts + // [X] it reverts // when the caller is the admin - // [ ] it reverts + // [X] it reverts // when the new admin is the zero address - // [ ] it reverts + // [X] it sets the admin // when the new admin is the owner address - // [ ] it sets the admin - // [ ] it sets the admin + // [X] it reverts + // [X] it sets the admin + + function test_setAdmin_notOwner_reverts() public { + // Expect revert + vm.expectRevert("UNAUTHORIZED"); + + utils.setAdmin(admin); + } + + function test_setAdmin_asAdmin_reverts() public { + // Expect revert + vm.expectRevert("UNAUTHORIZED"); + + vm.prank(admin); + utils.setAdmin(admin); + } + + function test_setAdmin_zeroAddress() public { + vm.prank(owner); + utils.setAdmin(address(0)); + + assertEq(utils.admin(), address(0), "admin"); + } + + function test_setAdmin_ownerAddress() public { + // Expect revert + bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); + vm.expectRevert(err); + + vm.prank(owner); + utils.setAdmin(owner); + } + + function test_setAdmin() public { + vm.prank(owner); + utils.setAdmin(admin); + + assertEq(utils.admin(), admin, "admin"); + } // --- AUX FUNCTIONS ----------------------------------------------------------- From d4c828231558d009f3a8051f95d599cebc36c453 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 26 Sep 2024 17:17:14 +0400 Subject: [PATCH 007/160] Minor test fix --- src/test/external/cooler/CoolerUtils.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/external/cooler/CoolerUtils.t.sol b/src/test/external/cooler/CoolerUtils.t.sol index 3e8a9bb8e..4bdab401a 100644 --- a/src/test/external/cooler/CoolerUtils.t.sol +++ b/src/test/external/cooler/CoolerUtils.t.sol @@ -1507,7 +1507,7 @@ contract CoolerUtilsTest is Test { utils.setAdmin(admin); } - function test_setAdmin_asAdmin_reverts() public { + function test_setAdmin_asAdmin_reverts() public givenAdminIsSet { // Expect revert vm.expectRevert("UNAUTHORIZED"); From 786cdbf005e121f2a743b775b9085a850c4f3a9b Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 26 Sep 2024 17:20:27 +0400 Subject: [PATCH 008/160] Update docs --- src/external/cooler/CoolerUtils.sol | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/external/cooler/CoolerUtils.sol b/src/external/cooler/CoolerUtils.sol index e5f090db4..fa5ab30de 100644 --- a/src/external/cooler/CoolerUtils.sol +++ b/src/external/cooler/CoolerUtils.sol @@ -164,9 +164,14 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { /// @notice Consolidate loans (taken with a single Cooler contract) into a single loan by using /// Maker flashloans. /// - /// @dev This function will revert unless the message sender has: - /// - Approved this contract to spend the `useFunds_`. - /// - Approved this contract to spend the gOHM escrowed by the target Cooler. + /// @dev This function will revert if: + /// - The caller has not approved this contract to spend the `useFunds_`. + /// - The caller has not approved this contract to spend the gOHM escrowed by the target Cooler. + /// - `clearinghouse_` is not registered with the Clearinghouse registry. + /// - `cooler_` is not a valid Cooler for the Clearinghouse. + /// - Less than two loans are being consolidated. + /// - The available funds are less than the required flashloan amount. + /// /// For flexibility purposes, the user can either pay with DAI or sDAI. /// /// @param clearinghouse_ Olympus Clearinghouse to be used to issue the consolidated loan. From 507341887969dfd9b79e33bfa6786d17c9709187 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 27 Sep 2024 10:18:30 +0400 Subject: [PATCH 009/160] Set fee collector to TRSRY v1 --- src/scripts/deploy/savedDeployments/cooler_utils.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/deploy/savedDeployments/cooler_utils.json b/src/scripts/deploy/savedDeployments/cooler_utils.json index d155c2644..2c32c9f73 100644 --- a/src/scripts/deploy/savedDeployments/cooler_utils.json +++ b/src/scripts/deploy/savedDeployments/cooler_utils.json @@ -3,7 +3,7 @@ { "name": "CoolerUtils", "args": { - "collector": "0x31F8Cc382c9898b273eff4e0b7626a6987C846E8", + "collector": "0xa8687A15D4BE32CC8F0a8a7B9704a4C3993D9613", "feePercentage": 0, "lender": "0x60744434d6339a6B27d73d9Eda62b6F66a0a04FA", "owner": "0x245cc372C84B3645Bf0Ffe6538620B04a217988B" From 5f3f6a4e005af5e2fc2a12e1e5ce9fe1f4e7b232 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 3 Oct 2024 10:36:39 +0400 Subject: [PATCH 010/160] Remove admin address, perform lookup of "emergency_shutdown" role. --- src/external/cooler/CoolerUtils.sol | 37 +++------ src/test/external/cooler/CoolerUtils.t.sol | 97 ++++++++-------------- 2 files changed, 47 insertions(+), 87 deletions(-) diff --git a/src/external/cooler/CoolerUtils.sol b/src/external/cooler/CoolerUtils.sol index fa5ab30de..55b6d389a 100644 --- a/src/external/cooler/CoolerUtils.sol +++ b/src/external/cooler/CoolerUtils.sol @@ -6,6 +6,7 @@ import {IERC4626} from "forge-std/interfaces/IERC4626.sol"; import {Owned} from "solmate/auth/Owned.sol"; import {Kernel, Module, toKeycode} from "src/Kernel.sol"; import {CHREGv1} from "src/modules/CHREG/CHREG.v1.sol"; +import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; import {IERC3156FlashBorrower} from "src/interfaces/maker-dao/IERC3156FlashBorrower.sol"; import {IERC3156FlashLender} from "src/interfaces/maker-dao/IERC3156FlashLender.sol"; @@ -37,7 +38,7 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { /// @notice Thrown when the contract is not active. error OnlyActive(); - /// @notice Thrown when the caller is not the admin or owner. + /// @notice Thrown when the caller is not the contract owner or and does not have the "emergency_shutdown" role. error OnlyAdmin(); /// @notice Thrown when the fee percentage is out of range. @@ -73,9 +74,6 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { /// @notice Emitted when the collector is set event CollectorSet(address collector); - /// @notice Emitted when the admin is set - event AdminSet(address admin); - // --- DATA STRUCTURES --------------------------------------------------------- struct Batch { @@ -112,9 +110,6 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { /// @notice Whether the contract is active bool public active; - /// @notice Address permitted to activate and deactivate the contract - address public admin; - // --- INITIALIZATION ---------------------------------------------------------- constructor( @@ -350,25 +345,17 @@ contract CoolerUtils is IERC3156FlashBorrower, Owned { _; } - /// @notice Set the admin address - /// @dev This function will revert if: - /// - The address is the owner address (since that would be a duplicate address) - /// - The caller is not the owner - /// - /// This approach is used (instead of a Bophades role), as the contract is designed to be an independent contract and not a Bophades policy. - /// - /// This function will NOT revert if `admin_` is the zero address, so that the admin can be removed. - function setAdmin(address admin_) external onlyOwner { - if (admin_ == owner) revert Params_InvalidAddress(); - - admin = admin_; - emit AdminSet(admin_); - } - - /// @notice Modifier to check that the caller is the owner or admin + /// @notice Modifier to check that the caller is the contract owner or has the "emergency_shutdown" role modifier onlyAdmin() { - // The caller has to be the owner or the emergency admin - if (msg.sender != owner && msg.sender != admin) revert OnlyAdmin(); + // As this is not a policy, it does not have a `configuredDependencies()` function + // that will be called by the kernel whenever modules or policies are upgraded. + // Instead, the ROLES module is determined through a kernel lookup. + // As the `kernel` state variable is immutable, the risk of this being tampered with is low. + ROLESv1 roles = ROLESv1(address(kernel.getModuleForKeycode(toKeycode("ROLES")))); + + // Revert if not owner and does not have the "emergency_shutdown" role + if (msg.sender != owner && !roles.hasRole(msg.sender, "emergency_shutdown")) + revert OnlyAdmin(); _; } diff --git a/src/test/external/cooler/CoolerUtils.t.sol b/src/test/external/cooler/CoolerUtils.t.sol index 4bdab401a..62690b7ce 100644 --- a/src/test/external/cooler/CoolerUtils.t.sol +++ b/src/test/external/cooler/CoolerUtils.t.sol @@ -12,6 +12,9 @@ import {CoolerFactory} from "src/external/cooler/CoolerFactory.sol"; import {Clearinghouse} from "src/policies/Clearinghouse.sol"; import {Cooler} from "src/external/cooler/Cooler.sol"; +import {RolesAdmin} from "src/policies/RolesAdmin.sol"; +import {Kernel} from "src/Kernel.sol"; + import {CoolerUtils} from "src/external/cooler/CoolerUtils.sol"; contract CoolerUtilsTest is Test { @@ -25,12 +28,15 @@ contract CoolerUtilsTest is Test { CoolerFactory public coolerFactory; Clearinghouse public clearinghouse; + RolesAdmin public rolesAdmin; + address public staking; address public kernel; address public owner; address public lender; address public collector; address public admin; + address public kernelExecutor; address public walletA; Cooler public coolerA; @@ -55,6 +61,10 @@ contract CoolerUtilsTest is Test { lender = 0x60744434d6339a6B27d73d9Eda62b6F66a0a04FA; kernel = 0x2286d7f9639e8158FaD1169e76d1FbC38247f54b; staking = 0xB63cac384247597756545b500253ff8E607a8020; + rolesAdmin = RolesAdmin(0xb216d714d91eeC4F7120a732c11428857C659eC8); + + // Determine the kernel executor + kernelExecutor = Kernel(kernel).executor(); owner = vm.addr(0x1); admin = vm.addr(0x2); @@ -177,12 +187,6 @@ contract CoolerUtilsTest is Test { return _getInterestDue(address(coolerA), ids_); } - modifier givenAdminIsSet() { - vm.prank(owner); - utils.setAdmin(admin); - _; - } - modifier givenActivated() { vm.prank(owner); utils.activate(); @@ -195,6 +199,12 @@ contract CoolerUtilsTest is Test { _; } + modifier givenAdminHasEmergencyRole() { + vm.prank(kernelExecutor); + rolesAdmin.grantRole("emergency_shutdown", admin); + _; + } + // ===== ASSERTIONS ===== // function _assertCoolerLoans(uint256 collateral_) internal { @@ -1408,7 +1418,11 @@ contract CoolerUtilsTest is Test { // when the contract is already active // [X] it does nothing - function test_activate_notAdminOrOwner_reverts() public givenAdminIsSet givenDeactivated { + function test_activate_notAdminOrOwner_reverts() + public + givenAdminHasEmergencyRole + givenDeactivated + { // Expect revert bytes memory err = abi.encodeWithSelector(CoolerUtils.OnlyAdmin.selector); vm.expectRevert(err); @@ -1416,14 +1430,14 @@ contract CoolerUtilsTest is Test { utils.activate(); } - function test_activate_asOwner_setsActive() public givenAdminIsSet givenDeactivated { + function test_activate_asOwner_setsActive() public givenAdminHasEmergencyRole givenDeactivated { vm.prank(owner); utils.activate(); assertTrue(utils.active(), "active"); } - function test_activate_asAdmin_setsActive() public givenAdminIsSet givenDeactivated { + function test_activate_asAdmin_setsActive() public givenAdminHasEmergencyRole givenDeactivated { vm.prank(admin); utils.activate(); @@ -1439,7 +1453,7 @@ contract CoolerUtilsTest is Test { utils.activate(); } - function test_activate_asAdmin_alreadyActive() public givenAdminIsSet { + function test_activate_asAdmin_alreadyActive() public givenAdminHasEmergencyRole { vm.prank(owner); utils.activate(); @@ -1458,7 +1472,11 @@ contract CoolerUtilsTest is Test { // when the contract is already deactivated // [X] it does nothing - function test_deactivate_notAdminOrOwner_reverts() public givenAdminIsSet givenActivated { + function test_deactivate_notAdminOrOwner_reverts() + public + givenAdminHasEmergencyRole + givenActivated + { // Expect revert bytes memory err = abi.encodeWithSelector(CoolerUtils.OnlyAdmin.selector); vm.expectRevert(err); @@ -1466,7 +1484,7 @@ contract CoolerUtilsTest is Test { utils.deactivate(); } - function test_deactivate_asOwner_setsActive() public givenAdminIsSet { + function test_deactivate_asOwner_setsActive() public givenAdminHasEmergencyRole { vm.prank(owner); utils.deactivate(); @@ -1482,62 +1500,17 @@ contract CoolerUtilsTest is Test { utils.deactivate(); } - function test_deactivate_asAdmin_alreadyDeactivated() public givenAdminIsSet givenDeactivated { + function test_deactivate_asAdmin_alreadyDeactivated() + public + givenAdminHasEmergencyRole + givenDeactivated + { vm.prank(owner); utils.deactivate(); assertFalse(utils.active(), "active"); } - // setAdmin - // when the caller is not the owner - // [X] it reverts - // when the caller is the admin - // [X] it reverts - // when the new admin is the zero address - // [X] it sets the admin - // when the new admin is the owner address - // [X] it reverts - // [X] it sets the admin - - function test_setAdmin_notOwner_reverts() public { - // Expect revert - vm.expectRevert("UNAUTHORIZED"); - - utils.setAdmin(admin); - } - - function test_setAdmin_asAdmin_reverts() public givenAdminIsSet { - // Expect revert - vm.expectRevert("UNAUTHORIZED"); - - vm.prank(admin); - utils.setAdmin(admin); - } - - function test_setAdmin_zeroAddress() public { - vm.prank(owner); - utils.setAdmin(address(0)); - - assertEq(utils.admin(), address(0), "admin"); - } - - function test_setAdmin_ownerAddress() public { - // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); - vm.expectRevert(err); - - vm.prank(owner); - utils.setAdmin(owner); - } - - function test_setAdmin() public { - vm.prank(owner); - utils.setAdmin(admin); - - assertEq(utils.admin(), admin, "admin"); - } - // --- AUX FUNCTIONS ----------------------------------------------------------- function _idsA() internal pure returns (uint256[] memory) { From 8678136cbc7f336040ffb0e4472cb0feb4816075 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 8 Oct 2024 12:16:33 +0400 Subject: [PATCH 011/160] Add initial version of EXREG module --- src/modules/EXREG/EXREG.v1.sol | 57 +++++++++ src/modules/EXREG/OlympusExternalRegistry.sol | 109 ++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 src/modules/EXREG/EXREG.v1.sol create mode 100644 src/modules/EXREG/OlympusExternalRegistry.sol diff --git a/src/modules/EXREG/EXREG.v1.sol b/src/modules/EXREG/EXREG.v1.sol new file mode 100644 index 000000000..1b2a07297 --- /dev/null +++ b/src/modules/EXREG/EXREG.v1.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.15; + +import {Module} from "src/Kernel.sol"; + +/// @title External Registry +/// @notice Interface for a module that can track the addresses of external contracts +abstract contract EXREGv1 is Module { + // ========= EVENTS ========= // + + /// @notice Emitted when a contract is registered or updated + event ContractRegistered(bytes5 indexed name, address indexed contractAddress); + + /// @notice Emitted when a contract is deregistered + event ContractDeregistered(bytes5 indexed name); + + // ========= ERRORS ========= // + + /// @notice Thrown when an invalid name is provided + error Params_InvalidName(); + + /// @notice Thrown when an invalid address is provided + error Params_InvalidAddress(); + + // ========= STATE ========= // + + /// @notice Stores the names of the registered contracts + bytes5[] internal _contractNames; + + /// @notice Mapping to store the address of a contract + /// @dev The address of a registered contract can be retrieved by `getContract()`, and the names of all registered contracts can be retrieved by `getContractNames()`. + mapping(bytes5 => address) internal _contracts; + + // ========= FUNCTIONS ========= // + + /// @notice Function to register or update a contract + /// + /// @param name_ The name of the contract + /// @param contractAddress_ The address of the contract + function registerContract(bytes5 name_, address contractAddress_) external virtual; + + /// @notice Function to deregister a contract + /// + /// @param name_ The name of the contract + function deregisterContract(bytes5 name_) external virtual; + + /// @notice Function to get the address of a contract + /// + /// @param name_ The name of the contract + /// @return The address of the contract + function getContract(bytes5 name_) external view virtual returns (address); + + /// @notice Function to get the names of all registered contracts + /// + /// @return The names of all registered contracts + function getContractNames() external view virtual returns (bytes5[] memory); +} diff --git a/src/modules/EXREG/OlympusExternalRegistry.sol b/src/modules/EXREG/OlympusExternalRegistry.sol new file mode 100644 index 000000000..4b4453cae --- /dev/null +++ b/src/modules/EXREG/OlympusExternalRegistry.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.15; + +import {Kernel, Module, Keycode, toKeycode} from "src/Kernel.sol"; +import {EXREGv1} from "./EXREG.v1.sol"; + +contract OlympusExternalRegistry is EXREGv1 { + constructor(address kernel_) Module(Kernel(kernel_)) {} + + // ========= MODULE SETUP ========= // + + /// @inheritdoc Module + function KEYCODE() public pure override returns (Keycode) { + return toKeycode("EXREG"); + } + + /// @inheritdoc Module + function VERSION() public pure override returns (uint8 major, uint8 minor) { + major = 1; + minor = 0; + } + + // ========= CONTRACT REGISTRATION ========= // + + /// @inheritdoc EXREGv1 + /// @dev If the contract is already registered, the address will be updated. + /// + /// This function will revert if: + /// - The caller is not permissioned + /// - The contract address is zero + function registerContract( + bytes5 name_, + address contractAddress_ + ) external override permissioned { + if (contractAddress_ == address(0)) revert Params_InvalidAddress(); + + _contracts[name_] = contractAddress_; + _updateContractNames(name_); + + emit ContractRegistered(name_, contractAddress_); + } + + /// @inheritdoc EXREGv1 + /// @dev This function will revert if: + /// - The caller is not permissioned + /// - The contract is not registered + function deregisterContract(bytes5 name_) external override permissioned { + address contractAddress = _contracts[name_]; + if (contractAddress == address(0)) revert Params_InvalidName(); + + delete _contracts[name_]; + _removeContractName(name_); + + emit ContractDeregistered(name_); + } + + // ========= VIEW FUNCTIONS ========= // + + /// @inheritdoc EXREGv1 + /// @dev This function will revert if: + /// - The contract is not registered + function getContract(bytes5 name_) external view override returns (address) { + address contractAddress = _contracts[name_]; + + if (contractAddress == address(0)) revert Params_InvalidName(); + + return contractAddress; + } + + /// @inheritdoc EXREGv1 + function getContractNames() external view override returns (bytes5[] memory) { + return _contractNames; + } + + // ========= INTERNAL FUNCTIONS ========= // + + /// @notice Updates the list of contract names if the name is not already present. + /// + /// @param name_ The name of the contract + function _updateContractNames(bytes5 name_) internal { + bytes5[] memory contractNames = _contractNames; + for (uint256 i; i < contractNames.length; ) { + if (contractNames[i] == name_) return; + unchecked { + ++i; + } + } + _contractNames.push(name_); + } + + /// @notice Removes the name of a contract from the list of contract names. + /// + /// @param name_ The name of the contract + function _removeContractName(bytes5 name_) internal { + uint256 length = _contractNames.length; + for (uint256 i; i < length; ) { + if (_contractNames[i] == name_) { + // Swap the found element with the last element + _contractNames[i] = _contractNames[length - 1]; + // Remove the last element + _contractNames.pop(); + return; + } + unchecked { + ++i; + } + } + } +} From 29a6416654aef9b8617ea625e72b2d6d48486590 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 8 Oct 2024 12:38:19 +0400 Subject: [PATCH 012/160] NatSpec --- src/modules/EXREG/EXREG.v1.sol | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/modules/EXREG/EXREG.v1.sol b/src/modules/EXREG/EXREG.v1.sol index 1b2a07297..b260bb370 100644 --- a/src/modules/EXREG/EXREG.v1.sol +++ b/src/modules/EXREG/EXREG.v1.sol @@ -31,19 +31,23 @@ abstract contract EXREGv1 is Module { /// @dev The address of a registered contract can be retrieved by `getContract()`, and the names of all registered contracts can be retrieved by `getContractNames()`. mapping(bytes5 => address) internal _contracts; - // ========= FUNCTIONS ========= // + // ========= REGISTRATION FUNCTIONS ========= // /// @notice Function to register or update a contract + /// @dev This function should be permissioned to prevent arbitrary contracts from being registered. /// /// @param name_ The name of the contract /// @param contractAddress_ The address of the contract function registerContract(bytes5 name_, address contractAddress_) external virtual; /// @notice Function to deregister a contract + /// @dev This function should be permissioned to prevent arbitrary contracts from being deregistered. /// /// @param name_ The name of the contract function deregisterContract(bytes5 name_) external virtual; + // ========= VIEW FUNCTIONS ========= // + /// @notice Function to get the address of a contract /// /// @param name_ The name of the contract From a061cd64aa2f0a6cf28cef2e26bf30dd44d0fd33 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 8 Oct 2024 12:38:26 +0400 Subject: [PATCH 013/160] Test stubs --- src/test/modules/EXREG.t.sol | 79 ++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/test/modules/EXREG.t.sol diff --git a/src/test/modules/EXREG.t.sol b/src/test/modules/EXREG.t.sol new file mode 100644 index 000000000..5afb9041c --- /dev/null +++ b/src/test/modules/EXREG.t.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: Unlicensed +pragma solidity 0.8.15; + +import {Test} from "forge-std/Test.sol"; +import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; + +import {Kernel, Actions} from "src/Kernel.sol"; +import {EXREGv1} from "src/modules/EXREG/EXREG.v1.sol"; +import {OlympusExternalRegistry} from "src/modules/EXREG/OlympusExternalRegistry.sol"; + +contract ExternalRegistryTest is Test { + using ModuleTestFixtureGenerator for OlympusExternalRegistry; + + address public godmode; + + Kernel internal _kernel; + OlympusExternalRegistry internal _exreg; + + // External Registry Expected events + event ContractRegistered(bytes5 indexed name, address indexed contractAddress); + event ContractDeregistered(bytes5 indexed name); + + function setUp() public { + // Deploy Kernel and modules + _kernel = new Kernel(); + _exreg = new OlympusExternalRegistry(address(_kernel)); + + // Generate fixtures + godmode = _exreg.generateGodmodeFixture(type(OlympusExternalRegistry).name); + + // Install modules and policies on Kernel + _kernel.executeAction(Actions.InstallModule, address(_exreg)); + _kernel.executeAction(Actions.ActivatePolicy, godmode); + } + + // ========= TESTS ========= // + + // constructor + // when the kernel address is zero + // [ ] it reverts + // when the kernel address is not zero + // [ ] it sets the kernel address + + // registerContract + // when the caller is not permissioned + // [ ] it reverts + // when the name is empty + // [ ] it reverts + // when the contract address is zero + // [ ] it reverts + // given the name is registered + // [ ] it updates the contract address and emits an event, but does not update the names array + // given the name is not registered + // [ ] it registers the contract address, emits an event and updates the names array + + // deregisterContract + // when the caller is not permissioned + // [ ] it reverts + // given the name is not registered + // [ ] it reverts + // given the name is registered + // given multiple names are registered + // [ ] it deregisters the name, emits an event and updates the names array + // [ ] it deregisters the name, emits an event and updates the names array + + // getContract + // given the name is not registered + // [ ] it reverts + // given the name is registered + // given the name has been updated + // [ ] it returns the latest address + // [ ] it returns the contract address + + // getContractNames + // given no names are registered + // [ ] it returns an empty array + // given names are registered + // [ ] it returns the names array +} From 593e005fd1c2b3611fb813448e44ce2dba274ea5 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 8 Oct 2024 13:18:00 +0400 Subject: [PATCH 014/160] Implementation fixes. Complete tests. --- src/modules/EXREG/OlympusExternalRegistry.sol | 16 +- src/test/modules/EXREG.t.sol | 359 +++++++++++++++++- 2 files changed, 357 insertions(+), 18 deletions(-) diff --git a/src/modules/EXREG/OlympusExternalRegistry.sol b/src/modules/EXREG/OlympusExternalRegistry.sol index 4b4453cae..499790bd2 100644 --- a/src/modules/EXREG/OlympusExternalRegistry.sol +++ b/src/modules/EXREG/OlympusExternalRegistry.sol @@ -4,8 +4,19 @@ pragma solidity 0.8.15; import {Kernel, Module, Keycode, toKeycode} from "src/Kernel.sol"; import {EXREGv1} from "./EXREG.v1.sol"; +/// @title Olympus External Registry +/// @notice This module is used to track the address of contracts that are external to the Bophades system. contract OlympusExternalRegistry is EXREGv1 { - constructor(address kernel_) Module(Kernel(kernel_)) {} + // ========= CONSTRUCTOR ========= // + + /// @notice Constructor for the Olympus External Registry + /// @dev This function will revert if: + /// - The provided kernel address is zero + /// + /// @param kernel_ The address of the kernel + constructor(address kernel_) Module(Kernel(kernel_)) { + if (kernel_ == address(0)) revert Params_InvalidAddress(); + } // ========= MODULE SETUP ========= // @@ -27,11 +38,13 @@ contract OlympusExternalRegistry is EXREGv1 { /// /// This function will revert if: /// - The caller is not permissioned + /// - The name is empty /// - The contract address is zero function registerContract( bytes5 name_, address contractAddress_ ) external override permissioned { + if (name_ == bytes5(0)) revert Params_InvalidName(); if (contractAddress_ == address(0)) revert Params_InvalidAddress(); _contracts[name_] = contractAddress_; @@ -68,6 +81,7 @@ contract OlympusExternalRegistry is EXREGv1 { } /// @inheritdoc EXREGv1 + /// @dev Note that the order of the names in the array is not guaranteed to be consistent. function getContractNames() external view override returns (bytes5[] memory) { return _contractNames; } diff --git a/src/test/modules/EXREG.t.sol b/src/test/modules/EXREG.t.sol index 5afb9041c..1227a79f4 100644 --- a/src/test/modules/EXREG.t.sol +++ b/src/test/modules/EXREG.t.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; -import {Kernel, Actions} from "src/Kernel.sol"; +import {Kernel, Actions, Module, fromKeycode} from "src/Kernel.sol"; import {EXREGv1} from "src/modules/EXREG/EXREG.v1.sol"; import {OlympusExternalRegistry} from "src/modules/EXREG/OlympusExternalRegistry.sol"; @@ -12,6 +12,10 @@ contract ExternalRegistryTest is Test { using ModuleTestFixtureGenerator for OlympusExternalRegistry; address public godmode; + address public notOwner = address(0x1); + + address public addressOne = address(0x2); + address public addressTwo = address(0x3); Kernel internal _kernel; OlympusExternalRegistry internal _exreg; @@ -22,6 +26,7 @@ contract ExternalRegistryTest is Test { function setUp() public { // Deploy Kernel and modules + // This contract is the owner _kernel = new Kernel(); _exreg = new OlympusExternalRegistry(address(_kernel)); @@ -33,47 +38,367 @@ contract ExternalRegistryTest is Test { _kernel.executeAction(Actions.ActivatePolicy, godmode); } + function _registerContract(bytes5 name_, address contractAddress_) internal { + vm.prank(godmode); + _exreg.registerContract(name_, contractAddress_); + } + + function _deregisterContract(bytes5 name_) internal { + vm.prank(godmode); + _exreg.deregisterContract(name_); + } + // ========= TESTS ========= // // constructor // when the kernel address is zero - // [ ] it reverts + // [X] it reverts // when the kernel address is not zero - // [ ] it sets the kernel address + // [X] it sets the kernel address + + function test_constructor_whenKernelAddressIsZero() public { + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidAddress.selector)); + + new OlympusExternalRegistry(address(0)); + } + + function test_constructor_whenKernelAddressIsNotZero() public { + OlympusExternalRegistry exreg = new OlympusExternalRegistry(address(1)); + + assertEq(address(exreg.kernel()), address(1), "Kernel address is not set correctly"); + } // registerContract // when the caller is not permissioned - // [ ] it reverts + // [X] it reverts // when the name is empty - // [ ] it reverts + // [X] it reverts // when the contract address is zero - // [ ] it reverts + // [X] it reverts // given the name is registered - // [ ] it updates the contract address and emits an event, but does not update the names array + // [X] it updates the contract address and emits an event, but does not update the names array // given the name is not registered - // [ ] it registers the contract address, emits an event and updates the names array + // given there are existing registrations + // [X] it updates the contract address, emits an event and updates the names array + // [X] it registers the contract address, emits an event and updates the names array + + function test_registerContract_whenCallerIsNotPermissioned() public { + vm.expectRevert( + abi.encodeWithSelector(Module.Module_PolicyNotPermitted.selector, notOwner) + ); + + vm.prank(notOwner); + _exreg.registerContract(bytes5("ohm"), addressOne); + } + + function test_registerContract_whenNameIsEmpty() public { + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + + _registerContract(bytes5(""), addressOne); + } + + function test_registerContract_whenNameIsZero() public { + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + + _registerContract(bytes5(0), addressOne); + } + + function test_registerContract_whenContractAddressIsZero() public { + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidAddress.selector)); + + _registerContract(bytes5("ohm"), address(0)); + } + + function test_registerContract_whenNameIsRegistered() public { + // Register the first time + _registerContract(bytes5("ohm"), addressOne); + + // Expect an event to be emitted for updated registration + vm.expectEmit(); + emit ContractRegistered(bytes5("ohm"), addressTwo); + + // Register the second time + _registerContract(bytes5("ohm"), addressTwo); + + // Assert values + assertEq( + _exreg.getContract(bytes5("ohm")), + addressTwo, + "Contract address is not set correctly" + ); + assertEq(_exreg.getContractNames().length, 1, "Names array is not updated correctly"); + assertEq( + _exreg.getContractNames()[0], + bytes5("ohm"), + "Names array is not updated correctly" + ); + } + + function test_registerContract_whenNameIsNotRegistered() public { + // Expect an event to be emitted for updated registration + vm.expectEmit(); + emit ContractRegistered(bytes5("ohm"), addressOne); + + // Register the first time + _registerContract(bytes5("ohm"), addressOne); + + assertEq( + _exreg.getContract(bytes5("ohm")), + addressOne, + "Contract address is not set correctly" + ); + assertEq(_exreg.getContractNames().length, 1, "Names array is not updated correctly"); + assertEq( + _exreg.getContractNames()[0], + bytes5("ohm"), + "Names array is not updated correctly" + ); + } + + function test_registerContract_whenOtherNamesAreRegistered() public { + // Register the first time + _registerContract(bytes5("ohm"), addressOne); + + // Register the second time + _registerContract(bytes5("ohm2"), addressTwo); + + // Register the third time + _registerContract(bytes5("ohm3"), address(0x4)); + + // Assert values + assertEq( + _exreg.getContract(bytes5("ohm")), + addressOne, + "ohm contract address is not set correctly" + ); + assertEq( + _exreg.getContract(bytes5("ohm2")), + addressTwo, + "ohm2 contract address is not set correctly" + ); + assertEq( + _exreg.getContract(bytes5("ohm3")), + address(0x4), + "ohm3 contract address is not set correctly" + ); + assertEq(_exreg.getContractNames().length, 3, "Names array is not updated correctly"); + assertEq( + _exreg.getContractNames()[0], + bytes5("ohm"), + "Names array is not updated correctly" + ); + assertEq( + _exreg.getContractNames()[1], + bytes5("ohm2"), + "Names array is not updated correctly" + ); + assertEq( + _exreg.getContractNames()[2], + bytes5("ohm3"), + "Names array is not updated correctly" + ); + } // deregisterContract // when the caller is not permissioned - // [ ] it reverts + // [X] it reverts // given the name is not registered - // [ ] it reverts + // [X] it reverts // given the name is registered // given multiple names are registered - // [ ] it deregisters the name, emits an event and updates the names array - // [ ] it deregisters the name, emits an event and updates the names array + // [X] it deregisters the name, emits an event and updates the names array + // [X] it deregisters the name, emits an event and updates the names array + + function test_deregisterContract_whenCallerIsNotPermissioned() public { + // Register the first time + _registerContract(bytes5("ohm"), addressOne); + + vm.expectRevert( + abi.encodeWithSelector(Module.Module_PolicyNotPermitted.selector, notOwner) + ); + + vm.prank(notOwner); + _exreg.deregisterContract(bytes5("ohm")); + } + + function test_deregisterContract_whenNameIsNotRegistered() public { + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + + _deregisterContract(bytes5("")); + } + + function test_deregisterContract_whenNameIsRegistered() public { + // Register the first time + _registerContract(bytes5("ohm"), addressOne); + + // Deregister the first time + _deregisterContract(bytes5("ohm")); + + // Assert values + // Deregistered contract should revert + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + _exreg.getContract(bytes5("ohm")); + + // Names array should be empty + assertEq(_exreg.getContractNames().length, 0, "Names array is not updated correctly"); + } + + function test_deregisterContract_whenMultipleNamesAreRegistered(uint256 index_) public { + uint256 randomIndex = bound(index_, 0, 2); + + bytes5[] memory names = new bytes5[](3); + names[0] = bytes5("ohm"); + names[1] = bytes5("ohm2"); + names[2] = bytes5("ohm3"); + + // Register the first time + _registerContract(names[0], addressOne); + + // Register the second time + _registerContract(names[1], addressTwo); + + // Register the third time + _registerContract(names[2], address(0x4)); + + // Deregister a random name + _deregisterContract(names[randomIndex]); + + // Assert values + // Deregistered contract should revert + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + _exreg.getContract(names[randomIndex]); + + // Other contracts should still be registered + if (randomIndex != 0) { + assertEq( + _exreg.getContract(names[0]), + addressOne, + "ohm contract address is not set correctly" + ); + } + if (randomIndex != 1) { + assertEq( + _exreg.getContract(names[1]), + addressTwo, + "ohm2 contract address is not set correctly" + ); + } + if (randomIndex != 2) { + assertEq( + _exreg.getContract(names[2]), + address(0x4), + "ohm3 contract address is not set correctly" + ); + } + + // Names array should be updated + bytes5[] memory expectedNames = new bytes5[](2); + uint256 expectedIndex = 0; + for (uint256 i = 0; i < 3; i++) { + if (i != randomIndex) { + expectedNames[expectedIndex] = names[i]; + expectedIndex++; + } + } + + bytes5[] memory contractNames = _exreg.getContractNames(); + assertEq(_exreg.getContractNames().length, 2, "Names array is not updated correctly"); + + // Check that the expected names are in the array + // This is done as the order of names in the array is not guaranteed + for (uint256 i = 0; i < expectedNames.length; i++) { + bool found = false; + for (uint256 j = 0; j < contractNames.length; j++) { + if (expectedNames[i] == contractNames[j]) { + found = true; + break; + } + } + assertEq(found, true, "Names array is not updated correctly"); + } + } // getContract // given the name is not registered - // [ ] it reverts + // [X] it reverts // given the name is registered // given the name has been updated - // [ ] it returns the latest address - // [ ] it returns the contract address + // [X] it returns the latest address + // [X] it returns the contract address + + function test_getContract_whenNameIsNotRegistered() public { + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + + _exreg.getContract(bytes5("ohm")); + } + + function test_getContract_whenNameIsRegistered() public { + _registerContract(bytes5("ohm"), addressOne); + + assertEq( + _exreg.getContract(bytes5("ohm")), + addressOne, + "Contract address is not set correctly" + ); + } + + function test_getContract_whenNameIsUpdated() public { + _registerContract(bytes5("ohm"), addressOne); + _registerContract(bytes5("ohm"), addressTwo); + + assertEq( + _exreg.getContract(bytes5("ohm")), + addressTwo, + "Contract address is not updated correctly" + ); + } // getContractNames // given no names are registered - // [ ] it returns an empty array + // [X] it returns an empty array // given names are registered - // [ ] it returns the names array + // [X] it returns the names array + + function test_getContractNames_whenNoNamesAreRegistered() public { + assertEq(_exreg.getContractNames().length, 0, "Names array is not empty"); + } + + function test_getContractNames_whenNamesAreRegistered() public { + _registerContract(bytes5("ohm"), addressOne); + _registerContract(bytes5("ohm2"), addressTwo); + _registerContract(bytes5("ohm3"), address(0x4)); + + assertEq(_exreg.getContractNames().length, 3, "Names array is not updated correctly"); + assertEq( + _exreg.getContractNames()[0], + bytes5("ohm"), + "Names array at index 0 is not updated correctly" + ); + assertEq( + _exreg.getContractNames()[1], + bytes5("ohm2"), + "Names array at index 1 is not updated correctly" + ); + assertEq( + _exreg.getContractNames()[2], + bytes5("ohm3"), + "Names array at index 2 is not updated correctly" + ); + } + + // KEYCODE + // [X] it returns the correct keycode + + function test_KEYCODE() public { + assertEq(fromKeycode(_exreg.KEYCODE()), bytes5("EXREG")); + } + + // VERSION + // [X] it returns the correct version + + function test_VERSION() public { + (uint8 major, uint8 minor) = _exreg.VERSION(); + assertEq(major, 1); + assertEq(minor, 0); + } } From 089c233c9d515f6d7e2adf8328810717d67f2f7b Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 8 Oct 2024 14:19:44 +0400 Subject: [PATCH 015/160] Rename CoolerUtils -> LoanConsolidator. Move into policies directory. --- .../LoanConsolidator.sol} | 2 +- src/scripts/deploy/DeployV2.sol | 16 ++-- ...olerUtils.s.sol => LoanConsolidator.s.sol} | 10 +-- .../LoanConsolidator.t.sol} | 88 ++++++++++--------- 4 files changed, 60 insertions(+), 56 deletions(-) rename src/{external/cooler/CoolerUtils.sol => policies/LoanConsolidator.sol} (99%) rename src/scripts/ops/{CoolerUtils.s.sol => LoanConsolidator.s.sol} (90%) rename src/test/{external/cooler/CoolerUtils.t.sol => policies/LoanConsolidator.t.sol} (94%) diff --git a/src/external/cooler/CoolerUtils.sol b/src/policies/LoanConsolidator.sol similarity index 99% rename from src/external/cooler/CoolerUtils.sol rename to src/policies/LoanConsolidator.sol index 55b6d389a..22ecd748c 100644 --- a/src/external/cooler/CoolerUtils.sol +++ b/src/policies/LoanConsolidator.sol @@ -23,7 +23,7 @@ import {CoolerFactory} from "src/external/cooler/CoolerFactory.sol"; // ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚══════╝ // -contract CoolerUtils is IERC3156FlashBorrower, Owned { +contract LoanConsolidator is IERC3156FlashBorrower, Owned { // --- ERRORS ------------------------------------------------------------------ /// @notice Thrown when the caller is not the contract itself. diff --git a/src/scripts/deploy/DeployV2.sol b/src/scripts/deploy/DeployV2.sol index c340ceae8..2f183b985 100644 --- a/src/scripts/deploy/DeployV2.sol +++ b/src/scripts/deploy/DeployV2.sol @@ -67,7 +67,7 @@ import {MockAuraBooster, MockAuraRewardPool, MockAuraMiningLib, MockAuraVirtualR import {MockBalancerPool, MockVault} from "test/mocks/BalancerMocks.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {Faucet} from "test/mocks/Faucet.sol"; -import {CoolerUtils} from "src/external/cooler/CoolerUtils.sol"; +import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; import {TransferHelper} from "libraries/TransferHelper.sol"; @@ -114,7 +114,7 @@ contract OlympusDeploy is Script { address public inverseBondDepository; pOLY public poly; Clearinghouse public clearinghouse; - CoolerUtils public coolerUtils; + LoanConsolidator public loanConsolidator; YieldRepurchaseFacility public yieldRepo; // Governance @@ -212,7 +212,7 @@ contract OlympusDeploy is Script { selectorMap["pOLY"] = this._deployPoly.selector; selectorMap["ClaimTransfer"] = this._deployClaimTransfer.selector; selectorMap["Clearinghouse"] = this._deployClearinghouse.selector; - selectorMap["CoolerUtils"] = this._deployCoolerUtils.selector; + selectorMap["LoanConsolidator"] = this._deployLoanConsolidator.selector; selectorMap["YieldRepurchaseFacility"] = this._deployYieldRepurchaseFacility.selector; // Governance @@ -1027,7 +1027,7 @@ contract OlympusDeploy is Script { return address(CHREG); } - function _deployCoolerUtils(bytes calldata args_) public returns (address) { + function _deployLoanConsolidator(bytes calldata args_) public returns (address) { // Decode arguments from the sequence file (address collector, uint256 feePercentage, address lender, address owner) = abi.decode( args_, @@ -1044,9 +1044,9 @@ contract OlympusDeploy is Script { console2.log(" Kernel:", address(kernel)); console2.log(" Owner:", owner); - // Deploy CoolerUtils + // Deploy LoanConsolidator vm.broadcast(); - coolerUtils = new CoolerUtils( + loanConsolidator = new LoanConsolidator( address(gohm), address(wrappedReserve), address(reserve), @@ -1056,9 +1056,9 @@ contract OlympusDeploy is Script { address(kernel), feePercentage ); - console2.log(" CoolerUtils deployed at:", address(coolerUtils)); + console2.log(" LoanConsolidator deployed at:", address(loanConsolidator)); - return address(coolerUtils); + return address(loanConsolidator); } // ========== GOVERNANCE ========== // diff --git a/src/scripts/ops/CoolerUtils.s.sol b/src/scripts/ops/LoanConsolidator.s.sol similarity index 90% rename from src/scripts/ops/CoolerUtils.s.sol rename to src/scripts/ops/LoanConsolidator.s.sol index 82735a50d..6bee79998 100644 --- a/src/scripts/ops/CoolerUtils.s.sol +++ b/src/scripts/ops/LoanConsolidator.s.sol @@ -6,11 +6,11 @@ import {Script} from "forge-std/Script.sol"; import {stdJson} from "forge-std/StdJson.sol"; import {console2} from "forge-std/console2.sol"; -import {CoolerUtils} from "../../external/cooler/CoolerUtils.sol"; +import {LoanConsolidator} from "../../policies/LoanConsolidator.sol"; import {Cooler} from "../../external/cooler/Cooler.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; -contract CoolerUtilsScript is Test { +contract LoanConsolidatorScript is Test { using stdJson for string; string internal _env; @@ -24,7 +24,7 @@ contract CoolerUtilsScript is Test { } /// @dev Call in the form: - /// forge script ./src/scripts/ops/CoolerUtils.s.sol:CoolerUtilsScript --chain mainnet --sig "consolidate(address,address,address,uint256[])()" --rpc-url "[,,]" + /// forge script ./src/scripts/ops/LoanConsolidator.s.sol:LoanConsolidatorScript --chain mainnet --sig "consolidate(address,address,address,uint256[])()" --rpc-url "[,,]" function consolidate( address owner_, address clearinghouse_, @@ -44,8 +44,8 @@ contract CoolerUtilsScript is Test { // loanIds_[2] = 2; Cooler cooler = Cooler(cooler_); - CoolerUtils utils = CoolerUtils( - _env.readAddress(".current.mainnet.olympus.policies.CoolerUtils") + LoanConsolidator utils = LoanConsolidator( + _env.readAddress(".current.mainnet.olympus.policies.LoanConsolidator") ); // Determine the approvals required diff --git a/src/test/external/cooler/CoolerUtils.t.sol b/src/test/policies/LoanConsolidator.t.sol similarity index 94% rename from src/test/external/cooler/CoolerUtils.t.sol rename to src/test/policies/LoanConsolidator.t.sol index 62690b7ce..d75cfcb8f 100644 --- a/src/test/external/cooler/CoolerUtils.t.sol +++ b/src/test/policies/LoanConsolidator.t.sol @@ -15,10 +15,10 @@ import {Cooler} from "src/external/cooler/Cooler.sol"; import {RolesAdmin} from "src/policies/RolesAdmin.sol"; import {Kernel} from "src/Kernel.sol"; -import {CoolerUtils} from "src/external/cooler/CoolerUtils.sol"; +import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; -contract CoolerUtilsTest is Test { - CoolerUtils public utils; +contract LoanConsolidatorTest is Test { + LoanConsolidator public utils; ERC20 public ohm; ERC20 public gohm; @@ -70,8 +70,8 @@ contract CoolerUtilsTest is Test { admin = vm.addr(0x2); collector = vm.addr(0xC); - // Deploy CoolerUtils - utils = new CoolerUtils( + // Deploy LoanConsolidator + utils = new LoanConsolidator( address(gohm), address(sdai), address(dai), @@ -293,9 +293,9 @@ contract CoolerUtilsTest is Test { // [X] it reverts // given the caller is not the cooler owner // [X] it reverts - // given DAI spending approval has not been given to CoolerUtils + // given DAI spending approval has not been given to LoanConsolidator // [X] it reverts - // given gOHM spending approval has not been given to CoolerUtils + // given gOHM spending approval has not been given to LoanConsolidator // [X] it reverts // given the protocol fee is non-zero // [X] it transfers the protocol fee to the collector @@ -303,7 +303,7 @@ contract CoolerUtilsTest is Test { // [ ] it transfers the lender fee to the lender // when useFunds is non-zero // when sDAI is true - // given sDAI spending approval has not been given to CoolerUtils + // given sDAI spending approval has not been given to LoanConsolidator // [X] it reverts // given the sDAI amount is greater than required for fees // [X] it returns the surplus as DAI to the caller @@ -318,17 +318,17 @@ contract CoolerUtilsTest is Test { // [X] it transfers the specified amount of DAI into the contract, and reduces the flashloan amount by the balance // when the protocol fee is zero // [X] it succeeds, but does not transfer additional DAI for the fee - // [X] it takes a flashloan for the total debt amount + CoolerUtils fee, and consolidates the loans into one + // [X] it takes a flashloan for the total debt amount + LoanConsolidator fee, and consolidates the loans into one // --- consolidateWithFlashLoan -------------------------------------------- function test_consolidate_deactivated_reverts() public { - // Deactivate the CoolerUtils contract + // Deactivate the LoanConsolidator contract vm.prank(owner); utils.deactivate(); // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.OnlyActive.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyActive.selector); vm.expectRevert(err); // Consolidate loans for coolerA @@ -349,7 +349,9 @@ contract CoolerUtilsTest is Test { ); // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidClearinghouse.selector); + bytes memory err = abi.encodeWithSelector( + LoanConsolidator.Params_InvalidClearinghouse.selector + ); vm.expectRevert(err); // Consolidate loans for coolers A, B, and C into coolerC @@ -364,7 +366,7 @@ contract CoolerUtilsTest is Test { Cooler newCooler = new Cooler(); // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidCooler.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidCooler.selector); vm.expectRevert(err); // Consolidate loans for coolerA into newCooler @@ -379,7 +381,7 @@ contract CoolerUtilsTest is Test { // Expect revert since no loan ids are given bytes memory err = abi.encodeWithSelector( - CoolerUtils.Params_InsufficientCoolerCount.selector + LoanConsolidator.Params_InsufficientCoolerCount.selector ); vm.expectRevert(err); @@ -394,7 +396,7 @@ contract CoolerUtilsTest is Test { // Expect revert since no loan ids are given bytes memory err = abi.encodeWithSelector( - CoolerUtils.Params_InsufficientCoolerCount.selector + LoanConsolidator.Params_InsufficientCoolerCount.selector ); vm.expectRevert(err); @@ -417,7 +419,7 @@ contract CoolerUtilsTest is Test { _grantCallerApprovals(gohmApproval, totalDebtWithFee); // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.OnlyCoolerOwner.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyCoolerOwner.selector); vm.expectRevert(err); // Consolidate loans for coolers A, B, and C into coolerC @@ -728,7 +730,9 @@ contract CoolerUtilsTest is Test { deal(address(dai), walletA, totalDebtWithFee + 1); // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_UseFundsOutOfBounds.selector); + bytes memory err = abi.encodeWithSelector( + LoanConsolidator.Params_UseFundsOutOfBounds.selector + ); vm.expectRevert(err); // Consolidate loans for coolers A, B, and C into coolerC @@ -948,7 +952,7 @@ contract CoolerUtilsTest is Test { function test_setFeePercentage_aboveMax_reverts() public { // Expect revert bytes memory err = abi.encodeWithSelector( - CoolerUtils.Params_FeePercentageOutOfRange.selector + LoanConsolidator.Params_FeePercentageOutOfRange.selector ); vm.expectRevert(err); @@ -981,7 +985,7 @@ contract CoolerUtilsTest is Test { function test_setCollector_zeroAddress_reverts() public { // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); vm.expectRevert(err); vm.prank(owner); @@ -1011,7 +1015,7 @@ contract CoolerUtilsTest is Test { // Expect revert bytes memory err = abi.encodeWithSelector( - CoolerUtils.Params_InsufficientCoolerCount.selector + LoanConsolidator.Params_InsufficientCoolerCount.selector ); vm.expectRevert(err); @@ -1024,7 +1028,7 @@ contract CoolerUtilsTest is Test { // Expect revert bytes memory err = abi.encodeWithSelector( - CoolerUtils.Params_InsufficientCoolerCount.selector + LoanConsolidator.Params_InsufficientCoolerCount.selector ); vm.expectRevert(err); @@ -1246,10 +1250,10 @@ contract CoolerUtilsTest is Test { function test_constructor_zeroGOhm_reverts() public { // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils( + new LoanConsolidator( address(0), address(sdai), address(dai), @@ -1263,10 +1267,10 @@ contract CoolerUtilsTest is Test { function test_constructor_zeroSDai_reverts() public { // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils( + new LoanConsolidator( address(gohm), address(0), address(dai), @@ -1280,10 +1284,10 @@ contract CoolerUtilsTest is Test { function test_constructor_zeroDai_reverts() public { // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils( + new LoanConsolidator( address(gohm), address(sdai), address(0), @@ -1297,10 +1301,10 @@ contract CoolerUtilsTest is Test { function test_constructor_zeroOwner_reverts() public { // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils( + new LoanConsolidator( address(gohm), address(sdai), address(dai), @@ -1314,10 +1318,10 @@ contract CoolerUtilsTest is Test { function test_constructor_zeroLender_reverts() public { // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils( + new LoanConsolidator( address(gohm), address(sdai), address(dai), @@ -1331,10 +1335,10 @@ contract CoolerUtilsTest is Test { function test_constructor_zeroCollector_reverts() public { // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils( + new LoanConsolidator( address(gohm), address(sdai), address(dai), @@ -1348,10 +1352,10 @@ contract CoolerUtilsTest is Test { function test_constructor_zeroKernel_reverts() public { // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.Params_InvalidAddress.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); vm.expectRevert(err); - new CoolerUtils( + new LoanConsolidator( address(gohm), address(sdai), address(dai), @@ -1366,11 +1370,11 @@ contract CoolerUtilsTest is Test { function test_constructor_feePercentageAboveMax_reverts() public { // Expect revert bytes memory err = abi.encodeWithSelector( - CoolerUtils.Params_FeePercentageOutOfRange.selector + LoanConsolidator.Params_FeePercentageOutOfRange.selector ); vm.expectRevert(err); - new CoolerUtils( + new LoanConsolidator( address(gohm), address(sdai), address(dai), @@ -1385,7 +1389,7 @@ contract CoolerUtilsTest is Test { function test_constructor(uint256 feePercentage_) public { uint256 feePercentage = bound(feePercentage_, 0, _ONE_HUNDRED_PERCENT); - utils = new CoolerUtils( + utils = new LoanConsolidator( address(gohm), address(sdai), address(dai), @@ -1424,7 +1428,7 @@ contract CoolerUtilsTest is Test { givenDeactivated { // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.OnlyAdmin.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyAdmin.selector); vm.expectRevert(err); utils.activate(); @@ -1446,7 +1450,7 @@ contract CoolerUtilsTest is Test { function test_activate_asAdmin_adminNotSet_reverts() public givenDeactivated { // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.OnlyAdmin.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyAdmin.selector); vm.expectRevert(err); vm.prank(admin); @@ -1478,7 +1482,7 @@ contract CoolerUtilsTest is Test { givenActivated { // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.OnlyAdmin.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyAdmin.selector); vm.expectRevert(err); utils.deactivate(); @@ -1493,7 +1497,7 @@ contract CoolerUtilsTest is Test { function test_deactivate_asAdmin_adminNotSet_reverts() public { // Expect revert - bytes memory err = abi.encodeWithSelector(CoolerUtils.OnlyAdmin.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyAdmin.selector); vm.expectRevert(err); vm.prank(admin); From b04be60f6c74f48e23ea47f14a8db6e21abcc729 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 8 Oct 2024 14:29:41 +0400 Subject: [PATCH 016/160] Rename env key --- src/scripts/env.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/env.json b/src/scripts/env.json index 551a252e9..02003b994 100644 --- a/src/scripts/env.json +++ b/src/scripts/env.json @@ -93,7 +93,7 @@ "BLVaultLusd": "0xfbB3742628e8D19E0E2d7D8dde208821C09dE960", "Clearinghouse": "0xE6343ad0675C9b8D3f32679ae6aDbA0766A2ab4c", "LegacyBurner": "0x367149cf2d04D3114fFD1Cc6b273222664908D0B", - "CoolerUtils": "0xB15bcb1b6593d85890f5287Baa2245B8A29F464a", + "LoanConsolidator": "0xB15bcb1b6593d85890f5287Baa2245B8A29F464a", "pOLY": "0xb37796941cA55b7E4243841930C104Ee325Da5a1", "YieldRepurchaseFacility": "0x30A967eB957E5B1eE053B75F1A57ea6bfb2e907E" }, From a0a7990d3763aabafe53d7bf0d21ff8137fe4bf1 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 8 Oct 2024 15:20:41 +0400 Subject: [PATCH 017/160] Convert LoanConsolidator to a policy --- src/policies/LoanConsolidator.sol | 255 +++++++++++++++--------------- 1 file changed, 129 insertions(+), 126 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index 22ecd748c..4e0451bb0 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -3,10 +3,16 @@ pragma solidity ^0.8.15; import {IERC20} from "forge-std/interfaces/IERC20.sol"; import {IERC4626} from "forge-std/interfaces/IERC4626.sol"; -import {Owned} from "solmate/auth/Owned.sol"; -import {Kernel, Module, toKeycode} from "src/Kernel.sol"; +import {Kernel, Keycode, toKeycode, Permissions, Policy} from "src/Kernel.sol"; + import {CHREGv1} from "src/modules/CHREG/CHREG.v1.sol"; import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; +import {TRSRYv1} from "src/modules/TRSRY/TRSRY.v1.sol"; +import {EXREGv1} from "src/modules/EXREG/EXREG.v1.sol"; + +import {RolesConsumer} from "src/modules/ROLES/OlympusRoles.sol"; + +import {ReentrancyGuard} from "solmate/utils/ReentrancyGuard.sol"; import {IERC3156FlashBorrower} from "src/interfaces/maker-dao/IERC3156FlashBorrower.sol"; import {IERC3156FlashLender} from "src/interfaces/maker-dao/IERC3156FlashLender.sol"; @@ -14,17 +20,11 @@ import {Clearinghouse} from "src/policies/Clearinghouse.sol"; import {Cooler} from "src/external/cooler/Cooler.sol"; import {CoolerFactory} from "src/external/cooler/CoolerFactory.sol"; -// -// ██████╗ ██████╗ ██████╗ ██╗ ███████╗██████╗ ██╗ ██╗████████╗██╗██╗ ███████╗ -// ██╔════╝██╔═══██╗██╔═══██╗██║ ██╔════╝██╔══██╗ ██║ ██║╚══██╔══╝██║██║ ██╔════╝ -// ██║ ██║ ██║██║ ██║██║ █████╗ ██████╔╝ ██║ ██║ ██║ ██║██║ ███████╗ -// ██║ ██║ ██║██║ ██║██║ ██╔══╝ ██╔══██╗ ██║ ██║ ██║ ██║██║ ╚════██║ -// ╚██████╗╚██████╔╝╚██████╔╝███████╗███████╗██║ ██║ ╚██████╔╝ ██║ ██║███████╗███████║ -// ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚══════╝ -// - -contract LoanConsolidator is IERC3156FlashBorrower, Owned { - // --- ERRORS ------------------------------------------------------------------ +/// @title Loan Consolidator +/// @notice A policy that consolidates loans taken with a single Cooler contract into a single loan using Maker flashloans. +/// @dev This policy uses the `IERC3156FlashBorrower` interface to interact with Maker flashloans. +contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, ReentrancyGuard { + // ========= ERRORS ========= // /// @notice Thrown when the caller is not the contract itself. error OnlyThis(); @@ -38,9 +38,6 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { /// @notice Thrown when the contract is not active. error OnlyActive(); - /// @notice Thrown when the caller is not the contract owner or and does not have the "emergency_shutdown" role. - error OnlyAdmin(); - /// @notice Thrown when the fee percentage is out of range. /// @dev Valid values are 0 <= feePercentage <= 100e2 error Params_FeePercentageOutOfRange(); @@ -60,7 +57,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { /// @notice Thrown when the Cooler is not created by the CoolerFactory for the specified Clearinghouse error Params_InvalidCooler(); - // --- EVENTS --------------------------------------------------------- + // ========= EVENTS ========= // /// @notice Emitted when the contract is activated event Activated(); @@ -71,16 +68,9 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { /// @notice Emitted when the fee percentage is set event FeePercentageSet(uint256 feePercentage); - /// @notice Emitted when the collector is set - event CollectorSet(address collector); - - // --- DATA STRUCTURES --------------------------------------------------------- - - struct Batch { - address cooler; - uint256[] ids; - } + // ========= DATA STRUCTURES ========= // + /// @notice Data structure used for flashloan parameters struct FlashLoanData { address clearinghouse; address cooler; @@ -89,72 +79,110 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { uint256 protocolFee; } - // --- IMMUTABLES AND STATE VARIABLES ------------------------------------------ + // ========= STATE ========= // - /// @notice FlashLender contract used to take flashloans - IERC3156FlashLender public immutable lender; - IERC20 public immutable gohm; - IERC4626 public immutable sdai; - IERC20 public immutable dai; - Kernel public immutable kernel; + /// @notice The Clearinghouse registry module + CHREGv1 internal CHREG; + /// @notice The treasury module + TRSRYv1 internal TRSRY; + + /// @notice The external contract registry module + EXREGv1 internal EXREG; + + /// @notice The DAI token + IERC20 internal DAI; + + /// @notice The sDAI token + IERC4626 internal SDAI; + + /// @notice The gOHM token + IERC20 internal GOHM; + + /// @notice The flash loan provider + IERC3156FlashLender internal FLASH; + + /// @notice The denominator for percentage calculations uint256 public constant ONE_HUNDRED_PERCENT = 100e2; /// @notice Percentage of the debt to be paid as a fee /// @dev In terms of `ONE_HUNDRED_PERCENT` uint256 public feePercentage; - /// @notice Address permitted to collect protocol fees - address public collector; - /// @notice Whether the contract is active bool public active; - // --- INITIALIZATION ---------------------------------------------------------- - - constructor( - address gohm_, - address sdai_, - address dai_, - address owner_, - address lender_, - address collector_, - address kernel_, - uint256 feePercentage_ - ) Owned(owner_) { + /// @notice The role required to call admin functions + bytes32 public constant ROLE_ADMIN = "loan_consolidator_admin"; + + /// @notice The role required to call emergency shutdown functions + bytes32 public constant ROLE_EMERGENCY_SHUTDOWN = "emergency_shutdown"; + + // ========= CONSTRUCTOR ========= // + + /// @notice Constructor for the Loan Consolidator + /// @dev This function will revert if: + /// - The fee percentage is above `ONE_HUNDRED_PERCENT` + /// - The kernel address is zero + constructor(address kernel_, uint256 feePercentage_) Policy(Kernel(kernel_)) { // Validation if (feePercentage_ > ONE_HUNDRED_PERCENT) revert Params_FeePercentageOutOfRange(); - if (collector_ == address(0)) revert Params_InvalidAddress(); - if (owner_ == address(0)) revert Params_InvalidAddress(); - if (lender_ == address(0)) revert Params_InvalidAddress(); - if (gohm_ == address(0)) revert Params_InvalidAddress(); - if (sdai_ == address(0)) revert Params_InvalidAddress(); - if (dai_ == address(0)) revert Params_InvalidAddress(); if (kernel_ == address(0)) revert Params_InvalidAddress(); - // store contracts - gohm = IERC20(gohm_); - sdai = IERC4626(sdai_); - dai = IERC20(dai_); - - lender = IERC3156FlashLender(lender_); - // store protocol data - owner = owner_; - collector = collector_; feePercentage = feePercentage_; - kernel = Kernel(kernel_); // Activate the contract active = true; // Emit events emit FeePercentageSet(feePercentage); - emit CollectorSet(collector); emit Activated(); } - // --- OPERATION --------------------------------------------------------------- + /// @inheritdoc Policy + function configureDependencies() external override returns (Keycode[] memory dependencies) { + dependencies = new Keycode[](4); + dependencies[0] = toKeycode("CHREG"); + dependencies[1] = toKeycode("EXREG"); + dependencies[2] = toKeycode("ROLES"); + dependencies[3] = toKeycode("TRSRY"); + + // Populate module dependencies + CHREG = CHREGv1(getModuleAddress(dependencies[0])); + EXREG = EXREGv1(getModuleAddress(dependencies[1])); + ROLES = ROLESv1(getModuleAddress(dependencies[2])); + TRSRY = TRSRYv1(getModuleAddress(dependencies[3])); + + // Ensure Modules are using the expected major version. + // Modules should be sorted in alphabetical order. + bytes memory expected = abi.encode([1, 1, 1, 1]); + (uint8 CHREG_MAJOR, ) = CHREG.VERSION(); + (uint8 EXREG_MAJOR, ) = EXREG.VERSION(); + (uint8 ROLES_MAJOR, ) = ROLES.VERSION(); + (uint8 TRSRY_MAJOR, ) = TRSRY.VERSION(); + if (CHREG_MAJOR != 1 || EXREG_MAJOR != 1 || ROLES_MAJOR != 1 || TRSRY_MAJOR != 1) + revert Policy_WrongModuleVersion(expected); + + // Populate variables + // This function will be called whenever a contract is registered or deregistered, which enables caching of the values + DAI = IERC20(EXREG.getContract("dai")); + SDAI = IERC4626(EXREG.getContract("sdai")); + GOHM = IERC20(EXREG.getContract("gohm")); + FLASH = IERC3156FlashLender(EXREG.getContract("flash")); + + return dependencies; + } + + /// @inheritdoc Policy + /// @dev This policy does not require any permissions + function requestPermissions() external pure override returns (Permissions[] memory requests) { + requests = new Permissions[](0); + + return requests; + } + + // ========= OPERATION ========= // /// @notice Consolidate loans (taken with a single Cooler contract) into a single loan by using /// Maker flashloans. @@ -166,6 +194,8 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { /// - `cooler_` is not a valid Cooler for the Clearinghouse. /// - Less than two loans are being consolidated. /// - The available funds are less than the required flashloan amount. + /// - The contract is not active. + /// - Re-entrancy is detected. /// /// For flexibility purposes, the user can either pay with DAI or sDAI. /// @@ -180,7 +210,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { uint256[] calldata ids_, uint256 useFunds_, bool sdai_ - ) public onlyActive { + ) public onlyActive nonReentrant { // Validate that the Clearinghouse is registered with the Bophades kernel if (!_isValidClearinghouse(clearinghouse_)) revert Params_InvalidClearinghouse(); @@ -194,7 +224,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { (uint256 totalDebt, uint256 totalPrincipal) = _getDebtForLoans(cooler_, ids_); // Grant approval to the Cooler to spend the debt - dai.approve(cooler_, totalDebt); + DAI.approve(cooler_, totalDebt); // Ensure `msg.sender` is allowed to spend cooler funds on behalf of this contract Cooler cooler = Cooler(cooler_); @@ -204,14 +234,14 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { // This can also reduce the flashloan fee if (useFunds_ != 0) { if (sdai_) { - sdai.redeem(useFunds_, address(this), msg.sender); + SDAI.redeem(useFunds_, address(this), msg.sender); } else { - dai.transferFrom(msg.sender, address(this), useFunds_); + DAI.transferFrom(msg.sender, address(this), useFunds_); } } // Calculate the required flashloan amount based on available funds and protocol fee. - uint256 daiBalance = dai.balanceOf(address(this)); + uint256 daiBalance = DAI.balanceOf(address(this)); // Prevent an underflow if (daiBalance > totalDebt) { revert Params_UseFundsOutOfBounds(); @@ -232,15 +262,19 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { // Take flashloan // This will trigger the `onFlashLoan` function after the flashloan amount has been transferred to this contract - lender.flashLoan(this, address(dai), flashloan, params); + FLASH.flashLoan(this, address(DAI), flashloan, params); // This shouldn't happen, but transfer any leftover funds back to the sender - uint256 daiBalanceAfter = dai.balanceOf(address(this)); + uint256 daiBalanceAfter = DAI.balanceOf(address(this)); if (daiBalanceAfter > 0) { - dai.transfer(msg.sender, daiBalanceAfter); + DAI.transfer(msg.sender, daiBalanceAfter); } } + /// @inheritdoc IERC3156FlashBorrower + /// @dev This function reverts if: + /// - The caller is not the flash loan provider + /// - The initiator is not this contract function onFlashLoan( address initiator_, address, @@ -252,7 +286,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { Cooler cooler = Cooler(flashLoanData.cooler); // perform sanity checks - if (msg.sender != address(lender)) revert OnlyLender(); + if (msg.sender != address(FLASH)) revert OnlyLender(); if (initiator_ != address(this)) revert OnlyThis(); // Iterate over all batches, repay the debt and collect the collateral @@ -264,61 +298,50 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { // If the collateral required is greater than the collateral that was returned, then transfer gOHM from the cooler owner // This can happen as the collateral required for the consolidated loan can be greater than the sum of the collateral of the loans being consolidated - if (consolidatedLoanCollateral > gohm.balanceOf(address(this))) { - gohm.transferFrom( + if (consolidatedLoanCollateral > GOHM.balanceOf(address(this))) { + GOHM.transferFrom( cooler.owner(), address(this), - consolidatedLoanCollateral - gohm.balanceOf(address(this)) + consolidatedLoanCollateral - GOHM.balanceOf(address(this)) ); } // Take a new Cooler loan for the principal required - gohm.approve(flashLoanData.clearinghouse, consolidatedLoanCollateral); + GOHM.approve(flashLoanData.clearinghouse, consolidatedLoanCollateral); Clearinghouse(flashLoanData.clearinghouse).lendToCooler(cooler, flashLoanData.principal); // The cooler owner will receive DAI for the consolidated loan // Transfer this amount, plus the fee, to this contract // Approval must have already been granted by the Cooler owner - dai.transferFrom(cooler.owner(), address(this), amount_ + lenderFee_); + DAI.transferFrom(cooler.owner(), address(this), amount_ + lenderFee_); // Approve the flash loan provider to collect the flashloan amount and fee - dai.approve(address(lender), amount_ + lenderFee_); + DAI.approve(address(FLASH), amount_ + lenderFee_); // Pay protocol fee - if (flashLoanData.protocolFee != 0) dai.transfer(collector, flashLoanData.protocolFee); + if (flashLoanData.protocolFee != 0) DAI.transfer(address(TRSRY), flashLoanData.protocolFee); return keccak256("ERC3156FlashBorrower.onFlashLoan"); } - // --- ADMIN --------------------------------------------------- + // ========= ADMIN ========= // /// @notice Set the fee percentage /// @dev This function will revert if: /// - The fee percentage is above `ONE_HUNDRED_PERCENT` - /// - The caller is not the owner - function setFeePercentage(uint256 feePercentage_) external onlyOwner { + /// - The caller does not have the `ROLE_ADMIN` role + function setFeePercentage(uint256 feePercentage_) external onlyRole(ROLE_ADMIN) { if (feePercentage_ > ONE_HUNDRED_PERCENT) revert Params_FeePercentageOutOfRange(); feePercentage = feePercentage_; emit FeePercentageSet(feePercentage_); } - /// @notice Set the collector address - /// @dev This function will revert if: - /// - The address is the zero address - /// - The caller is not the owner - function setCollector(address collector_) external onlyOwner { - if (collector_ == address(0)) revert Params_InvalidAddress(); - - collector = collector_; - emit CollectorSet(collector_); - } - /// @notice Activate the contract /// @dev This function will revert if: - /// - The caller is not an admin + /// - The caller does not have the `ROLE_EMERGENCY_SHUTDOWN` role /// /// If the contract is already active, it will do nothing. - function activate() external onlyAdmin { + function activate() external onlyRole(ROLE_EMERGENCY_SHUTDOWN) { // Skip if already activated if (active) return; @@ -328,10 +351,10 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { /// @notice Deactivate the contract /// @dev This function will revert if: - /// - The caller is not an admin + /// - The caller does not have the `ROLE_EMERGENCY_SHUTDOWN` role /// /// If the contract is already deactivated, it will do nothing. - function deactivate() external onlyAdmin { + function deactivate() external onlyRole(ROLE_EMERGENCY_SHUTDOWN) { // Skip if already deactivated if (!active) return; @@ -345,21 +368,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { _; } - /// @notice Modifier to check that the caller is the contract owner or has the "emergency_shutdown" role - modifier onlyAdmin() { - // As this is not a policy, it does not have a `configuredDependencies()` function - // that will be called by the kernel whenever modules or policies are upgraded. - // Instead, the ROLES module is determined through a kernel lookup. - // As the `kernel` state variable is immutable, the risk of this being tampered with is low. - ROLESv1 roles = ROLESv1(address(kernel.getModuleForKeycode(toKeycode("ROLES")))); - - // Revert if not owner and does not have the "emergency_shutdown" role - if (msg.sender != owner && !roles.hasRole(msg.sender, "emergency_shutdown")) - revert OnlyAdmin(); - _; - } - - // --- INTERNAL FUNCTIONS ------------------------------------------------------ + // ========= FUNCTIONS ========= // function _getDebtForLoans( address cooler_, @@ -399,21 +408,15 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { } // Transfers all of the gOHM collateral to this contract - gohm.transferFrom(cooler.owner(), address(this), totalCollateral); + GOHM.transferFrom(cooler.owner(), address(this), totalCollateral); } function _isValidClearinghouse(address clearinghouse_) internal view returns (bool) { - // Get the Clearinghouse registry from the kernel - Module module = kernel.getModuleForKeycode(toKeycode("CHREG")); - if (address(module) == address(0)) revert Params_InvalidClearinghouse(); - - CHREGv1 chreg = CHREGv1(address(module)); - // We check against the registry (not just active), as repayments are still allowed when a Clearinghouse is deactivated - uint256 registryCount = chreg.registryCount(); + uint256 registryCount = CHREG.registryCount(); bool found; for (uint256 i; i < registryCount; i++) { - if (chreg.registry(i) == clearinghouse_) { + if (CHREG.registry(i) == clearinghouse_) { found = true; break; } @@ -435,7 +438,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { return coolerFactory.created(cooler_); } - // --- AUX FUNCTIONS ----------------------------------------------------------- + // ========= AUX FUNCTIONS ========= // /// @notice View function to compute the protocol fee for a given total debt. function getProtocolFee(uint256 totalDebt_) public view returns (uint256) { @@ -482,7 +485,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Owned { Cooler(cooler_).owner(), consolidatedLoanCollateral, totalDebtWithFee, - sdai.previewWithdraw(totalDebtWithFee), + SDAI.previewWithdraw(totalDebtWithFee), protocolFee ); } From 802e9b417cb765642194c2046908444a09feb130 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 8 Oct 2024 15:41:36 +0400 Subject: [PATCH 018/160] Update deployment and tests for LoanConsolidator --- src/scripts/deploy/DeployV2.sol | 22 +- .../cooler_loan_consolidator.json | 10 + .../deploy/savedDeployments/cooler_utils.json | 13 -- src/test/policies/LoanConsolidator.t.sol | 215 ++---------------- 4 files changed, 33 insertions(+), 227 deletions(-) create mode 100644 src/scripts/deploy/savedDeployments/cooler_loan_consolidator.json delete mode 100644 src/scripts/deploy/savedDeployments/cooler_utils.json diff --git a/src/scripts/deploy/DeployV2.sol b/src/scripts/deploy/DeployV2.sol index 2f183b985..4db8d32d1 100644 --- a/src/scripts/deploy/DeployV2.sol +++ b/src/scripts/deploy/DeployV2.sol @@ -1029,33 +1029,15 @@ contract OlympusDeploy is Script { function _deployLoanConsolidator(bytes calldata args_) public returns (address) { // Decode arguments from the sequence file - (address collector, uint256 feePercentage, address lender, address owner) = abi.decode( - args_, - (address, uint256, address, address) - ); + uint256 feePercentage = abi.decode(args_, (uint256)); // Print the arguments - console2.log(" gOHM:", address(gohm)); - console2.log(" SDAI:", address(wrappedReserve)); - console2.log(" DAI:", address(reserve)); - console2.log(" Collector:", collector); console2.log(" Fee Percentage:", feePercentage); - console2.log(" Lender:", lender); console2.log(" Kernel:", address(kernel)); - console2.log(" Owner:", owner); // Deploy LoanConsolidator vm.broadcast(); - loanConsolidator = new LoanConsolidator( - address(gohm), - address(wrappedReserve), - address(reserve), - owner, - lender, - collector, - address(kernel), - feePercentage - ); + loanConsolidator = new LoanConsolidator(address(kernel), feePercentage); console2.log(" LoanConsolidator deployed at:", address(loanConsolidator)); return address(loanConsolidator); diff --git a/src/scripts/deploy/savedDeployments/cooler_loan_consolidator.json b/src/scripts/deploy/savedDeployments/cooler_loan_consolidator.json new file mode 100644 index 000000000..8f14289c2 --- /dev/null +++ b/src/scripts/deploy/savedDeployments/cooler_loan_consolidator.json @@ -0,0 +1,10 @@ +{ + "sequence": [ + { + "name": "CoolerUtils", + "args": { + "feePercentage": 0 + } + } + ] +} \ No newline at end of file diff --git a/src/scripts/deploy/savedDeployments/cooler_utils.json b/src/scripts/deploy/savedDeployments/cooler_utils.json deleted file mode 100644 index 2c32c9f73..000000000 --- a/src/scripts/deploy/savedDeployments/cooler_utils.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "sequence": [ - { - "name": "CoolerUtils", - "args": { - "collector": "0xa8687A15D4BE32CC8F0a8a7B9704a4C3993D9613", - "feePercentage": 0, - "lender": "0x60744434d6339a6B27d73d9Eda62b6F66a0a04FA", - "owner": "0x245cc372C84B3645Bf0Ffe6538620B04a217988B" - } - } - ] -} \ No newline at end of file diff --git a/src/test/policies/LoanConsolidator.t.sol b/src/test/policies/LoanConsolidator.t.sol index d75cfcb8f..0969d0f99 100644 --- a/src/test/policies/LoanConsolidator.t.sol +++ b/src/test/policies/LoanConsolidator.t.sol @@ -12,6 +12,7 @@ import {CoolerFactory} from "src/external/cooler/CoolerFactory.sol"; import {Clearinghouse} from "src/policies/Clearinghouse.sol"; import {Cooler} from "src/external/cooler/Cooler.sol"; +import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; import {RolesAdmin} from "src/policies/RolesAdmin.sol"; import {Kernel} from "src/Kernel.sol"; @@ -71,16 +72,7 @@ contract LoanConsolidatorTest is Test { collector = vm.addr(0xC); // Deploy LoanConsolidator - utils = new LoanConsolidator( - address(gohm), - address(sdai), - address(dai), - owner, - lender, - collector, - kernel, - 0 - ); + utils = new LoanConsolidator(kernel, 0); walletA = vm.addr(0xA); @@ -969,36 +961,6 @@ contract LoanConsolidatorTest is Test { assertEq(utils.feePercentage(), feePercentage, "fee percentage"); } - // setCollector - // when the caller is not the owner - // [X] it reverts - // when the new collector is the zero address - // [X] it reverts - // [X] it sets the collector - - function test_setCollector_notOwner_reverts() public { - // Expect revert - vm.expectRevert("UNAUTHORIZED"); - - utils.setCollector(owner); - } - - function test_setCollector_zeroAddress_reverts() public { - // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); - vm.expectRevert(err); - - vm.prank(owner); - utils.setCollector(address(0)); - } - - function test_setCollector() public { - vm.prank(owner); - utils.setCollector(owner); - - assertEq(utils.collector(), owner, "collector"); - } - // requiredApprovals // when the caller has no loans // [X] it reverts @@ -1230,141 +1192,18 @@ contract LoanConsolidatorTest is Test { } // constructor - // when the gOHM address is the zero address - // [X] it reverts - // when the sDAI address is the zero address - // [X] it reverts - // when the DAI address is the zero address - // [X] it reverts - // when the owner address is the zero address - // [X] it reverts - // when the lender address is the zero address - // [X] it reverts - // when the collector address is the zero address - // [X] it reverts // when the kernel address is the zero address // [X] it reverts // when the fee percentage is > 100e2 // [X] it reverts // [X] it sets the values - function test_constructor_zeroGOhm_reverts() public { - // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); - vm.expectRevert(err); - - new LoanConsolidator( - address(0), - address(sdai), - address(dai), - owner, - lender, - collector, - kernel, - 0 - ); - } - - function test_constructor_zeroSDai_reverts() public { - // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); - vm.expectRevert(err); - - new LoanConsolidator( - address(gohm), - address(0), - address(dai), - owner, - lender, - collector, - kernel, - 0 - ); - } - - function test_constructor_zeroDai_reverts() public { - // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); - vm.expectRevert(err); - - new LoanConsolidator( - address(gohm), - address(sdai), - address(0), - owner, - lender, - collector, - kernel, - 0 - ); - } - - function test_constructor_zeroOwner_reverts() public { - // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); - vm.expectRevert(err); - - new LoanConsolidator( - address(gohm), - address(sdai), - address(dai), - address(0), - lender, - collector, - kernel, - 0 - ); - } - - function test_constructor_zeroLender_reverts() public { - // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); - vm.expectRevert(err); - - new LoanConsolidator( - address(gohm), - address(sdai), - address(dai), - owner, - address(0), - collector, - kernel, - 0 - ); - } - - function test_constructor_zeroCollector_reverts() public { - // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); - vm.expectRevert(err); - - new LoanConsolidator( - address(gohm), - address(sdai), - address(dai), - owner, - lender, - address(0), - kernel, - 0 - ); - } - function test_constructor_zeroKernel_reverts() public { // Expect revert bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidAddress.selector); vm.expectRevert(err); - new LoanConsolidator( - address(gohm), - address(sdai), - address(dai), - owner, - lender, - collector, - address(0), - 0 - ); + new LoanConsolidator(address(0), 0); } function test_constructor_feePercentageAboveMax_reverts() public { @@ -1374,38 +1213,14 @@ contract LoanConsolidatorTest is Test { ); vm.expectRevert(err); - new LoanConsolidator( - address(gohm), - address(sdai), - address(dai), - owner, - lender, - collector, - kernel, - _ONE_HUNDRED_PERCENT + 1 - ); + new LoanConsolidator(kernel, _ONE_HUNDRED_PERCENT + 1); } function test_constructor(uint256 feePercentage_) public { uint256 feePercentage = bound(feePercentage_, 0, _ONE_HUNDRED_PERCENT); - utils = new LoanConsolidator( - address(gohm), - address(sdai), - address(dai), - owner, - lender, - collector, - kernel, - feePercentage - ); + utils = new LoanConsolidator(kernel, feePercentage); - assertEq(address(utils.gohm()), address(gohm), "gOHM"); - assertEq(address(utils.sdai()), address(sdai), "sDai"); - assertEq(address(utils.dai()), address(dai), "DAI"); - assertEq(utils.owner(), owner, "owner"); - assertEq(address(utils.lender()), lender, "lender"); - assertEq(utils.collector(), collector, "collector"); assertEq(address(utils.kernel()), kernel, "kernel"); assertEq(utils.feePercentage(), feePercentage, "fee percentage"); } @@ -1428,7 +1243,10 @@ contract LoanConsolidatorTest is Test { givenDeactivated { // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyAdmin.selector); + bytes memory err = abi.encodeWithSelector( + ROLESv1.ROLES_RequireRole.selector, + "emergency_shutdown" + ); vm.expectRevert(err); utils.activate(); @@ -1450,7 +1268,10 @@ contract LoanConsolidatorTest is Test { function test_activate_asAdmin_adminNotSet_reverts() public givenDeactivated { // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyAdmin.selector); + bytes memory err = abi.encodeWithSelector( + ROLESv1.ROLES_RequireRole.selector, + "emergency_shutdown" + ); vm.expectRevert(err); vm.prank(admin); @@ -1482,7 +1303,10 @@ contract LoanConsolidatorTest is Test { givenActivated { // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyAdmin.selector); + bytes memory err = abi.encodeWithSelector( + ROLESv1.ROLES_RequireRole.selector, + "emergency_shutdown" + ); vm.expectRevert(err); utils.deactivate(); @@ -1497,7 +1321,10 @@ contract LoanConsolidatorTest is Test { function test_deactivate_asAdmin_adminNotSet_reverts() public { // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyAdmin.selector); + bytes memory err = abi.encodeWithSelector( + ROLESv1.ROLES_RequireRole.selector, + "emergency_shutdown" + ); vm.expectRevert(err); vm.prank(admin); From b7f2e75711317af37cbccaa46f22dd40cc38bccb Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 8 Oct 2024 16:06:40 +0400 Subject: [PATCH 019/160] Refresh dependent policies when registering/deregistering contracts in EXREG --- src/modules/EXREG/OlympusExternalRegistry.sol | 44 +++++++++- src/test/mocks/MockExternalRegistryPolicy.sol | 33 +++++++ src/test/modules/EXREG.t.sol | 87 +++++++++++++++++-- 3 files changed, 151 insertions(+), 13 deletions(-) create mode 100644 src/test/mocks/MockExternalRegistryPolicy.sol diff --git a/src/modules/EXREG/OlympusExternalRegistry.sol b/src/modules/EXREG/OlympusExternalRegistry.sol index 499790bd2..2d933ee48 100644 --- a/src/modules/EXREG/OlympusExternalRegistry.sol +++ b/src/modules/EXREG/OlympusExternalRegistry.sol @@ -1,12 +1,17 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.15; -import {Kernel, Module, Keycode, toKeycode} from "src/Kernel.sol"; +import {Kernel, Module, Policy, Keycode, toKeycode} from "src/Kernel.sol"; import {EXREGv1} from "./EXREG.v1.sol"; /// @title Olympus External Registry /// @notice This module is used to track the address of contracts that are external to the Bophades system. contract OlympusExternalRegistry is EXREGv1 { + // ========= STATE ========= // + + /// @notice The keycode for the Olympus External Registry + bytes5 public constant keycode = "EXREG"; + // ========= CONSTRUCTOR ========= // /// @notice Constructor for the Olympus External Registry @@ -22,7 +27,7 @@ contract OlympusExternalRegistry is EXREGv1 { /// @inheritdoc Module function KEYCODE() public pure override returns (Keycode) { - return toKeycode("EXREG"); + return toKeycode(keycode); } /// @inheritdoc Module @@ -34,7 +39,11 @@ contract OlympusExternalRegistry is EXREGv1 { // ========= CONTRACT REGISTRATION ========= // /// @inheritdoc EXREGv1 - /// @dev If the contract is already registered, the address will be updated. + /// @dev This function performs the following steps: + /// - Validates the parameters + /// - Updates the contract address + /// - Updates the contract names (if needed) + /// - Refreshes the dependent policies /// /// This function will revert if: /// - The caller is not permissioned @@ -49,12 +58,19 @@ contract OlympusExternalRegistry is EXREGv1 { _contracts[name_] = contractAddress_; _updateContractNames(name_); + _refreshDependents(); emit ContractRegistered(name_, contractAddress_); } /// @inheritdoc EXREGv1 - /// @dev This function will revert if: + /// @dev This function performs the following steps: + /// - Validates the parameters + /// - Removes the contract address + /// - Removes the contract name + /// - Refreshes the dependent policies + /// + /// This function will revert if: /// - The caller is not permissioned /// - The contract is not registered function deregisterContract(bytes5 name_) external override permissioned { @@ -63,6 +79,7 @@ contract OlympusExternalRegistry is EXREGv1 { delete _contracts[name_]; _removeContractName(name_); + _refreshDependents(); emit ContractDeregistered(name_); } @@ -120,4 +137,23 @@ contract OlympusExternalRegistry is EXREGv1 { } } } + + /// @notice Refreshes the dependents of the module + function _refreshDependents() internal { + Keycode moduleKeycode = toKeycode(keycode); + + // Iterate over each dependent policy until the end of the array is reached + uint256 dependentIndex; + while (true) { + try kernel.moduleDependents(moduleKeycode, dependentIndex) returns (Policy dependent) { + dependent.configureDependencies(); + unchecked { + ++dependentIndex; + } + } catch { + // If the call to the moduleDependents mapping reverts, then we have reached the end of the array + break; + } + } + } } diff --git a/src/test/mocks/MockExternalRegistryPolicy.sol b/src/test/mocks/MockExternalRegistryPolicy.sol new file mode 100644 index 000000000..8546dbe07 --- /dev/null +++ b/src/test/mocks/MockExternalRegistryPolicy.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Unlicensed +pragma solidity 0.8.15; + +import {Kernel, Policy, Keycode, toKeycode, Permissions} from "src/Kernel.sol"; +import {EXREGv1} from "src/modules/EXREG/EXREG.v1.sol"; + +contract MockExternalRegistryPolicy is Policy { + address public dai; + + EXREGv1 public exreg; + + constructor(Kernel kernel_) Policy(kernel_) {} + + function configureDependencies() external override returns (Keycode[] memory dependencies) { + dependencies = new Keycode[](1); + dependencies[0] = toKeycode("EXREG"); + + // Populate module dependencies + exreg = EXREGv1(getModuleAddress(dependencies[0])); + + // Populate variables + // This function will be called whenever a contract is registered or deregistered, which enables caching of the values + dai = exreg.getContract("dai"); + + return dependencies; + } + + function requestPermissions() external pure override returns (Permissions[] memory requests) { + requests = new Permissions[](0); + + return requests; + } +} diff --git a/src/test/modules/EXREG.t.sol b/src/test/modules/EXREG.t.sol index 1227a79f4..bd0ccc46a 100644 --- a/src/test/modules/EXREG.t.sol +++ b/src/test/modules/EXREG.t.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; +import {MockExternalRegistryPolicy} from "test/mocks/MockExternalRegistryPolicy.sol"; import {Kernel, Actions, Module, fromKeycode} from "src/Kernel.sol"; import {EXREGv1} from "src/modules/EXREG/EXREG.v1.sol"; @@ -19,6 +20,8 @@ contract ExternalRegistryTest is Test { Kernel internal _kernel; OlympusExternalRegistry internal _exreg; + MockExternalRegistryPolicy internal _policy; + MockExternalRegistryPolicy internal _policy2; // External Registry Expected events event ContractRegistered(bytes5 indexed name, address indexed contractAddress); @@ -29,6 +32,8 @@ contract ExternalRegistryTest is Test { // This contract is the owner _kernel = new Kernel(); _exreg = new OlympusExternalRegistry(address(_kernel)); + _policy = new MockExternalRegistryPolicy(_kernel); + _policy2 = new MockExternalRegistryPolicy(_kernel); // Generate fixtures godmode = _exreg.generateGodmodeFixture(type(OlympusExternalRegistry).name); @@ -48,6 +53,14 @@ contract ExternalRegistryTest is Test { _exreg.deregisterContract(name_); } + function _activatePolicyOne() internal { + _kernel.executeAction(Actions.ActivatePolicy, address(_policy)); + } + + function _activatePolicyTwo() internal { + _kernel.executeAction(Actions.ActivatePolicy, address(_policy2)); + } + // ========= TESTS ========= // // constructor @@ -56,13 +69,13 @@ contract ExternalRegistryTest is Test { // when the kernel address is not zero // [X] it sets the kernel address - function test_constructor_whenKernelAddressIsZero() public { + function test_constructor_whenKernelAddressIsZero_reverts() public { vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidAddress.selector)); new OlympusExternalRegistry(address(0)); } - function test_constructor_whenKernelAddressIsNotZero() public { + function test_constructor_whenKernelAddressIsNotZero_reverts() public { OlympusExternalRegistry exreg = new OlympusExternalRegistry(address(1)); assertEq(address(exreg.kernel()), address(1), "Kernel address is not set correctly"); @@ -81,8 +94,10 @@ contract ExternalRegistryTest is Test { // given there are existing registrations // [X] it updates the contract address, emits an event and updates the names array // [X] it registers the contract address, emits an event and updates the names array + // given dependent policies are registered + // [X] it refreshes the dependents - function test_registerContract_whenCallerIsNotPermissioned() public { + function test_registerContract_whenCallerIsNotPermissioned_reverts() public { vm.expectRevert( abi.encodeWithSelector(Module.Module_PolicyNotPermitted.selector, notOwner) ); @@ -91,19 +106,19 @@ contract ExternalRegistryTest is Test { _exreg.registerContract(bytes5("ohm"), addressOne); } - function test_registerContract_whenNameIsEmpty() public { + function test_registerContract_whenNameIsEmpty_reverts() public { vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); _registerContract(bytes5(""), addressOne); } - function test_registerContract_whenNameIsZero() public { + function test_registerContract_whenNameIsZero_reverts() public { vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); _registerContract(bytes5(0), addressOne); } - function test_registerContract_whenContractAddressIsZero() public { + function test_registerContract_whenContractAddressIsZero_reverts() public { vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidAddress.selector)); _registerContract(bytes5("ohm"), address(0)); @@ -199,6 +214,29 @@ contract ExternalRegistryTest is Test { ); } + function test_activatePolicies_whenContractIsRegistered() public { + // Register the contract + _registerContract(bytes5("dai"), addressOne); + + assertEq(_policy.dai(), address(0)); + assertEq(_policy2.dai(), address(0)); + + // Activate the dependent policies + _activatePolicyOne(); + _activatePolicyTwo(); + + assertEq(_policy.dai(), addressOne); + assertEq(_policy2.dai(), addressOne); + } + + function test_activatePolicies_whenContractNotRegistered_reverts() public { + // Expect it to revert + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + + // Activate the dependent policies + _activatePolicyOne(); + } + // deregisterContract // when the caller is not permissioned // [X] it reverts @@ -208,8 +246,12 @@ contract ExternalRegistryTest is Test { // given multiple names are registered // [X] it deregisters the name, emits an event and updates the names array // [X] it deregisters the name, emits an event and updates the names array + // given dependent policies are registered + // given one of the required contracts is deregistered + // [X] it reverts + // [X] it refreshes the dependents - function test_deregisterContract_whenCallerIsNotPermissioned() public { + function test_deregisterContract_whenCallerIsNotPermissioned_reverts() public { // Register the first time _registerContract(bytes5("ohm"), addressOne); @@ -221,7 +263,7 @@ contract ExternalRegistryTest is Test { _exreg.deregisterContract(bytes5("ohm")); } - function test_deregisterContract_whenNameIsNotRegistered() public { + function test_deregisterContract_whenNameIsNotRegistered_reverts() public { vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); _deregisterContract(bytes5("")); @@ -318,6 +360,33 @@ contract ExternalRegistryTest is Test { } } + function test_deregisterContract_whenDependentPoliciesAreRegistered_reverts() public { + // Register the contract + _registerContract(bytes5("dai"), addressOne); + + // Activate the dependent policies + _activatePolicyOne(); + _activatePolicyTwo(); + + // Expect it to revert + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + + // Deregister the contract + _deregisterContract(bytes5("dai")); + } + + function test_deregisterContract_whenDependentPoliciesAreNotRegistered() public { + // Register the contract + _registerContract(bytes5("ohm"), addressOne); + + // Deregister the contract + _deregisterContract(bytes5("ohm")); + + // Assert values + assertEq(_policy.dai(), address(0)); + assertEq(_policy2.dai(), address(0)); + } + // getContract // given the name is not registered // [X] it reverts @@ -326,7 +395,7 @@ contract ExternalRegistryTest is Test { // [X] it returns the latest address // [X] it returns the contract address - function test_getContract_whenNameIsNotRegistered() public { + function test_getContract_whenNameIsNotRegistered_reverts() public { vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); _exreg.getContract(bytes5("ohm")); From 03c10e96aaaeb19b961aa23b4cd50c25b14b3f0d Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 9 Oct 2024 11:45:43 +0400 Subject: [PATCH 020/160] LoanConsolidator: Rename active state variable to be distinct from Policy.isActive(). Add test stubs. --- src/policies/LoanConsolidator.sol | 44 +++++++++++++++--------- src/test/policies/LoanConsolidator.t.sol | 23 +++++++++---- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index 4e0451bb0..33b49bd58 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -36,7 +36,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent error OnlyCoolerOwner(); /// @notice Thrown when the contract is not active. - error OnlyActive(); + error OnlyConsolidatorActive(); /// @notice Thrown when the fee percentage is out of range. /// @dev Valid values are 0 <= feePercentage <= 100e2 @@ -60,10 +60,12 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent // ========= EVENTS ========= // /// @notice Emitted when the contract is activated - event Activated(); + /// @dev Note that this is different to activation of the contract as a policy + event ConsolidatorActivated(); /// @notice Emitted when the contract is deactivated - event Deactivated(); + /// @dev Note that this is different to deactivation of the contract as a policy + event ConsolidatorDeactivated(); /// @notice Emitted when the fee percentage is set event FeePercentageSet(uint256 feePercentage); @@ -110,7 +112,8 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent uint256 public feePercentage; /// @notice Whether the contract is active - bool public active; + /// @dev Note that this is different to the policy activation status + bool public consolidatorActive; /// @notice The role required to call admin functions bytes32 public constant ROLE_ADMIN = "loan_consolidator_admin"; @@ -132,12 +135,14 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent // store protocol data feePercentage = feePercentage_; - // Activate the contract - active = true; + // Set the contract to be active + // It is activated here so that it is performed by default + // However, the contract will not be useable until it has been installed as a policy + consolidatorActive = true; // Emit events emit FeePercentageSet(feePercentage); - emit Activated(); + emit ConsolidatorActivated(); } /// @inheritdoc Policy @@ -195,6 +200,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// - Less than two loans are being consolidated. /// - The available funds are less than the required flashloan amount. /// - The contract is not active. + /// - The contract has not been activated as a policy. /// - Re-entrancy is detected. /// /// For flexibility purposes, the user can either pay with DAI or sDAI. @@ -210,7 +216,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent uint256[] calldata ids_, uint256 useFunds_, bool sdai_ - ) public onlyActive nonReentrant { + ) public onlyConsolidatorActive nonReentrant { // Validate that the Clearinghouse is registered with the Bophades kernel if (!_isValidClearinghouse(clearinghouse_)) revert Params_InvalidClearinghouse(); @@ -327,6 +333,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @notice Set the fee percentage /// @dev This function will revert if: + /// - The contract has not been activated as a policy. /// - The fee percentage is above `ONE_HUNDRED_PERCENT` /// - The caller does not have the `ROLE_ADMIN` role function setFeePercentage(uint256 feePercentage_) external onlyRole(ROLE_ADMIN) { @@ -338,33 +345,35 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @notice Activate the contract /// @dev This function will revert if: + /// - The contract has not been activated as a policy. /// - The caller does not have the `ROLE_EMERGENCY_SHUTDOWN` role /// /// If the contract is already active, it will do nothing. function activate() external onlyRole(ROLE_EMERGENCY_SHUTDOWN) { // Skip if already activated - if (active) return; + if (consolidatorActive) return; - active = true; - emit Activated(); + consolidatorActive = true; + emit ConsolidatorActivated(); } /// @notice Deactivate the contract /// @dev This function will revert if: + /// - The contract has not been activated as a policy. /// - The caller does not have the `ROLE_EMERGENCY_SHUTDOWN` role /// /// If the contract is already deactivated, it will do nothing. function deactivate() external onlyRole(ROLE_EMERGENCY_SHUTDOWN) { // Skip if already deactivated - if (!active) return; + if (!consolidatorActive) return; - active = false; - emit Deactivated(); + consolidatorActive = false; + emit ConsolidatorDeactivated(); } /// @notice Modifier to check that the contract is active - modifier onlyActive() { - if (!active) revert OnlyActive(); + modifier onlyConsolidatorActive() { + if (!consolidatorActive) revert OnlyConsolidatorActive(); _; } @@ -448,6 +457,9 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @notice View function to compute the required approval amounts that the owner of a given Cooler /// must give to this contract in order to consolidate the loans. /// + /// @dev This function will revert if: + /// - The contract has not been activated as a policy. + /// /// @param cooler_ Contract which issued the loans. /// @param ids_ Array of loan ids to be consolidated. /// @return owner Owner of the Cooler (address that should grant the approval). diff --git a/src/test/policies/LoanConsolidator.t.sol b/src/test/policies/LoanConsolidator.t.sol index 0969d0f99..5f7a24085 100644 --- a/src/test/policies/LoanConsolidator.t.sol +++ b/src/test/policies/LoanConsolidator.t.sol @@ -275,6 +275,8 @@ contract LoanConsolidatorTest is Test { // consolidateWithFlashLoan // given the contract has been disabled // [X] it reverts + // given the contract has not been activated as a policy + // [ ] it reverts // when the clearinghouse is not registered with CHREG // [X] it reverts // when the cooler was not created by a valid CoolerFactory @@ -320,7 +322,7 @@ contract LoanConsolidatorTest is Test { utils.deactivate(); // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyActive.selector); + bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyConsolidatorActive.selector); vm.expectRevert(err); // Consolidate loans for coolerA @@ -927,6 +929,8 @@ contract LoanConsolidatorTest is Test { } // setFeePercentage + // when the policy is not active + // [ ] it reverts // when the caller is not the owner // [X] it reverts // when the fee is > 100% @@ -962,6 +966,8 @@ contract LoanConsolidatorTest is Test { } // requiredApprovals + // when the policy is not active + // [ ] it reverts // when the caller has no loans // [X] it reverts // when the caller has 1 loan @@ -1223,9 +1229,12 @@ contract LoanConsolidatorTest is Test { assertEq(address(utils.kernel()), kernel, "kernel"); assertEq(utils.feePercentage(), feePercentage, "fee percentage"); + assertEq(utils.consolidatorActive(), true, "consolidator active"); } // activate + // when the policy is not active + // [ ] it reverts // when the caller is not an admin or owner // [X] it reverts // when the caller is the owner @@ -1256,14 +1265,14 @@ contract LoanConsolidatorTest is Test { vm.prank(owner); utils.activate(); - assertTrue(utils.active(), "active"); + assertTrue(utils.consolidatorActive(), "consolidator active"); } function test_activate_asAdmin_setsActive() public givenAdminHasEmergencyRole givenDeactivated { vm.prank(admin); utils.activate(); - assertTrue(utils.active(), "active"); + assertTrue(utils.consolidatorActive(), "consolidator active"); } function test_activate_asAdmin_adminNotSet_reverts() public givenDeactivated { @@ -1282,10 +1291,12 @@ contract LoanConsolidatorTest is Test { vm.prank(owner); utils.activate(); - assertTrue(utils.active(), "active"); + assertTrue(utils.consolidatorActive(), "consolidator active"); } // deactivate + // when the policy is not active + // [ ] it reverts // when the caller is not an admin or owner // [X] it reverts // when the caller is the owner @@ -1316,7 +1327,7 @@ contract LoanConsolidatorTest is Test { vm.prank(owner); utils.deactivate(); - assertFalse(utils.active(), "active"); + assertFalse(utils.consolidatorActive(), "consolidator active"); } function test_deactivate_asAdmin_adminNotSet_reverts() public { @@ -1339,7 +1350,7 @@ contract LoanConsolidatorTest is Test { vm.prank(owner); utils.deactivate(); - assertFalse(utils.active(), "active"); + assertFalse(utils.consolidatorActive(), "consolidator active"); } // --- AUX FUNCTIONS ----------------------------------------------------------- From 79e12cffe8b296a313652ca7b70de1917f449bc2 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 9 Oct 2024 13:07:27 +0400 Subject: [PATCH 021/160] Fix formatter used by VSCode --- .vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a35dab1d8..8063c9119 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,7 @@ "editor.formatOnSave": true, "solidity.formatter": "prettier", "[solidity]": { - "editor.defaultFormatter": "JuanBlanco.solidity" + "editor.defaultFormatter": "NomicFoundation.hardhat-solidity" }, "solidity.defaultCompiler": "remote", "solidity.compileUsingRemoteVersion": "v0.8.15+commit.e14f2714" From f491d0ecca71331d9fe5abfd9a323b6725a0c2e1 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 9 Oct 2024 13:07:47 +0400 Subject: [PATCH 022/160] Add policy to interface with EXREG --- src/policies/ExternalRegistryAdmin.sol | 135 +++++++++++ src/test/policies/ExternalRegistryAdmin.t.sol | 216 ++++++++++++++++++ 2 files changed, 351 insertions(+) create mode 100644 src/policies/ExternalRegistryAdmin.sol create mode 100644 src/test/policies/ExternalRegistryAdmin.t.sol diff --git a/src/policies/ExternalRegistryAdmin.sol b/src/policies/ExternalRegistryAdmin.sol new file mode 100644 index 000000000..5668b958b --- /dev/null +++ b/src/policies/ExternalRegistryAdmin.sol @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.15; + +import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; +import {RolesConsumer} from "src/modules/ROLES/OlympusRoles.sol"; +import {EXREGv1} from "src/modules/EXREG/EXREG.v1.sol"; +import {Kernel, Policy, Keycode, toKeycode, Permissions} from "src/Kernel.sol"; + +/// @title ExternalRegistryAdmin +/// @notice This policy is used to register and deregister contracts in the EXREG module. +contract ExternalRegistryAdmin is Policy, RolesConsumer { + // ============ ERRORS ============ // + + /// @notice Thrown when the address is invalid + error Params_InvalidAddress(); + + /// @notice Thrown when the contract is not activated as a policy + error OnlyPolicyActive(); + + // ============ STATE ============ // + + /// @notice The EXREG module + EXREGv1 internal _EXREG; + + /// @notice The role for the external registry admin + bytes32 public constant EXTERNAL_REGISTRY_ADMIN_ROLE = "external_registry_admin"; + + // ============ CONSTRUCTOR ============ // + + constructor(address kernel_) Policy(Kernel(kernel_)) { + // Validate that the kernel address is valid + if (kernel_ == address(0)) revert Params_InvalidAddress(); + } + + // ============ POLICY FUNCTIONS ============ // + + /// @inheritdoc Policy + function configureDependencies() external override returns (Keycode[] memory dependencies) { + dependencies = new Keycode[](2); + dependencies[0] = toKeycode("EXREG"); + dependencies[1] = toKeycode("ROLES"); + + _EXREG = EXREGv1(getModuleAddress(dependencies[0])); + ROLES = ROLESv1(getModuleAddress(dependencies[1])); + + // Verify the supported version + bytes memory expected = abi.encode([1, 1]); + (uint8 EXREG_MAJOR, ) = _EXREG.VERSION(); + (uint8 ROLES_MAJOR, ) = ROLES.VERSION(); + if (EXREG_MAJOR != 1 || ROLES_MAJOR != 1) revert Policy_WrongModuleVersion(expected); + + return dependencies; + } + + /// @inheritdoc Policy + function requestPermissions() + external + pure + override + returns (Permissions[] memory permissions) + { + Keycode exregKeycode = toKeycode("EXREG"); + + permissions = new Permissions[](2); + permissions[0] = Permissions(exregKeycode, EXREGv1.registerContract.selector); + permissions[1] = Permissions(exregKeycode, EXREGv1.deregisterContract.selector); + + return permissions; + } + + /// @notice The version of the policy + function VERSION() external pure returns (uint8) { + return 1; + } + + // ============ MODIFIERS ============ // + + /// @notice Modifier to check that the contract is activated as a policy + modifier onlyPolicyActive() { + if (!kernel.isPolicyActive(this)) revert OnlyPolicyActive(); + _; + } + + // ============ ADMIN FUNCTIONS ============ // + + /// @notice Register a contract in the external registry + /// @dev This function will revert if: + /// - This contract is not activated as a policy + /// - The caller does not have the required role + /// - The EXREG module reverts + /// + /// @param name_ The name of the contract + /// @param contractAddress_ The address of the contract + function registerContract( + bytes5 name_, + address contractAddress_ + ) external onlyPolicyActive onlyRole(EXTERNAL_REGISTRY_ADMIN_ROLE) { + _EXREG.registerContract(name_, contractAddress_); + } + + /// @notice Deregister a contract in the external registry + /// @dev This function will revert if: + /// - This contract is not activated as a policy + /// - The caller does not have the required role + /// - The EXREG module reverts + /// + /// @param name_ The name of the contract + function deregisterContract( + bytes5 name_ + ) external onlyPolicyActive onlyRole(EXTERNAL_REGISTRY_ADMIN_ROLE) { + _EXREG.deregisterContract(name_); + } + + // ============ VIEW FUNCTIONS ============ // + + /// @notice Get the address of the contract + /// @dev This function will revert if: + /// - This contract is not activated as a policy + /// - The EXREG module reverts + /// + /// @return The address of the contract + function getContract(bytes5 name_) external view onlyPolicyActive returns (address) { + return _EXREG.getContract(name_); + } + + /// @notice Get the names of the contracts + /// @dev This function will revert if: + /// - This contract is not activated as a policy + /// - The EXREG module reverts + /// + /// @return The names of the contracts + function getContractNames() external view onlyPolicyActive returns (bytes5[] memory) { + return _EXREG.getContractNames(); + } +} diff --git a/src/test/policies/ExternalRegistryAdmin.t.sol b/src/test/policies/ExternalRegistryAdmin.t.sol new file mode 100644 index 000000000..e7b234998 --- /dev/null +++ b/src/test/policies/ExternalRegistryAdmin.t.sol @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: Unlicensed +pragma solidity 0.8.15; + +import {Test} from "forge-std/Test.sol"; + +import {Kernel, Actions} from "src/Kernel.sol"; +import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; +import {OlympusRoles} from "src/modules/ROLES/OlympusRoles.sol"; +import {RolesAdmin} from "src/policies/RolesAdmin.sol"; +import {EXREGv1} from "src/modules/EXREG/EXREG.v1.sol"; +import {OlympusExternalRegistry} from "src/modules/EXREG/OlympusExternalRegistry.sol"; +import {ExternalRegistryAdmin} from "src/policies/ExternalRegistryAdmin.sol"; + +contract ExternalRegistryAdminTest is Test { + Kernel public kernel; + OlympusExternalRegistry public EXREG; + ExternalRegistryAdmin public exRegAdmin; + OlympusRoles public ROLES; + RolesAdmin public rolesAdmin; + + address public admin = address(0x1); + address public notAdmin = address(0x2); + address public ohm = address(0x3); + + bytes32 public EXREG_ROLE = "external_registry_admin"; + + function setUp() public { + kernel = new Kernel(); + + // Install the ROLES module + ROLES = new OlympusRoles(kernel); + kernel.executeAction(Actions.InstallModule, address(ROLES)); + + // Install the RolesAdmin policy + rolesAdmin = new RolesAdmin(kernel); + kernel.executeAction(Actions.ActivatePolicy, address(rolesAdmin)); + + // Install the EXREG module + EXREG = new OlympusExternalRegistry(address(kernel)); + kernel.executeAction(Actions.InstallModule, address(EXREG)); + + // Set up the ExternalRegistryAdmin policy + exRegAdmin = new ExternalRegistryAdmin(address(kernel)); + } + + modifier givenPolicyIsActivated() { + kernel.executeAction(Actions.ActivatePolicy, address(exRegAdmin)); + _; + } + + modifier givenAdminHasRole() { + rolesAdmin.grantRole(EXREG_ROLE, admin); + _; + } + + modifier givenContractIsRegistered() { + vm.prank(admin); + exRegAdmin.registerContract("ohm", ohm); + _; + } + + // ===== TESTS ===== // + + // registerContract + // when the policy is not active + // [X] it reverts + // when the caller does not have the role + // [X] it reverts + // [X] it registers the contract + + function test_registerContract_policyNotActive_reverts() public { + vm.expectRevert(abi.encodeWithSelector(ExternalRegistryAdmin.OnlyPolicyActive.selector)); + + exRegAdmin.registerContract("ohm", ohm); + } + + function test_registerContract_callerDoesNotHaveRole_reverts() + public + givenPolicyIsActivated + givenAdminHasRole + { + vm.expectRevert(abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, EXREG_ROLE)); + + vm.prank(notAdmin); + exRegAdmin.registerContract("ohm", ohm); + } + + function test_registerContract() + public + givenPolicyIsActivated + givenAdminHasRole + givenContractIsRegistered + { + assertEq(EXREG.getContract("ohm"), ohm, "contract address"); + } + + // deregisterContract + // when the policy is not active + // [X] it reverts + // when the caller does not have the role + // [X] it reverts + // [X] it deregisters the contract + + function test_deregisterContract_policyNotActive_reverts() public { + vm.expectRevert(abi.encodeWithSelector(ExternalRegistryAdmin.OnlyPolicyActive.selector)); + + exRegAdmin.deregisterContract("ohm"); + } + + function test_deregisterContract_callerDoesNotHaveRole_reverts() + public + givenPolicyIsActivated + givenAdminHasRole + givenContractIsRegistered + { + vm.expectRevert(abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, EXREG_ROLE)); + + vm.prank(notAdmin); + exRegAdmin.deregisterContract("ohm"); + } + + function test_deregisterContract() + public + givenPolicyIsActivated + givenAdminHasRole + givenContractIsRegistered + { + // Deregister the contract + vm.prank(admin); + exRegAdmin.deregisterContract("ohm"); + + // Assert values + vm.expectRevert(EXREGv1.Params_InvalidName.selector); + EXREG.getContract("ohm"); + } + + // getContract + // when the policy is not active + // [X] it reverts + // when the caller is not the admin + // [X] it returns the contract address + // [X] it returns the contract address + + function test_getContract_policyNotActive_reverts() public { + vm.expectRevert(abi.encodeWithSelector(ExternalRegistryAdmin.OnlyPolicyActive.selector)); + + exRegAdmin.getContract("ohm"); + } + + function test_getContract_notAdmin() + public + givenPolicyIsActivated + givenAdminHasRole + givenContractIsRegistered + { + vm.prank(notAdmin); + address contractAddress = exRegAdmin.getContract("ohm"); + + // Assert values + assertEq(contractAddress, ohm, "contract address"); + } + + function test_getContract() + public + givenPolicyIsActivated + givenAdminHasRole + givenContractIsRegistered + { + vm.prank(admin); + address contractAddress = exRegAdmin.getContract("ohm"); + + // Assert values + assertEq(contractAddress, ohm, "contract address"); + assertEq(EXREG.getContract("ohm"), ohm, "EXREG: contract address"); + } + + // getContractNames + // when the policy is not active + // [X] it reverts + // when the caller is not the admin + // [X] it returns the contract names + // [X] it returns the contract names + + function test_getContractNames_policyNotActive_reverts() public { + vm.expectRevert(abi.encodeWithSelector(ExternalRegistryAdmin.OnlyPolicyActive.selector)); + + exRegAdmin.getContractNames(); + } + + function test_getContractNames_notAdmin() + public + givenPolicyIsActivated + givenAdminHasRole + givenContractIsRegistered + { + vm.prank(notAdmin); + bytes5[] memory contractNames = exRegAdmin.getContractNames(); + + // Assert values + assertEq(contractNames.length, 1, "contract names length"); + assertEq(contractNames[0], "ohm", "contract name"); + } + + function test_getContractNames() + public + givenPolicyIsActivated + givenAdminHasRole + givenContractIsRegistered + { + bytes5[] memory contractNames = exRegAdmin.getContractNames(); + + // Assert values + assertEq(contractNames.length, 1, "contract names length"); + assertEq(contractNames[0], "ohm", "contract name"); + } +} From 43c857101488e5e75caf368e8dfa581192c828b9 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 9 Oct 2024 13:19:49 +0400 Subject: [PATCH 023/160] EXREG: force contract names to be lowercase or have a numeral --- src/modules/EXREG/OlympusExternalRegistry.sol | 23 +++++++++++++ src/test/modules/EXREG.t.sol | 34 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/modules/EXREG/OlympusExternalRegistry.sol b/src/modules/EXREG/OlympusExternalRegistry.sol index 2d933ee48..442933f6c 100644 --- a/src/modules/EXREG/OlympusExternalRegistry.sol +++ b/src/modules/EXREG/OlympusExternalRegistry.sol @@ -45,9 +45,14 @@ contract OlympusExternalRegistry is EXREGv1 { /// - Updates the contract names (if needed) /// - Refreshes the dependent policies /// + /// The contract name can contain: + /// - Lowercase letters + /// - Numerals + /// /// This function will revert if: /// - The caller is not permissioned /// - The name is empty + /// - The name contains punctuation or uppercase letters /// - The contract address is zero function registerContract( bytes5 name_, @@ -56,6 +61,24 @@ contract OlympusExternalRegistry is EXREGv1 { if (name_ == bytes5(0)) revert Params_InvalidName(); if (contractAddress_ == address(0)) revert Params_InvalidAddress(); + // Check that the contract name is lowercase letters and numerals only + for (uint256 i = 0; i < 5; i++) { + bytes1 char = name_[i]; + + // 0-9 + if (char >= 0x30 && char <= 0x39) continue; + + // a-z + if (char >= 0x61 && char <= 0x7A) continue; + + // Skip if empty + // An empty name has already been checked at the start + if (char == 0x00) continue; + + revert Params_InvalidName(); + } + + // Register the contract _contracts[name_] = contractAddress_; _updateContractNames(name_); _refreshDependents(); diff --git a/src/test/modules/EXREG.t.sol b/src/test/modules/EXREG.t.sol index bd0ccc46a..816a7125a 100644 --- a/src/test/modules/EXREG.t.sol +++ b/src/test/modules/EXREG.t.sol @@ -88,6 +88,12 @@ contract ExternalRegistryTest is Test { // [X] it reverts // when the contract address is zero // [X] it reverts + // when the name is not lowercase + // [X] it reverts + // when the name contains punctuation + // [X] it reverts + // when the name contains a numeral + // [X] it succeeds // given the name is registered // [X] it updates the contract address and emits an event, but does not update the names array // given the name is not registered @@ -118,6 +124,34 @@ contract ExternalRegistryTest is Test { _registerContract(bytes5(0), addressOne); } + function test_registerContract_whenNameIsNotLowercase_reverts() public { + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + _registerContract(bytes5("Ohm"), addressOne); + + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + _registerContract(bytes5("oHm"), addressOne); + + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + _registerContract(bytes5("ohM"), addressOne); + } + + function test_registerContract_whenNameContainsPunctuation_reverts() public { + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + _registerContract(bytes5("ohm!"), addressOne); + + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + _registerContract(bytes5("ohm "), addressOne); + + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + _registerContract(bytes5("ohm-"), addressOne); + } + + function test_registerContract_whenNameContainsNumeral() public { + _registerContract(bytes5("ohm1"), addressOne); + + assertEq(_exreg.getContract(bytes5("ohm1")), addressOne); + } + function test_registerContract_whenContractAddressIsZero_reverts() public { vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidAddress.selector)); From 4eb71103f177fff29249b85df66a9bcc8aaf438b Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 9 Oct 2024 15:52:48 +0400 Subject: [PATCH 024/160] Avoid stalling due to formatting on save --- .vscode/settings.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 8063c9119..02f15cf86 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,4 @@ { - "editor.formatOnSave": true, "solidity.formatter": "prettier", "[solidity]": { "editor.defaultFormatter": "NomicFoundation.hardhat-solidity" From cb7137149f82d31678f3599e87c6071a4968dbbf Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 9 Oct 2024 15:52:57 +0400 Subject: [PATCH 025/160] Document roles used --- src/policies/ExternalRegistryAdmin.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/policies/ExternalRegistryAdmin.sol b/src/policies/ExternalRegistryAdmin.sol index 5668b958b..a2affbfed 100644 --- a/src/policies/ExternalRegistryAdmin.sol +++ b/src/policies/ExternalRegistryAdmin.sol @@ -8,6 +8,8 @@ import {Kernel, Policy, Keycode, toKeycode, Permissions} from "src/Kernel.sol"; /// @title ExternalRegistryAdmin /// @notice This policy is used to register and deregister contracts in the EXREG module. +/// @dev This contract utilises the following roles: +/// - `external_registry_admin`: Can register and deregister contracts contract ExternalRegistryAdmin is Policy, RolesConsumer { // ============ ERRORS ============ // From 40efb96528f677f54f91d38278d2b33f85181b71 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 9 Oct 2024 15:53:35 +0400 Subject: [PATCH 026/160] LoanConsolidator: disable functions if the policy is not active --- src/policies/LoanConsolidator.sol | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index 33b49bd58..05dee2722 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -23,6 +23,10 @@ import {CoolerFactory} from "src/external/cooler/CoolerFactory.sol"; /// @title Loan Consolidator /// @notice A policy that consolidates loans taken with a single Cooler contract into a single loan using Maker flashloans. /// @dev This policy uses the `IERC3156FlashBorrower` interface to interact with Maker flashloans. +/// +/// This contract utilises the following roles: +/// - `loan_consolidator_admin`: Can set the fee percentage +/// - `emergency_shutdown`: Can activate and deactivate the contract contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, ReentrancyGuard { // ========= ERRORS ========= // @@ -38,6 +42,9 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @notice Thrown when the contract is not active. error OnlyConsolidatorActive(); + /// @notice Thrown when the contract is not activated as a policy. + error OnlyPolicyActive(); + /// @notice Thrown when the fee percentage is out of range. /// @dev Valid values are 0 <= feePercentage <= 100e2 error Params_FeePercentageOutOfRange(); @@ -216,7 +223,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent uint256[] calldata ids_, uint256 useFunds_, bool sdai_ - ) public onlyConsolidatorActive nonReentrant { + ) public onlyPolicyActive onlyConsolidatorActive nonReentrant { // Validate that the Clearinghouse is registered with the Bophades kernel if (!_isValidClearinghouse(clearinghouse_)) revert Params_InvalidClearinghouse(); @@ -336,7 +343,9 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// - The contract has not been activated as a policy. /// - The fee percentage is above `ONE_HUNDRED_PERCENT` /// - The caller does not have the `ROLE_ADMIN` role - function setFeePercentage(uint256 feePercentage_) external onlyRole(ROLE_ADMIN) { + function setFeePercentage( + uint256 feePercentage_ + ) external onlyPolicyActive onlyRole(ROLE_ADMIN) { if (feePercentage_ > ONE_HUNDRED_PERCENT) revert Params_FeePercentageOutOfRange(); feePercentage = feePercentage_; @@ -349,7 +358,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// - The caller does not have the `ROLE_EMERGENCY_SHUTDOWN` role /// /// If the contract is already active, it will do nothing. - function activate() external onlyRole(ROLE_EMERGENCY_SHUTDOWN) { + function activate() external onlyPolicyActive onlyRole(ROLE_EMERGENCY_SHUTDOWN) { // Skip if already activated if (consolidatorActive) return; @@ -363,7 +372,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// - The caller does not have the `ROLE_EMERGENCY_SHUTDOWN` role /// /// If the contract is already deactivated, it will do nothing. - function deactivate() external onlyRole(ROLE_EMERGENCY_SHUTDOWN) { + function deactivate() external onlyPolicyActive onlyRole(ROLE_EMERGENCY_SHUTDOWN) { // Skip if already deactivated if (!consolidatorActive) return; @@ -377,6 +386,12 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent _; } + /// @notice Modifier to check that the contract is activated as a policy + modifier onlyPolicyActive() { + if (!kernel.isPolicyActive(this)) revert OnlyPolicyActive(); + _; + } + // ========= FUNCTIONS ========= // function _getDebtForLoans( @@ -471,7 +486,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent address clearinghouse_, address cooler_, uint256[] calldata ids_ - ) external view returns (address, uint256, uint256, uint256, uint256) { + ) external view onlyPolicyActive returns (address, uint256, uint256, uint256, uint256) { if (ids_.length < 2) revert Params_InsufficientCoolerCount(); uint256 totalPrincipal; From 776933ecc83cb988c34406da692e41bcf24f5a00 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 9 Oct 2024 15:56:02 +0400 Subject: [PATCH 027/160] LoanConsolidator: update tests to support conversion to policy --- src/test/policies/LoanConsolidator.t.sol | 422 +++++++++++++++-------- 1 file changed, 281 insertions(+), 141 deletions(-) diff --git a/src/test/policies/LoanConsolidator.t.sol b/src/test/policies/LoanConsolidator.t.sol index 5f7a24085..e05f77f14 100644 --- a/src/test/policies/LoanConsolidator.t.sol +++ b/src/test/policies/LoanConsolidator.t.sol @@ -12,9 +12,12 @@ import {CoolerFactory} from "src/external/cooler/CoolerFactory.sol"; import {Clearinghouse} from "src/policies/Clearinghouse.sol"; import {Cooler} from "src/external/cooler/Cooler.sol"; +import {OlympusExternalRegistry} from "src/modules/EXREG/OlympusExternalRegistry.sol"; +import {ExternalRegistryAdmin} from "src/policies/ExternalRegistryAdmin.sol"; import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; import {RolesAdmin} from "src/policies/RolesAdmin.sol"; -import {Kernel} from "src/Kernel.sol"; +import {TRSRYv1} from "src/modules/TRSRY/TRSRY.v1.sol"; +import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; @@ -29,14 +32,16 @@ contract LoanConsolidatorTest is Test { CoolerFactory public coolerFactory; Clearinghouse public clearinghouse; + OlympusExternalRegistry public EXREG; + ExternalRegistryAdmin public exregAdmin; RolesAdmin public rolesAdmin; + TRSRYv1 public TRSRY; + Kernel public kernel; address public staking; - address public kernel; - address public owner; address public lender; - address public collector; address public admin; + address public emergency; address public kernelExecutor; address public walletA; @@ -45,8 +50,16 @@ contract LoanConsolidatorTest is Test { uint256 internal constant _GOHM_AMOUNT = 3_333 * 1e18; uint256 internal constant _ONE_HUNDRED_PERCENT = 100e2; + uint256 internal trsryDaiBalance; + uint256 internal trsryGOhmBalance; + uint256 internal trsrySDaiBalance; + string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); + // These are replicated here so that if they are updated, the tests will fail + bytes32 public constant ROLE_ADMIN = "loan_consolidator_admin"; + bytes32 public constant ROLE_EMERGENCY_SHUTDOWN = "emergency_shutdown"; + function setUp() public { // Mainnet Fork at current block. vm.createSelectFork(RPC_URL, 18762666); @@ -60,19 +73,52 @@ contract LoanConsolidatorTest is Test { dai = ERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); sdai = IERC4626(0x83F20F44975D03b1b09e64809B757c47f942BEeA); lender = 0x60744434d6339a6B27d73d9Eda62b6F66a0a04FA; - kernel = 0x2286d7f9639e8158FaD1169e76d1FbC38247f54b; staking = 0xB63cac384247597756545b500253ff8E607a8020; + + kernel = Kernel(0x2286d7f9639e8158FaD1169e76d1FbC38247f54b); rolesAdmin = RolesAdmin(0xb216d714d91eeC4F7120a732c11428857C659eC8); + TRSRY = TRSRYv1(address(kernel.getModuleForKeycode(toKeycode("TRSRY")))); + + // Cache the TRSRY balances + trsryDaiBalance = dai.balanceOf(address(TRSRY)); + trsryGOhmBalance = gohm.balanceOf(address(TRSRY)); + trsrySDaiBalance = sdai.balanceOf(address(TRSRY)); // Determine the kernel executor kernelExecutor = Kernel(kernel).executor(); - owner = vm.addr(0x1); + // Install EXREG if not already installed + address exregAddress = address(kernel.getModuleForKeycode(toKeycode("EXREG"))); + if (exregAddress == address(0)) { + EXREG = new OlympusExternalRegistry(address(kernel)); + vm.prank(kernelExecutor); + kernel.executeAction(Actions.InstallModule, address(EXREG)); + } else { + EXREG = OlympusExternalRegistry(exregAddress); + } + + // Set up and install the external registry admin policy + exregAdmin = new ExternalRegistryAdmin(address(kernel)); + vm.prank(kernelExecutor); + kernel.executeAction(Actions.ActivatePolicy, address(exregAdmin)); + + // Grant the external registry admin role to this contract + vm.prank(kernelExecutor); + rolesAdmin.grantRole("external_registry_admin", address(this)); + + // Register the tokens with EXREG + vm.startPrank(address(this)); + exregAdmin.registerContract("dai", address(dai)); + exregAdmin.registerContract("sdai", address(sdai)); + exregAdmin.registerContract("ohm", address(ohm)); + exregAdmin.registerContract("gohm", address(gohm)); + exregAdmin.registerContract("flash", address(lender)); + vm.stopPrank(); + admin = vm.addr(0x2); - collector = vm.addr(0xC); // Deploy LoanConsolidator - utils = new LoanConsolidator(kernel, 0); + utils = new LoanConsolidator(address(kernel), 0); walletA = vm.addr(0xA); @@ -103,8 +149,20 @@ contract LoanConsolidatorTest is Test { // ===== MODIFIERS ===== // + modifier givenAdminHasRole() { + vm.prank(kernelExecutor); + rolesAdmin.grantRole(ROLE_ADMIN, admin); + _; + } + + modifier givenEmergencyHasRole() { + vm.prank(kernelExecutor); + rolesAdmin.grantRole(ROLE_EMERGENCY_SHUTDOWN, emergency); + _; + } + modifier givenProtocolFee(uint256 feePercent_) { - vm.prank(owner); + vm.prank(admin); utils.setFeePercentage(feePercent_); _; } @@ -179,24 +237,24 @@ contract LoanConsolidatorTest is Test { return _getInterestDue(address(coolerA), ids_); } + modifier givenPolicyActive() { + vm.prank(kernelExecutor); + kernel.executeAction(Actions.ActivatePolicy, address(utils)); + _; + } + modifier givenActivated() { - vm.prank(owner); + vm.prank(emergency); utils.activate(); _; } modifier givenDeactivated() { - vm.prank(owner); + vm.prank(emergency); utils.deactivate(); _; } - modifier givenAdminHasEmergencyRole() { - vm.prank(kernelExecutor); - rolesAdmin.grantRole("emergency_shutdown", admin); - _; - } - // ===== ASSERTIONS ===== // function _assertCoolerLoans(uint256 collateral_) internal { @@ -223,17 +281,21 @@ contract LoanConsolidatorTest is Test { assertEq(dai.balanceOf(walletA), walletABalance, "dai: walletA"); assertEq(dai.balanceOf(address(coolerA)), 0, "dai: coolerA"); assertEq(dai.balanceOf(lender), lenderBalance, "dai: lender"); - assertEq(dai.balanceOf(collector), collectorBalance, "dai: collector"); + assertEq( + dai.balanceOf(address(TRSRY)), + trsryDaiBalance + collectorBalance, + "dai: collector" + ); assertEq(sdai.balanceOf(address(utils)), 0, "sdai: utils"); assertEq(sdai.balanceOf(walletA), 0, "sdai: walletA"); assertEq(sdai.balanceOf(address(coolerA)), 0, "sdai: coolerA"); assertEq(sdai.balanceOf(lender), 0, "sdai: lender"); - assertEq(sdai.balanceOf(collector), 0, "sdai: collector"); + assertEq(sdai.balanceOf(address(TRSRY)), trsrySDaiBalance, "sdai: collector"); assertEq(gohm.balanceOf(address(utils)), 0, "gohm: utils"); assertEq(gohm.balanceOf(walletA), 0, "gohm: walletA"); assertEq(gohm.balanceOf(address(coolerA)), collateralBalance, "gohm: coolerA"); assertEq(gohm.balanceOf(lender), 0, "gohm: lender"); - assertEq(gohm.balanceOf(collector), 0, "gohm: collector"); + assertEq(gohm.balanceOf(address(TRSRY)), trsryGOhmBalance, "gohm: collector"); } function _assertApprovals() internal { @@ -273,10 +335,10 @@ contract LoanConsolidatorTest is Test { // ===== TESTS ===== // // consolidateWithFlashLoan + // given the contract has not been activated as a policy + // [X] it reverts // given the contract has been disabled // [X] it reverts - // given the contract has not been activated as a policy - // [ ] it reverts // when the clearinghouse is not registered with CHREG // [X] it reverts // when the cooler was not created by a valid CoolerFactory @@ -316,21 +378,31 @@ contract LoanConsolidatorTest is Test { // --- consolidateWithFlashLoan -------------------------------------------- - function test_consolidate_deactivated_reverts() public { - // Deactivate the LoanConsolidator contract - vm.prank(owner); - utils.deactivate(); + function test_consolidate_policyNotActive_reverts() public { + // Expect revert + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyPolicyActive.selector)); + // Consolidate loans for coolerA + uint256[] memory idsA = _idsA(); + _consolidate(idsA); + } + + function test_consolidate_deactivated_reverts() + public + givenAdminHasRole + givenEmergencyHasRole + givenPolicyActive + givenDeactivated + { // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyConsolidatorActive.selector); - vm.expectRevert(err); + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyConsolidatorActive.selector)); // Consolidate loans for coolerA uint256[] memory idsA = _idsA(); _consolidate(idsA); } - function test_consolidate_thirdPartyClearinghouse_reverts() public { + function test_consolidate_thirdPartyClearinghouse_reverts() public givenPolicyActive { // Create a new Clearinghouse // It is not registered with CHREG, so should be rejected Clearinghouse newClearinghouse = new Clearinghouse( @@ -339,14 +411,13 @@ contract LoanConsolidatorTest is Test { staking, address(sdai), address(coolerFactory), - kernel + address(kernel) ); // Expect revert - bytes memory err = abi.encodeWithSelector( - LoanConsolidator.Params_InvalidClearinghouse.selector + vm.expectRevert( + abi.encodeWithSelector(LoanConsolidator.Params_InvalidClearinghouse.selector) ); - vm.expectRevert(err); // Consolidate loans for coolers A, B, and C into coolerC uint256[] memory idsA = _idsA(); @@ -354,14 +425,13 @@ contract LoanConsolidatorTest is Test { utils.consolidateWithFlashLoan(address(newClearinghouse), address(coolerA), idsA, 0, false); } - function test_consolidate_thirdPartyCooler_reverts() public { + function test_consolidate_thirdPartyCooler_reverts() public givenPolicyActive { // Create a new Cooler // It was not created by the Clearinghouse's CoolerFactory, so should be rejected Cooler newCooler = new Cooler(); // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.Params_InvalidCooler.selector); - vm.expectRevert(err); + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.Params_InvalidCooler.selector)); // Consolidate loans for coolerA into newCooler uint256[] memory idsA = _idsA(); @@ -369,30 +439,28 @@ contract LoanConsolidatorTest is Test { utils.consolidateWithFlashLoan(address(clearinghouse), address(newCooler), idsA, 0, false); } - function test_consolidate_noLoans_reverts() public { + function test_consolidate_noLoans_reverts() public givenPolicyActive { // Grant approvals _grantCallerApprovals(type(uint256).max, type(uint256).max); // Expect revert since no loan ids are given - bytes memory err = abi.encodeWithSelector( - LoanConsolidator.Params_InsufficientCoolerCount.selector + vm.expectRevert( + abi.encodeWithSelector(LoanConsolidator.Params_InsufficientCoolerCount.selector) ); - vm.expectRevert(err); // Consolidate loans, but give no ids uint256[] memory ids = new uint256[](0); _consolidate(ids); } - function test_consolidate_oneLoan_reverts() public { + function test_consolidate_oneLoan_reverts() public givenPolicyActive { // Grant approvals _grantCallerApprovals(type(uint256).max, type(uint256).max); // Expect revert since no loan ids are given - bytes memory err = abi.encodeWithSelector( - LoanConsolidator.Params_InsufficientCoolerCount.selector + vm.expectRevert( + abi.encodeWithSelector(LoanConsolidator.Params_InsufficientCoolerCount.selector) ); - vm.expectRevert(err); // Consolidate loans, but give one id uint256[] memory ids = new uint256[](1); @@ -400,7 +468,7 @@ contract LoanConsolidatorTest is Test { _consolidate(ids); } - function test_consolidate_callerNotOwner_reverts() public { + function test_consolidate_callerNotOwner_reverts() public givenPolicyActive { uint256[] memory idsA = _idsA(); // Grant approvals @@ -413,15 +481,14 @@ contract LoanConsolidatorTest is Test { _grantCallerApprovals(gohmApproval, totalDebtWithFee); // Expect revert - bytes memory err = abi.encodeWithSelector(LoanConsolidator.OnlyCoolerOwner.selector); - vm.expectRevert(err); + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyCoolerOwner.selector)); // Consolidate loans for coolers A, B, and C into coolerC // Do not perform as the cooler owner utils.consolidateWithFlashLoan(address(clearinghouse), address(coolerA), idsA, 0, false); } - function test_consolidate_insufficientGOhmApproval_reverts() public { + function test_consolidate_insufficientGOhmApproval_reverts() public givenPolicyActive { uint256[] memory idsA = _idsA(); // Grant approvals @@ -439,7 +506,7 @@ contract LoanConsolidatorTest is Test { _consolidate(idsA); } - function test_consolidate_insufficientDaiApproval_reverts() public { + function test_consolidate_insufficientDaiApproval_reverts() public givenPolicyActive { uint256[] memory idsA = _idsA(); // Grant approvals @@ -457,7 +524,7 @@ contract LoanConsolidatorTest is Test { _consolidate(idsA, 2, false); } - function test_consolidate_insufficientSdaiApproval_reverts() public { + function test_consolidate_insufficientSdaiApproval_reverts() public givenPolicyActive { uint256[] memory idsA = _idsA(); // Grant approvals @@ -477,7 +544,7 @@ contract LoanConsolidatorTest is Test { _consolidate(idsA, 2, true); } - function test_consolidate_noProtocolFee() public { + function test_consolidate_noProtocolFee() public givenPolicyActive { uint256[] memory idsA = _idsA(); // Grant approvals @@ -504,7 +571,7 @@ contract LoanConsolidatorTest is Test { function test_consolidate_noProtocolFee_fuzz( uint256 loanOneCollateral_, uint256 loanTwoCollateral_ - ) public { + ) public givenPolicyActive { // Bound the collateral values loanOneCollateral_ = bound(loanOneCollateral_, 1, 1e18); loanTwoCollateral_ = bound(loanTwoCollateral_, 1, 1e18); @@ -597,6 +664,8 @@ contract LoanConsolidatorTest is Test { function test_consolidate_protocolFee() public + givenAdminHasRole + givenPolicyActive givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -624,7 +693,7 @@ contract LoanConsolidatorTest is Test { _assertApprovals(); } - function test_consolidate_whenUseFundsLessThanTotalDebt() public { + function test_consolidate_whenUseFundsLessThanTotalDebt() public givenPolicyActive { uint256[] memory idsA = _idsA(); // Grant approvals @@ -648,7 +717,7 @@ contract LoanConsolidatorTest is Test { _assertApprovals(); } - function test_consolidate_whenUseFundsEqualToTotalDebt() public { + function test_consolidate_whenUseFundsEqualToTotalDebt() public givenPolicyActive { uint256[] memory idsA = _idsA(); // Grant approvals @@ -676,6 +745,8 @@ contract LoanConsolidatorTest is Test { function test_consolidate_protocolFee_whenUseFundsGreaterThanProtocolFee() public + givenAdminHasRole + givenPolicyActive givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -708,7 +779,7 @@ contract LoanConsolidatorTest is Test { _assertApprovals(); } - function test_consolidate_whenUseFundsGreaterThanTotalDebt_reverts() public { + function test_consolidate_whenUseFundsGreaterThanTotalDebt_reverts() public givenPolicyActive { uint256[] memory idsA = _idsA(); // Grant approvals @@ -736,6 +807,8 @@ contract LoanConsolidatorTest is Test { function test_consolidate_protocolFee_whenUseFundsLessThanProtocolFee() public + givenAdminHasRole + givenPolicyActive givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -770,6 +843,8 @@ contract LoanConsolidatorTest is Test { function test_consolidate_protocolFee_whenUseFundsEqualToProtocolFee() public + givenAdminHasRole + givenPolicyActive givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -804,6 +879,8 @@ contract LoanConsolidatorTest is Test { function test_consolidate_protocolFee_whenUseFundsEqualToProtocolFee_usingSDai() public + givenAdminHasRole + givenPolicyActive givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -846,6 +923,8 @@ contract LoanConsolidatorTest is Test { function test_consolidate_protocolFee_whenUseFundsGreaterThanProtocolFee_usingSDai() public + givenAdminHasRole + givenPolicyActive givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -888,6 +967,8 @@ contract LoanConsolidatorTest is Test { function test_consolidate_protocolFee_whenUseFundsLessThanProtocolFee_usingSDai() public + givenAdminHasRole + givenPolicyActive givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -930,36 +1011,45 @@ contract LoanConsolidatorTest is Test { // setFeePercentage // when the policy is not active - // [ ] it reverts - // when the caller is not the owner + // [X] it reverts + // when the caller is not the admin // [X] it reverts // when the fee is > 100% // [X] it reverts // [X] it sets the fee percentage - function test_setFeePercentage_notOwner_reverts() public { + function test_setFeePercentage_whenPolicyNotActive_reverts() public givenAdminHasRole { // Expect revert - vm.expectRevert("UNAUTHORIZED"); + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyPolicyActive.selector)); - // Set the fee percentage as a non-owner + vm.prank(admin); utils.setFeePercentage(1000); } - function test_setFeePercentage_aboveMax_reverts() public { + function test_setFeePercentage_notAdmin_reverts() public givenAdminHasRole givenPolicyActive { // Expect revert - bytes memory err = abi.encodeWithSelector( - LoanConsolidator.Params_FeePercentageOutOfRange.selector + vm.expectRevert(abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, ROLE_ADMIN)); + + // Set the fee percentage as a non-admin + utils.setFeePercentage(1000); + } + + function test_setFeePercentage_aboveMax_reverts() public givenAdminHasRole givenPolicyActive { + // Expect revert + vm.expectRevert( + abi.encodeWithSelector(LoanConsolidator.Params_FeePercentageOutOfRange.selector) ); - vm.expectRevert(err); - vm.prank(owner); + vm.prank(admin); utils.setFeePercentage(_ONE_HUNDRED_PERCENT + 1); } - function test_setFeePercentage(uint256 feePercentage_) public { + function test_setFeePercentage( + uint256 feePercentage_ + ) public givenAdminHasRole givenPolicyActive { uint256 feePercentage = bound(feePercentage_, 0, _ONE_HUNDRED_PERCENT); - vm.prank(owner); + vm.prank(admin); utils.setFeePercentage(feePercentage); assertEq(utils.feePercentage(), feePercentage, "fee percentage"); @@ -967,7 +1057,7 @@ contract LoanConsolidatorTest is Test { // requiredApprovals // when the policy is not active - // [ ] it reverts + // [X] it reverts // when the caller has no loans // [X] it reverts // when the caller has 1 loan @@ -978,32 +1068,39 @@ contract LoanConsolidatorTest is Test { // [X] it returns the correct values // [X] it returns the correct values for owner, gOHM amount, total DAI debt and sDAI amount - function test_requiredApprovals_noLoans() public { + function test_requiredApprovals_policyNotActive_reverts() public { + uint256[] memory ids = _idsA(); + + // Expect revert + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyPolicyActive.selector)); + + utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); + } + + function test_requiredApprovals_noLoans() public givenPolicyActive { uint256[] memory ids = new uint256[](0); // Expect revert - bytes memory err = abi.encodeWithSelector( - LoanConsolidator.Params_InsufficientCoolerCount.selector + vm.expectRevert( + abi.encodeWithSelector(LoanConsolidator.Params_InsufficientCoolerCount.selector) ); - vm.expectRevert(err); utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); } - function test_requiredApprovals_oneLoan() public { + function test_requiredApprovals_oneLoan() public givenPolicyActive { uint256[] memory ids = new uint256[](1); ids[0] = 0; // Expect revert - bytes memory err = abi.encodeWithSelector( - LoanConsolidator.Params_InsufficientCoolerCount.selector + vm.expectRevert( + abi.encodeWithSelector(LoanConsolidator.Params_InsufficientCoolerCount.selector) ); - vm.expectRevert(err); utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); } - function test_requiredApprovals_noProtocolFee() public { + function test_requiredApprovals_noProtocolFee() public givenPolicyActive { uint256[] memory ids = _idsA(); ( @@ -1029,6 +1126,8 @@ contract LoanConsolidatorTest is Test { function test_requiredApprovals_ProtocolFee() public + givenAdminHasRole + givenPolicyActive givenProtocolFee(1000) // 1% { uint256[] memory ids = _idsA(); @@ -1068,7 +1167,7 @@ contract LoanConsolidatorTest is Test { function test_requiredApprovals_fuzz( uint256 loanOneCollateral_, uint256 loanTwoCollateral_ - ) public { + ) public givenPolicyActive { // Bound the collateral values loanOneCollateral_ = bound(loanOneCollateral_, 1, 1e18); loanTwoCollateral_ = bound(loanTwoCollateral_, 1, 1e18); @@ -1122,7 +1221,7 @@ contract LoanConsolidatorTest is Test { function test_collateralRequired_fuzz( uint256 loanOneCollateral_, uint256 loanTwoCollateral_ - ) public { + ) public givenPolicyActive { // Bound the collateral values loanOneCollateral_ = bound(loanOneCollateral_, 1, 1e18); loanTwoCollateral_ = bound(loanTwoCollateral_, 1, 1e18); @@ -1219,67 +1318,71 @@ contract LoanConsolidatorTest is Test { ); vm.expectRevert(err); - new LoanConsolidator(kernel, _ONE_HUNDRED_PERCENT + 1); + new LoanConsolidator(address(kernel), _ONE_HUNDRED_PERCENT + 1); } function test_constructor(uint256 feePercentage_) public { uint256 feePercentage = bound(feePercentage_, 0, _ONE_HUNDRED_PERCENT); - utils = new LoanConsolidator(kernel, feePercentage); + utils = new LoanConsolidator(address(kernel), feePercentage); - assertEq(address(utils.kernel()), kernel, "kernel"); + assertEq(address(utils.kernel()), address(kernel), "kernel"); assertEq(utils.feePercentage(), feePercentage, "fee percentage"); assertEq(utils.consolidatorActive(), true, "consolidator active"); } // activate // when the policy is not active - // [ ] it reverts - // when the caller is not an admin or owner // [X] it reverts - // when the caller is the owner - // [X] it sets the active flag to true - // when the caller is an admin - // when the admin is not set - // [X] it reverts + // when the caller is not an admin or emergency shutdown + // [X] it reverts + // when the caller is the admin role + // [X] it reverts + // when the caller is the emergency shutdown role + // when the contract is already active + // [X] it does nothing // [X] it sets the active flag to true - // when the contract is already active - // [X] it does nothing - function test_activate_notAdminOrOwner_reverts() + function test_activate_policyNotActive_reverts() + public + givenAdminHasRole + givenEmergencyHasRole + { + // Expect revert + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyPolicyActive.selector)); + + vm.prank(emergency); + utils.activate(); + } + + function test_activate_notAdminOrEmergency_reverts() public - givenAdminHasEmergencyRole + givenAdminHasRole + givenEmergencyHasRole + givenPolicyActive givenDeactivated { // Expect revert bytes memory err = abi.encodeWithSelector( ROLESv1.ROLES_RequireRole.selector, - "emergency_shutdown" + ROLE_EMERGENCY_SHUTDOWN ); vm.expectRevert(err); utils.activate(); } - function test_activate_asOwner_setsActive() public givenAdminHasEmergencyRole givenDeactivated { - vm.prank(owner); - utils.activate(); - - assertTrue(utils.consolidatorActive(), "consolidator active"); - } - - function test_activate_asAdmin_setsActive() public givenAdminHasEmergencyRole givenDeactivated { - vm.prank(admin); - utils.activate(); - - assertTrue(utils.consolidatorActive(), "consolidator active"); - } - - function test_activate_asAdmin_adminNotSet_reverts() public givenDeactivated { + function test_activate_asAdmin_reverts() + public + givenAdminHasRole + givenEmergencyHasRole + givenPolicyActive + givenDeactivated + { // Expect revert bytes memory err = abi.encodeWithSelector( ROLESv1.ROLES_RequireRole.selector, - "emergency_shutdown" + ROLE_EMERGENCY_SHUTDOWN ); vm.expectRevert(err); @@ -1287,8 +1390,26 @@ contract LoanConsolidatorTest is Test { utils.activate(); } - function test_activate_asAdmin_alreadyActive() public givenAdminHasEmergencyRole { - vm.prank(owner); + function test_activate_asEmergency() + public + givenAdminHasRole + givenEmergencyHasRole + givenPolicyActive + givenDeactivated + { + vm.prank(emergency); + utils.activate(); + + assertTrue(utils.consolidatorActive(), "consolidator active"); + } + + function test_activate_asEmergency_alreadyActive() + public + givenAdminHasRole + givenEmergencyHasRole + givenPolicyActive + { + vm.prank(emergency); utils.activate(); assertTrue(utils.consolidatorActive(), "consolidator active"); @@ -1296,58 +1417,77 @@ contract LoanConsolidatorTest is Test { // deactivate // when the policy is not active - // [ ] it reverts - // when the caller is not an admin or owner // [X] it reverts - // when the caller is the owner - // [X] it sets the active flag to false - // when the caller is an admin - // when the admin is not set - // [X] it reverts + // when the caller is not an admin or emergency shutdown + // [X] it reverts + // when the caller has the admin role + // [X] it reverts + // when the caller has the emergency shutdown role + // when the contract is already deactivated + // [X] it does nothing // [X] it sets the active flag to false - // when the contract is already deactivated - // [X] it does nothing - function test_deactivate_notAdminOrOwner_reverts() + function test_deactivate_policyNotActive_reverts() public - givenAdminHasEmergencyRole - givenActivated + givenAdminHasRole + givenEmergencyHasRole { // Expect revert - bytes memory err = abi.encodeWithSelector( - ROLESv1.ROLES_RequireRole.selector, - "emergency_shutdown" - ); - vm.expectRevert(err); + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyPolicyActive.selector)); + vm.prank(emergency); utils.deactivate(); } - function test_deactivate_asOwner_setsActive() public givenAdminHasEmergencyRole { - vm.prank(owner); - utils.deactivate(); + function test_deactivate_notAdminOrEmergency_reverts() + public + givenAdminHasRole + givenEmergencyHasRole + givenPolicyActive + { + // Expect revert + vm.expectRevert( + abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, ROLE_EMERGENCY_SHUTDOWN) + ); - assertFalse(utils.consolidatorActive(), "consolidator active"); + utils.deactivate(); } - function test_deactivate_asAdmin_adminNotSet_reverts() public { + function test_deactivate_asAdmin_reverts() + public + givenAdminHasRole + givenEmergencyHasRole + givenPolicyActive + { // Expect revert - bytes memory err = abi.encodeWithSelector( - ROLESv1.ROLES_RequireRole.selector, - "emergency_shutdown" + vm.expectRevert( + abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, ROLE_EMERGENCY_SHUTDOWN) ); - vm.expectRevert(err); vm.prank(admin); utils.deactivate(); } - function test_deactivate_asAdmin_alreadyDeactivated() + function test_deactivate_asEmergency() + public + givenAdminHasRole + givenEmergencyHasRole + givenPolicyActive + { + vm.prank(emergency); + utils.deactivate(); + + assertFalse(utils.consolidatorActive(), "consolidator active"); + } + + function test_deactivate_asEmergency_alreadyDeactivated() public - givenAdminHasEmergencyRole + givenAdminHasRole + givenEmergencyHasRole + givenPolicyActive givenDeactivated { - vm.prank(owner); + vm.prank(emergency); utils.deactivate(); assertFalse(utils.consolidatorActive(), "consolidator active"); From 58cebc218621e7fc8c1d908be5545392b2ce1dbe Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 9 Oct 2024 16:11:59 +0400 Subject: [PATCH 028/160] Fix regex for fork tests script --- package.json | 4 ++-- src/test/policies/LoanConsolidator.t.sol | 15 +++++---------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 8f8a4614d..e32295d3e 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ "lint": "pnpm run prettier && pnpm run solhint", "lint:check": "pnpm run prettier:check && pnpm run solhint:check", "test": "./shell/test_all.sh", - "test:unit": "forge test --no-match-contract '(Fork|OCGProposal|CoolerUtils)' -vvv", - "test:fork": "forge test --match-contract '($1.*Fork$|CoolerUtils)' --fork-url $FORK_TEST_RPC_URL -vvv", + "test:unit": "forge test --no-match-contract '(Fork|OCGProposal)' -vvv", + "test:fork": "forge test --match-contract $1.*Fork$ --fork-url $FORK_TEST_RPC_URL -vvv", "test:proposal": "forge test --match-contract OCGProposal --fork-url $FORK_TEST_RPC_URL -vvv", "test:crosschainfork": "forge test --match-contract CrossChainBridgeFork -vvv", "test:coverage": "./shell/test_coverage.sh", diff --git a/src/test/policies/LoanConsolidator.t.sol b/src/test/policies/LoanConsolidator.t.sol index e05f77f14..360dbbab8 100644 --- a/src/test/policies/LoanConsolidator.t.sol +++ b/src/test/policies/LoanConsolidator.t.sol @@ -21,7 +21,7 @@ import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; -contract LoanConsolidatorTest is Test { +contract LoanConsolidatorForkTest is Test { LoanConsolidator public utils; ERC20 public ohm; @@ -87,15 +87,10 @@ contract LoanConsolidatorTest is Test { // Determine the kernel executor kernelExecutor = Kernel(kernel).executor(); - // Install EXREG if not already installed - address exregAddress = address(kernel.getModuleForKeycode(toKeycode("EXREG"))); - if (exregAddress == address(0)) { - EXREG = new OlympusExternalRegistry(address(kernel)); - vm.prank(kernelExecutor); - kernel.executeAction(Actions.InstallModule, address(EXREG)); - } else { - EXREG = OlympusExternalRegistry(exregAddress); - } + // Install EXREG (since block is pinned, it won't be installed) + EXREG = new OlympusExternalRegistry(address(kernel)); + vm.prank(kernelExecutor); + kernel.executeAction(Actions.InstallModule, address(EXREG)); // Set up and install the external registry admin policy exregAdmin = new ExternalRegistryAdmin(address(kernel)); From dc5ff35b8e9bd5344076a567f7d51708502b43c5 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 10 Oct 2024 12:22:14 +0400 Subject: [PATCH 029/160] Add NatSpec on initialisation of state variables --- src/policies/LoanConsolidator.sol | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index 05dee2722..bc7cf570c 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -91,24 +91,31 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent // ========= STATE ========= // /// @notice The Clearinghouse registry module + /// @dev The value is set when the policy is activated CHREGv1 internal CHREG; /// @notice The treasury module + /// @dev The value is set when the policy is activated TRSRYv1 internal TRSRY; /// @notice The external contract registry module + /// @dev The value is set when the policy is activated EXREGv1 internal EXREG; /// @notice The DAI token + /// @dev The value is set when the policy is activated IERC20 internal DAI; /// @notice The sDAI token + /// @dev The value is set when the policy is activated IERC4626 internal SDAI; /// @notice The gOHM token + /// @dev The value is set when the policy is activated IERC20 internal GOHM; - /// @notice The flash loan provider + /// @notice The ERC3156 flash loan provider + /// @dev The value is set when the policy is activated IERC3156FlashLender internal FLASH; /// @notice The denominator for percentage calculations From a1b099ce85e533b255360af6ba27c80b41a27432 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 10 Oct 2024 12:22:33 +0400 Subject: [PATCH 030/160] Rename _EXREG state variable to EXREG for consistency --- src/policies/ExternalRegistryAdmin.sol | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/policies/ExternalRegistryAdmin.sol b/src/policies/ExternalRegistryAdmin.sol index a2affbfed..0cb8168ef 100644 --- a/src/policies/ExternalRegistryAdmin.sol +++ b/src/policies/ExternalRegistryAdmin.sol @@ -22,7 +22,8 @@ contract ExternalRegistryAdmin is Policy, RolesConsumer { // ============ STATE ============ // /// @notice The EXREG module - EXREGv1 internal _EXREG; + /// @dev The value is set when the policy is activated + EXREGv1 internal EXREG; /// @notice The role for the external registry admin bytes32 public constant EXTERNAL_REGISTRY_ADMIN_ROLE = "external_registry_admin"; @@ -42,12 +43,12 @@ contract ExternalRegistryAdmin is Policy, RolesConsumer { dependencies[0] = toKeycode("EXREG"); dependencies[1] = toKeycode("ROLES"); - _EXREG = EXREGv1(getModuleAddress(dependencies[0])); + EXREG = EXREGv1(getModuleAddress(dependencies[0])); ROLES = ROLESv1(getModuleAddress(dependencies[1])); // Verify the supported version bytes memory expected = abi.encode([1, 1]); - (uint8 EXREG_MAJOR, ) = _EXREG.VERSION(); + (uint8 EXREG_MAJOR, ) = EXREG.VERSION(); (uint8 ROLES_MAJOR, ) = ROLES.VERSION(); if (EXREG_MAJOR != 1 || ROLES_MAJOR != 1) revert Policy_WrongModuleVersion(expected); @@ -97,7 +98,7 @@ contract ExternalRegistryAdmin is Policy, RolesConsumer { bytes5 name_, address contractAddress_ ) external onlyPolicyActive onlyRole(EXTERNAL_REGISTRY_ADMIN_ROLE) { - _EXREG.registerContract(name_, contractAddress_); + EXREG.registerContract(name_, contractAddress_); } /// @notice Deregister a contract in the external registry @@ -110,7 +111,7 @@ contract ExternalRegistryAdmin is Policy, RolesConsumer { function deregisterContract( bytes5 name_ ) external onlyPolicyActive onlyRole(EXTERNAL_REGISTRY_ADMIN_ROLE) { - _EXREG.deregisterContract(name_); + EXREG.deregisterContract(name_); } // ============ VIEW FUNCTIONS ============ // @@ -122,7 +123,7 @@ contract ExternalRegistryAdmin is Policy, RolesConsumer { /// /// @return The address of the contract function getContract(bytes5 name_) external view onlyPolicyActive returns (address) { - return _EXREG.getContract(name_); + return EXREG.getContract(name_); } /// @notice Get the names of the contracts @@ -132,6 +133,6 @@ contract ExternalRegistryAdmin is Policy, RolesConsumer { /// /// @return The names of the contracts function getContractNames() external view onlyPolicyActive returns (bytes5[] memory) { - return _EXREG.getContractNames(); + return EXREG.getContractNames(); } } From 0b56b41118f8032b80520b3d2b682f8dbcff1a3e Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 10 Oct 2024 12:27:24 +0400 Subject: [PATCH 031/160] Remove view functions from ExternalRegistryAdmin --- src/policies/ExternalRegistryAdmin.sol | 24 +----- src/test/policies/ExternalRegistryAdmin.t.sol | 80 ------------------- 2 files changed, 2 insertions(+), 102 deletions(-) diff --git a/src/policies/ExternalRegistryAdmin.sol b/src/policies/ExternalRegistryAdmin.sol index 0cb8168ef..17de2bff6 100644 --- a/src/policies/ExternalRegistryAdmin.sol +++ b/src/policies/ExternalRegistryAdmin.sol @@ -10,6 +10,8 @@ import {Kernel, Policy, Keycode, toKeycode, Permissions} from "src/Kernel.sol"; /// @notice This policy is used to register and deregister contracts in the EXREG module. /// @dev This contract utilises the following roles: /// - `external_registry_admin`: Can register and deregister contracts +/// +/// This policy provides permissioned access to the state-changing functions on the EXREG module. The view functions can be called directly on the module. contract ExternalRegistryAdmin is Policy, RolesConsumer { // ============ ERRORS ============ // @@ -113,26 +115,4 @@ contract ExternalRegistryAdmin is Policy, RolesConsumer { ) external onlyPolicyActive onlyRole(EXTERNAL_REGISTRY_ADMIN_ROLE) { EXREG.deregisterContract(name_); } - - // ============ VIEW FUNCTIONS ============ // - - /// @notice Get the address of the contract - /// @dev This function will revert if: - /// - This contract is not activated as a policy - /// - The EXREG module reverts - /// - /// @return The address of the contract - function getContract(bytes5 name_) external view onlyPolicyActive returns (address) { - return EXREG.getContract(name_); - } - - /// @notice Get the names of the contracts - /// @dev This function will revert if: - /// - This contract is not activated as a policy - /// - The EXREG module reverts - /// - /// @return The names of the contracts - function getContractNames() external view onlyPolicyActive returns (bytes5[] memory) { - return EXREG.getContractNames(); - } } diff --git a/src/test/policies/ExternalRegistryAdmin.t.sol b/src/test/policies/ExternalRegistryAdmin.t.sol index e7b234998..adce0be9a 100644 --- a/src/test/policies/ExternalRegistryAdmin.t.sol +++ b/src/test/policies/ExternalRegistryAdmin.t.sol @@ -133,84 +133,4 @@ contract ExternalRegistryAdminTest is Test { vm.expectRevert(EXREGv1.Params_InvalidName.selector); EXREG.getContract("ohm"); } - - // getContract - // when the policy is not active - // [X] it reverts - // when the caller is not the admin - // [X] it returns the contract address - // [X] it returns the contract address - - function test_getContract_policyNotActive_reverts() public { - vm.expectRevert(abi.encodeWithSelector(ExternalRegistryAdmin.OnlyPolicyActive.selector)); - - exRegAdmin.getContract("ohm"); - } - - function test_getContract_notAdmin() - public - givenPolicyIsActivated - givenAdminHasRole - givenContractIsRegistered - { - vm.prank(notAdmin); - address contractAddress = exRegAdmin.getContract("ohm"); - - // Assert values - assertEq(contractAddress, ohm, "contract address"); - } - - function test_getContract() - public - givenPolicyIsActivated - givenAdminHasRole - givenContractIsRegistered - { - vm.prank(admin); - address contractAddress = exRegAdmin.getContract("ohm"); - - // Assert values - assertEq(contractAddress, ohm, "contract address"); - assertEq(EXREG.getContract("ohm"), ohm, "EXREG: contract address"); - } - - // getContractNames - // when the policy is not active - // [X] it reverts - // when the caller is not the admin - // [X] it returns the contract names - // [X] it returns the contract names - - function test_getContractNames_policyNotActive_reverts() public { - vm.expectRevert(abi.encodeWithSelector(ExternalRegistryAdmin.OnlyPolicyActive.selector)); - - exRegAdmin.getContractNames(); - } - - function test_getContractNames_notAdmin() - public - givenPolicyIsActivated - givenAdminHasRole - givenContractIsRegistered - { - vm.prank(notAdmin); - bytes5[] memory contractNames = exRegAdmin.getContractNames(); - - // Assert values - assertEq(contractNames.length, 1, "contract names length"); - assertEq(contractNames[0], "ohm", "contract name"); - } - - function test_getContractNames() - public - givenPolicyIsActivated - givenAdminHasRole - givenContractIsRegistered - { - bytes5[] memory contractNames = exRegAdmin.getContractNames(); - - // Assert values - assertEq(contractNames.length, 1, "contract names length"); - assertEq(contractNames[0], "ohm", "contract name"); - } } From e14996609a581ec87eeb01854e775185cf0f5d37 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 10 Oct 2024 13:10:08 +0400 Subject: [PATCH 032/160] Update EXREG to have distinct registerContract and updateContract functions. Also updates ExternalRegistryAdmin. --- src/modules/EXREG/EXREG.v1.sol | 29 ++- src/modules/EXREG/OlympusExternalRegistry.sol | 35 ++- src/policies/ExternalRegistryAdmin.sol | 20 +- src/test/modules/EXREG.t.sol | 200 ++++++++++++------ src/test/policies/ExternalRegistryAdmin.t.sol | 40 +++- 5 files changed, 252 insertions(+), 72 deletions(-) diff --git a/src/modules/EXREG/EXREG.v1.sol b/src/modules/EXREG/EXREG.v1.sol index b260bb370..c32700266 100644 --- a/src/modules/EXREG/EXREG.v1.sol +++ b/src/modules/EXREG/EXREG.v1.sol @@ -11,17 +11,26 @@ abstract contract EXREGv1 is Module { /// @notice Emitted when a contract is registered or updated event ContractRegistered(bytes5 indexed name, address indexed contractAddress); + /// @notice Emitted when a contract is updated + event ContractUpdated(bytes5 indexed name, address indexed contractAddress); + /// @notice Emitted when a contract is deregistered event ContractDeregistered(bytes5 indexed name); // ========= ERRORS ========= // - /// @notice Thrown when an invalid name is provided + /// @notice The provided name is invalid error Params_InvalidName(); - /// @notice Thrown when an invalid address is provided + /// @notice The provided address is invalid error Params_InvalidAddress(); + /// @notice The provided contract name is already registered + error Params_ContractAlreadyRegistered(); + + /// @notice The provided contract name is not registered + error Params_ContractNotRegistered(); + // ========= STATE ========= // /// @notice Stores the names of the registered contracts @@ -33,14 +42,22 @@ abstract contract EXREGv1 is Module { // ========= REGISTRATION FUNCTIONS ========= // - /// @notice Function to register or update a contract + /// @notice Register a new contract name and address /// @dev This function should be permissioned to prevent arbitrary contracts from being registered. + /// /// @param name_ The name of the contract /// @param contractAddress_ The address of the contract function registerContract(bytes5 name_, address contractAddress_) external virtual; - /// @notice Function to deregister a contract + /// @notice Update the address of an existing contract name + /// @dev This function should be permissioned to prevent arbitrary contracts from being updated. + /// + /// @param name_ The name of the contract + /// @param contractAddress_ The address of the contract + function updateContract(bytes5 name_, address contractAddress_) external virtual; + + /// @notice Deregister an existing contract name /// @dev This function should be permissioned to prevent arbitrary contracts from being deregistered. /// /// @param name_ The name of the contract @@ -48,13 +65,13 @@ abstract contract EXREGv1 is Module { // ========= VIEW FUNCTIONS ========= // - /// @notice Function to get the address of a contract + /// @notice Get the address of a registered contract /// /// @param name_ The name of the contract /// @return The address of the contract function getContract(bytes5 name_) external view virtual returns (address); - /// @notice Function to get the names of all registered contracts + /// @notice Get the names of all registered contracts /// /// @return The names of all registered contracts function getContractNames() external view virtual returns (bytes5[] memory); diff --git a/src/modules/EXREG/OlympusExternalRegistry.sol b/src/modules/EXREG/OlympusExternalRegistry.sol index 442933f6c..f8e23d6a6 100644 --- a/src/modules/EXREG/OlympusExternalRegistry.sol +++ b/src/modules/EXREG/OlympusExternalRegistry.sol @@ -54,11 +54,18 @@ contract OlympusExternalRegistry is EXREGv1 { /// - The name is empty /// - The name contains punctuation or uppercase letters /// - The contract address is zero + /// - The contract name is already registered function registerContract( bytes5 name_, address contractAddress_ ) external override permissioned { + // Check that the name is not empty if (name_ == bytes5(0)) revert Params_InvalidName(); + + // Check that the contract has not already been registered + if (_contracts[name_] != address(0)) revert Params_ContractAlreadyRegistered(); + + // Check that the contract address is not zero if (contractAddress_ == address(0)) revert Params_InvalidAddress(); // Check that the contract name is lowercase letters and numerals only @@ -86,6 +93,30 @@ contract OlympusExternalRegistry is EXREGv1 { emit ContractRegistered(name_, contractAddress_); } + /// @inheritdoc EXREGv1 + /// @dev This function performs the following steps: + /// - Validates the parameters + /// - Updates the contract address + /// - Updates the contract names (if needed) + /// - Refreshes the dependent policies + /// + /// This function will revert if: + /// - The caller is not permissioned + /// - The name is not registered + /// - The contract address is zero + function updateContract(bytes5 name_, address contractAddress_) external override permissioned { + // Check that the contract address is not zero + if (contractAddress_ == address(0)) revert Params_InvalidAddress(); + + // Check that the contract name is registered + if (_contracts[name_] == address(0)) revert Params_ContractNotRegistered(); + + _contracts[name_] = contractAddress_; + _refreshDependents(); + + emit ContractUpdated(name_, contractAddress_); + } + /// @inheritdoc EXREGv1 /// @dev This function performs the following steps: /// - Validates the parameters @@ -98,7 +129,7 @@ contract OlympusExternalRegistry is EXREGv1 { /// - The contract is not registered function deregisterContract(bytes5 name_) external override permissioned { address contractAddress = _contracts[name_]; - if (contractAddress == address(0)) revert Params_InvalidName(); + if (contractAddress == address(0)) revert Params_ContractNotRegistered(); delete _contracts[name_]; _removeContractName(name_); @@ -115,7 +146,7 @@ contract OlympusExternalRegistry is EXREGv1 { function getContract(bytes5 name_) external view override returns (address) { address contractAddress = _contracts[name_]; - if (contractAddress == address(0)) revert Params_InvalidName(); + if (contractAddress == address(0)) revert Params_ContractNotRegistered(); return contractAddress; } diff --git a/src/policies/ExternalRegistryAdmin.sol b/src/policies/ExternalRegistryAdmin.sol index 17de2bff6..3a151f183 100644 --- a/src/policies/ExternalRegistryAdmin.sol +++ b/src/policies/ExternalRegistryAdmin.sol @@ -66,9 +66,10 @@ contract ExternalRegistryAdmin is Policy, RolesConsumer { { Keycode exregKeycode = toKeycode("EXREG"); - permissions = new Permissions[](2); + permissions = new Permissions[](3); permissions[0] = Permissions(exregKeycode, EXREGv1.registerContract.selector); - permissions[1] = Permissions(exregKeycode, EXREGv1.deregisterContract.selector); + permissions[1] = Permissions(exregKeycode, EXREGv1.updateContract.selector); + permissions[2] = Permissions(exregKeycode, EXREGv1.deregisterContract.selector); return permissions; } @@ -103,6 +104,21 @@ contract ExternalRegistryAdmin is Policy, RolesConsumer { EXREG.registerContract(name_, contractAddress_); } + /// @notice Update a contract in the external registry + /// @dev This function will revert if: + /// - This contract is not activated as a policy + /// - The caller does not have the required role + /// - The EXREG module reverts + /// + /// @param name_ The name of the contract + /// @param contractAddress_ The address of the contract + function updateContract( + bytes5 name_, + address contractAddress_ + ) external onlyPolicyActive onlyRole(EXTERNAL_REGISTRY_ADMIN_ROLE) { + EXREG.updateContract(name_, contractAddress_); + } + /// @notice Deregister a contract in the external registry /// @dev This function will revert if: /// - This contract is not activated as a policy diff --git a/src/test/modules/EXREG.t.sol b/src/test/modules/EXREG.t.sol index 816a7125a..98dbaf962 100644 --- a/src/test/modules/EXREG.t.sol +++ b/src/test/modules/EXREG.t.sol @@ -25,6 +25,7 @@ contract ExternalRegistryTest is Test { // External Registry Expected events event ContractRegistered(bytes5 indexed name, address indexed contractAddress); + event ContractUpdated(bytes5 indexed name, address indexed contractAddress); event ContractDeregistered(bytes5 indexed name); function setUp() public { @@ -53,6 +54,11 @@ contract ExternalRegistryTest is Test { _exreg.deregisterContract(name_); } + function _updateContract(bytes5 name_, address contractAddress_) internal { + vm.prank(godmode); + _exreg.updateContract(name_, contractAddress_); + } + function _activatePolicyOne() internal { _kernel.executeAction(Actions.ActivatePolicy, address(_policy)); } @@ -61,6 +67,31 @@ contract ExternalRegistryTest is Test { _kernel.executeAction(Actions.ActivatePolicy, address(_policy2)); } + modifier givenContractIsRegistered(bytes5 name_, address contractAddress_) { + _registerContract(name_, contractAddress_); + _; + } + + modifier givenContractIsDeregistered(bytes5 name_) { + _deregisterContract(name_); + _; + } + + modifier givenContractIsUpdated(bytes5 name_, address contractAddress_) { + _updateContract(name_, contractAddress_); + _; + } + + modifier givenPolicyOneIsActive() { + _activatePolicyOne(); + _; + } + + modifier givenPolicyTwoIsActive() { + _activatePolicyTwo(); + _; + } + // ========= TESTS ========= // // constructor @@ -95,7 +126,7 @@ contract ExternalRegistryTest is Test { // when the name contains a numeral // [X] it succeeds // given the name is registered - // [X] it updates the contract address and emits an event, but does not update the names array + // [X] it reverts // given the name is not registered // given there are existing registrations // [X] it updates the contract address, emits an event and updates the names array @@ -103,7 +134,7 @@ contract ExternalRegistryTest is Test { // given dependent policies are registered // [X] it refreshes the dependents - function test_registerContract_whenCallerIsNotPermissioned_reverts() public { + function test_registerContract_callerNotPermissioned_reverts() public { vm.expectRevert( abi.encodeWithSelector(Module.Module_PolicyNotPermitted.selector, notOwner) ); @@ -158,29 +189,15 @@ contract ExternalRegistryTest is Test { _registerContract(bytes5("ohm"), address(0)); } - function test_registerContract_whenNameIsRegistered() public { - // Register the first time - _registerContract(bytes5("ohm"), addressOne); - - // Expect an event to be emitted for updated registration - vm.expectEmit(); - emit ContractRegistered(bytes5("ohm"), addressTwo); + function test_registerContract_whenNameIsRegistered_reverts() + public + givenContractIsRegistered(bytes5("ohm"), addressOne) + { + // Expect revert + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractAlreadyRegistered.selector)); // Register the second time _registerContract(bytes5("ohm"), addressTwo); - - // Assert values - assertEq( - _exreg.getContract(bytes5("ohm")), - addressTwo, - "Contract address is not set correctly" - ); - assertEq(_exreg.getContractNames().length, 1, "Names array is not updated correctly"); - assertEq( - _exreg.getContractNames()[0], - bytes5("ohm"), - "Names array is not updated correctly" - ); } function test_registerContract_whenNameIsNotRegistered() public { @@ -204,16 +221,12 @@ contract ExternalRegistryTest is Test { ); } - function test_registerContract_whenOtherNamesAreRegistered() public { - // Register the first time - _registerContract(bytes5("ohm"), addressOne); - - // Register the second time - _registerContract(bytes5("ohm2"), addressTwo); - - // Register the third time - _registerContract(bytes5("ohm3"), address(0x4)); - + function test_registerContract_whenOtherNamesAreRegistered() + public + givenContractIsRegistered(bytes5("ohm"), addressOne) + givenContractIsRegistered(bytes5("ohm2"), addressTwo) + givenContractIsRegistered(bytes5("ohm3"), address(0x4)) + { // Assert values assertEq( _exreg.getContract(bytes5("ohm")), @@ -264,13 +277,78 @@ contract ExternalRegistryTest is Test { } function test_activatePolicies_whenContractNotRegistered_reverts() public { - // Expect it to revert - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + // Expect the policy to revert + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); // Activate the dependent policies _activatePolicyOne(); } + // updateContract + // when the caller is not permissioned + // [X] it reverts + // when the name is not registered + // [X] it reverts + // when the address is zero + // [X] it reverts + // given dependent policies are registered + // [X] it refreshes the dependents + // [X] it updates the contract address + + function test_updateContract_callerNotPermissioned_reverts() public { + vm.expectRevert( + abi.encodeWithSelector(Module.Module_PolicyNotPermitted.selector, notOwner) + ); + + vm.prank(notOwner); + _exreg.updateContract(bytes5("ohm"), addressOne); + } + + function test_updateContract_whenNameIsNotRegistered_reverts() public { + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); + + _updateContract(bytes5("ohm"), addressOne); + } + + function test_updateContract_whenContractAddressIsZero_reverts() + public + givenContractIsRegistered(bytes5("ohm"), addressOne) + { + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidAddress.selector)); + + _updateContract(bytes5("ohm"), address(0)); + } + + function test_updateContract() public givenContractIsRegistered(bytes5("ohm"), addressOne) { + // Expect an event to be emitted + vm.expectEmit(); + emit ContractUpdated(bytes5("ohm"), addressTwo); + + // Update the contract + _updateContract(bytes5("ohm"), addressTwo); + + // Assert values + assertEq( + _exreg.getContract(bytes5("ohm")), + addressTwo, + "Contract address is not updated correctly" + ); + } + + function test_updateContract_whenDependentPoliciesAreRegistered() + public + givenContractIsRegistered(bytes5("dai"), addressOne) + givenPolicyOneIsActive + givenPolicyTwoIsActive + { + // Update the contract + _updateContract(bytes5("dai"), addressTwo); + + // Assert values in the policies have been updated + assertEq(_policy.dai(), addressTwo); + assertEq(_policy2.dai(), addressTwo); + } + // deregisterContract // when the caller is not permissioned // [X] it reverts @@ -298,7 +376,7 @@ contract ExternalRegistryTest is Test { } function test_deregisterContract_whenNameIsNotRegistered_reverts() public { - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); _deregisterContract(bytes5("")); } @@ -312,7 +390,7 @@ contract ExternalRegistryTest is Test { // Assert values // Deregistered contract should revert - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); _exreg.getContract(bytes5("ohm")); // Names array should be empty @@ -341,7 +419,7 @@ contract ExternalRegistryTest is Test { // Assert values // Deregistered contract should revert - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); _exreg.getContract(names[randomIndex]); // Other contracts should still be registered @@ -394,25 +472,23 @@ contract ExternalRegistryTest is Test { } } - function test_deregisterContract_whenDependentPoliciesAreRegistered_reverts() public { - // Register the contract - _registerContract(bytes5("dai"), addressOne); - - // Activate the dependent policies - _activatePolicyOne(); - _activatePolicyTwo(); - - // Expect it to revert - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + function test_deregisterContract_whenDependentPoliciesAreRegistered_reverts() + public + givenContractIsRegistered(bytes5("dai"), addressOne) + givenPolicyOneIsActive + givenPolicyTwoIsActive + { + // Expect the policies to revert + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); // Deregister the contract _deregisterContract(bytes5("dai")); } - function test_deregisterContract_whenDependentPoliciesAreNotRegistered() public { - // Register the contract - _registerContract(bytes5("ohm"), addressOne); - + function test_deregisterContract_whenDependentPoliciesAreNotRegistered() + public + givenContractIsRegistered(bytes5("ohm"), addressOne) + { // Deregister the contract _deregisterContract(bytes5("ohm")); @@ -430,7 +506,7 @@ contract ExternalRegistryTest is Test { // [X] it returns the contract address function test_getContract_whenNameIsNotRegistered_reverts() public { - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); _exreg.getContract(bytes5("ohm")); } @@ -445,10 +521,11 @@ contract ExternalRegistryTest is Test { ); } - function test_getContract_whenNameIsUpdated() public { - _registerContract(bytes5("ohm"), addressOne); - _registerContract(bytes5("ohm"), addressTwo); - + function test_getContract_whenNameIsUpdated() + public + givenContractIsRegistered(bytes5("ohm"), addressOne) + givenContractIsUpdated(bytes5("ohm"), addressTwo) + { assertEq( _exreg.getContract(bytes5("ohm")), addressTwo, @@ -466,11 +543,12 @@ contract ExternalRegistryTest is Test { assertEq(_exreg.getContractNames().length, 0, "Names array is not empty"); } - function test_getContractNames_whenNamesAreRegistered() public { - _registerContract(bytes5("ohm"), addressOne); - _registerContract(bytes5("ohm2"), addressTwo); - _registerContract(bytes5("ohm3"), address(0x4)); - + function test_getContractNames_whenNamesAreRegistered() + public + givenContractIsRegistered(bytes5("ohm"), addressOne) + givenContractIsRegistered(bytes5("ohm2"), addressTwo) + givenContractIsRegistered(bytes5("ohm3"), address(0x4)) + { assertEq(_exreg.getContractNames().length, 3, "Names array is not updated correctly"); assertEq( _exreg.getContractNames()[0], diff --git a/src/test/policies/ExternalRegistryAdmin.t.sol b/src/test/policies/ExternalRegistryAdmin.t.sol index adce0be9a..644c233f3 100644 --- a/src/test/policies/ExternalRegistryAdmin.t.sol +++ b/src/test/policies/ExternalRegistryAdmin.t.sol @@ -94,6 +94,44 @@ contract ExternalRegistryAdminTest is Test { assertEq(EXREG.getContract("ohm"), ohm, "contract address"); } + // updateContract + // when the policy is not active + // [X] it reverts + // when the caller does not have the role + // [X] it reverts + // [X] it updates the contract + + function test_updateContract_policyNotActive_reverts() public { + vm.expectRevert(abi.encodeWithSelector(ExternalRegistryAdmin.OnlyPolicyActive.selector)); + + exRegAdmin.updateContract("ohm", ohm); + } + + function test_updateContract_callerDoesNotHaveRole_reverts() + public + givenPolicyIsActivated + givenAdminHasRole + { + vm.expectRevert(abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, EXREG_ROLE)); + + vm.prank(notAdmin); + exRegAdmin.updateContract("ohm", ohm); + } + + function test_updateContract() + public + givenPolicyIsActivated + givenAdminHasRole + givenContractIsRegistered + { + // Update the contract + vm.prank(admin); + exRegAdmin.updateContract("ohm", address(0x4)); + + // Assert values + assertEq(EXREG.getContract("ohm"), address(0x4), "contract address"); + } + // deregisterContract // when the policy is not active // [X] it reverts @@ -130,7 +168,7 @@ contract ExternalRegistryAdminTest is Test { exRegAdmin.deregisterContract("ohm"); // Assert values - vm.expectRevert(EXREGv1.Params_InvalidName.selector); + vm.expectRevert(EXREGv1.Params_ContractNotRegistered.selector); EXREG.getContract("ohm"); } } From d4d498b67b972b0fc645601b1f60af4d315c5546 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 10 Oct 2024 13:22:06 +0400 Subject: [PATCH 033/160] Extra logs in deploy script --- shell/deploy.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/shell/deploy.sh b/shell/deploy.sh index 2fa017e32..66c43a811 100755 --- a/shell/deploy.sh +++ b/shell/deploy.sh @@ -40,6 +40,8 @@ BROADCAST_FLAG="" if [ "$BROADCAST" = "true" ] || [ "$BROADCAST" = "TRUE" ]; then BROADCAST_FLAG="--broadcast" echo "Broadcasting is enabled" +else + echo "Broadcasting is disabled" fi # Set VERIFY_FLAG based on VERIFY @@ -62,6 +64,8 @@ if [ "$VERIFY" = "true" ] || [ "$VERIFY" = "TRUE" ]; then fi echo "Verification is enabled" +else + echo "Verification is disabled" fi # Set RESUME_FLAG based on RESUME From fc841ddda80370add306dd8530f0b8586e9bd9ce Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 10 Oct 2024 13:22:19 +0400 Subject: [PATCH 034/160] Add EXREG and ExternalRegistryAdmin to deploy script --- src/scripts/deploy/DeployV2.sol | 47 ++++++++++++++++++- .../savedDeployments/external_registry.json | 12 +++++ src/scripts/env.json | 6 ++- 3 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 src/scripts/deploy/savedDeployments/external_registry.json diff --git a/src/scripts/deploy/DeployV2.sol b/src/scripts/deploy/DeployV2.sol index 4db8d32d1..3b2eb8e95 100644 --- a/src/scripts/deploy/DeployV2.sol +++ b/src/scripts/deploy/DeployV2.sol @@ -61,6 +61,8 @@ import {pOLY} from "policies/pOLY.sol"; import {ClaimTransfer} from "src/external/ClaimTransfer.sol"; import {Clearinghouse} from "policies/Clearinghouse.sol"; import {YieldRepurchaseFacility} from "policies/YieldRepurchaseFacility.sol"; +import {OlympusExternalRegistry} from "modules/EXREG/OlympusExternalRegistry.sol"; +import {ExternalRegistryAdmin} from "policies/ExternalRegistryAdmin.sol"; import {MockPriceFeed} from "test/mocks/MockPriceFeed.sol"; import {MockAuraBooster, MockAuraRewardPool, MockAuraMiningLib, MockAuraVirtualRewardPool, MockAuraStashToken} from "test/mocks/AuraMocks.sol"; @@ -87,6 +89,7 @@ contract OlympusDeploy is Script { OlympusRoles public ROLES; OlympusBoostedLiquidityRegistry public BLREG; OlympusClearinghouseRegistry public CHREG; + OlympusExternalRegistry public EXREG; /// Policies Operator public operator; @@ -106,6 +109,8 @@ contract OlympusDeploy is Script { BLVaultLusd public lusdVault; CrossChainBridge public bridge; LegacyBurner public legacyBurner; + ExternalRegistryAdmin public externalRegistryAdmin; + LoanConsolidator public loanConsolidator; /// Other Olympus contracts OlympusAuthority public burnerReplacementAuthority; @@ -114,7 +119,6 @@ contract OlympusDeploy is Script { address public inverseBondDepository; pOLY public poly; Clearinghouse public clearinghouse; - LoanConsolidator public loanConsolidator; YieldRepurchaseFacility public yieldRepo; // Governance @@ -182,6 +186,7 @@ contract OlympusDeploy is Script { chain = chain_; // Setup contract -> selector mappings + // Modules selectorMap["OlympusPrice"] = this._deployPrice.selector; selectorMap["OlympusRange"] = this._deployRange.selector; selectorMap["OlympusTreasury"] = this._deployTreasury.selector; @@ -191,6 +196,8 @@ contract OlympusDeploy is Script { ._deployBoostedLiquidityRegistry .selector; selectorMap["OlympusClearinghouseRegistry"] = this._deployClearinghouseRegistry.selector; + selectorMap["OlympusExternalRegistry"] = this._deployExternalRegistry.selector; + // Policies selectorMap["Operator"] = this._deployOperator.selector; selectorMap["OlympusHeart"] = this._deployHeart.selector; selectorMap["BondCallback"] = this._deployBondCallback.selector; @@ -214,6 +221,7 @@ contract OlympusDeploy is Script { selectorMap["Clearinghouse"] = this._deployClearinghouse.selector; selectorMap["LoanConsolidator"] = this._deployLoanConsolidator.selector; selectorMap["YieldRepurchaseFacility"] = this._deployYieldRepurchaseFacility.selector; + selectorMap["ExternalRegistryAdmin"] = this._deployExternalRegistryAdmin.selector; // Governance selectorMap["Timelock"] = this._deployTimelock.selector; @@ -269,6 +277,7 @@ contract OlympusDeploy is Script { // Bophades contracts kernel = Kernel(envAddress("olympus.Kernel")); + // Modules PRICE = OlympusPrice(envAddress("olympus.modules.OlympusPriceV2")); RANGE = OlympusRange(envAddress("olympus.modules.OlympusRangeV2")); TRSRY = OlympusTreasury(envAddress("olympus.modules.OlympusTreasury")); @@ -278,6 +287,8 @@ contract OlympusDeploy is Script { BLREG = OlympusBoostedLiquidityRegistry( envAddress("olympus.modules.OlympusBoostedLiquidityRegistry") ); + EXREG = OlympusExternalRegistry(envAddress("olympus.modules.OlympusExternalRegistry")); + // Policies operator = Operator(envAddress("olympus.policies.Operator")); heart = OlympusHeart(envAddress("olympus.policies.OlympusHeart")); callback = BondCallback(envAddress("olympus.policies.BondCallback")); @@ -299,6 +310,10 @@ contract OlympusDeploy is Script { claimTransfer = ClaimTransfer(envAddress("olympus.claim.ClaimTransfer")); clearinghouse = Clearinghouse(envAddress("olympus.policies.Clearinghouse")); yieldRepo = YieldRepurchaseFacility(envAddress("olympus.policies.YieldRepurchaseFacility")); + externalRegistryAdmin = ExternalRegistryAdmin( + envAddress("olympus.policies.ExternalRegistryAdmin") + ); + loanConsolidator = LoanConsolidator(envAddress("olympus.policies.LoanConsolidator")); // Governance timelock = Timelock(payable(envAddress("olympus.governance.Timelock"))); @@ -1027,6 +1042,36 @@ contract OlympusDeploy is Script { return address(CHREG); } + function _deployExternalRegistry(bytes calldata) public returns (address) { + // Decode arguments from the sequence file + // None + + // Print the arguments + console2.log(" Kernel:", address(kernel)); + + // Deploy OlympusExternalRegistry + vm.broadcast(); + EXREG = new OlympusExternalRegistry(address(kernel)); + console2.log("ExternalRegistry deployed at:", address(EXREG)); + + return address(EXREG); + } + + function _deployExternalRegistryAdmin(bytes calldata) public returns (address) { + // Decode arguments from the sequence file + // None + + // Print the arguments + console2.log(" Kernel:", address(kernel)); + + // Deploy ExternalRegistryAdmin + vm.broadcast(); + externalRegistryAdmin = new ExternalRegistryAdmin(address(kernel)); + console2.log("ExternalRegistryAdmin deployed at:", address(externalRegistryAdmin)); + + return address(externalRegistryAdmin); + } + function _deployLoanConsolidator(bytes calldata args_) public returns (address) { // Decode arguments from the sequence file uint256 feePercentage = abi.decode(args_, (uint256)); diff --git a/src/scripts/deploy/savedDeployments/external_registry.json b/src/scripts/deploy/savedDeployments/external_registry.json new file mode 100644 index 000000000..e4a3328d6 --- /dev/null +++ b/src/scripts/deploy/savedDeployments/external_registry.json @@ -0,0 +1,12 @@ +{ + "sequence": [ + { + "name": "OlympusExternalRegistry", + "args": {} + }, + { + "name": "ExternalRegistryAdmin", + "args": {} + } + ] +} diff --git a/src/scripts/env.json b/src/scripts/env.json index 02003b994..089523500 100644 --- a/src/scripts/env.json +++ b/src/scripts/env.json @@ -63,7 +63,8 @@ "OlympusInstructions": "0x0000000000000000000000000000000000000000", "OlympusVotes": "0x0000000000000000000000000000000000000000", "OlympusBoostedLiquidityRegistry": "0x375E06C694B5E50aF8be8FB03495A612eA3e2275", - "OlympusClearinghouseRegistry": "0x24b96f2150BF1ed10D3e8B28Ed33E392fbB4Cad5" + "OlympusClearinghouseRegistry": "0x24b96f2150BF1ed10D3e8B28Ed33E392fbB4Cad5", + "OlympusExternalRegistry": "0x0000000000000000000000000000000000000000" }, "submodules": { "PRICE": { @@ -95,7 +96,8 @@ "LegacyBurner": "0x367149cf2d04D3114fFD1Cc6b273222664908D0B", "LoanConsolidator": "0xB15bcb1b6593d85890f5287Baa2245B8A29F464a", "pOLY": "0xb37796941cA55b7E4243841930C104Ee325Da5a1", - "YieldRepurchaseFacility": "0x30A967eB957E5B1eE053B75F1A57ea6bfb2e907E" + "YieldRepurchaseFacility": "0x30A967eB957E5B1eE053B75F1A57ea6bfb2e907E", + "ExternalRegistryAdmin": "0x0000000000000000000000000000000000000000" }, "legacy": { "OHM": "0x64aa3364F17a4D01c6f1751Fd97C2BD3D7e7f1D5", From 6cadd5369afbc6eedebfdc3f3fdc7a2c1f73d4b5 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 14 Oct 2024 10:59:51 +0400 Subject: [PATCH 035/160] Cross-port extensions and solidity metrics from PR #203 --- .vscode/extensions.json | 3 +- .vscode/settings.json | 4 +- shell/metrics.js | 134 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 shell/metrics.js diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 9d1cb9ddf..f2a888d2b 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,6 +3,7 @@ "nomicfoundation.hardhat-solidity", "gruntfuggly.todo-tree", "ryanluker.vscode-coverage-gutters", - "github.vscode-github-actions" + "github.vscode-github-actions", + "d-koppenhagen.file-tree-to-text-generator" ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 02f15cf86..5cdfdad41 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,7 @@ "editor.defaultFormatter": "NomicFoundation.hardhat-solidity" }, "solidity.defaultCompiler": "remote", - "solidity.compileUsingRemoteVersion": "v0.8.15+commit.e14f2714" + "solidity.compileUsingRemoteVersion": "v0.8.15+commit.e14f2714", + "solidity-metrics.file.exclusions.glob": "{**/node_modules,**/mock*,**/test*,**/migrations,**/Migrations.sol,lib,**/external,**/libraries,**/interfaces}", + "solidity.monoRepoSupport": false } \ No newline at end of file diff --git a/shell/metrics.js b/shell/metrics.js new file mode 100644 index 000000000..59a05fdb7 --- /dev/null +++ b/shell/metrics.js @@ -0,0 +1,134 @@ +#!/usr/bin/env node +'use strict'; +/** + * @author github.com/tintinweb + * @license MIT + * + * Based on: + * - https://github.com/Consensys/solidity-metrics/blob/master/src/cli.js + * - https://github.com/Consensys/vscode-solidity-metrics/blob/master/src/extension.js + * + */ + +const glob = require("glob"); +const fs = require("fs"); +const { SolidityMetricsContainer } = require("solidity-code-metrics"); +const { exportAsHtml } = require("solidity-code-metrics/src/metrics/helper"); + +const globExclusions = [ + "**/node_modules", + "**/mock*", + "**/test*", + "**/migrations", + "**/Migrations.sol", + "lib", + "**/external", + "**/libraries", + "**/interfaces", +]; + +function getWsGitInfo(rootPath) { + let branch = "unknown_branch"; + let commit = "unknown_commit"; + let remote = ""; + + let basePath = rootPath; + + if (fs.existsSync(basePath + "/.git/HEAD")) { + let branchFile = fs.readFileSync(basePath + "/.git/HEAD").toString('utf-8').trim(); + if (branchFile && branchFile.startsWith("ref: ")) { + branchFile = branchFile.replace("ref: ", ""); + + let branchFileNormalized = path.normalize(basePath + "/.git/" + branchFile); + + if (branchFileNormalized.startsWith(basePath) && fs.existsSync(branchFileNormalized)) { + branch = branchFile.replace("refs/heads/", ""); + commit = fs.readFileSync(branchFileNormalized).toString('utf-8').trim(); + if (fs.existsSync(basePath + "/.git/FETCH_HEAD")) { + let fetchHeadData = fs.readFileSync(basePath + "/.git/FETCH_HEAD").toString('utf-8').trim().split("\n"); + if (fetchHeadData.lenght) { + let fetchHead = fetchHeadData.find(line => line.startsWith(commit)) || fetchHeadData.find(line => line.includes(`branch '${branch}' of `)) || fetchHeadData[0]; + remote = fetchHead.trim().split(/[\s]+/).pop(); + } + } + } + + + } + } + return { + branch: branch, + commit: commit, + remote: remote + }; +} + +function convertGlobExclusions(array) { + let result = "{"; + for (let i = 0; i < array.length; i++) { + result += array[i]; + if (i < array.length - 1) { + result += ","; + } + } + result += "}"; + + return result; +} + +let options = []; + +let outputFile = "solidity-metrics.html"; + +// Get exclusions +process.argv.slice(1,).forEach(f => { + if (f.startsWith("--exclude")) { + let globExclusion = f.split("=")[1]; + console.log("excluding", globExclusion); + globExclusions.push(globExclusion); + } else if (f.startsWith("--")) { + options.push(f); + } +}); + +// Get inclusions +let includedFiles = []; +process.argv.slice(1,).forEach(f => { + if (f.endsWith(".sol") && !f.startsWith("--exclude")) { + console.log("including", f); + glob.sync(f, { ignore: globExclusions }).forEach(f => includedFiles.push(f)); + } +}); + +let metrics = new SolidityMetricsContainer("'CLI'", { + basePath: undefined, + initDoppelGanger: undefined, + inputFileGlobExclusions: convertGlobExclusions(globExclusions), + inputFileGlob: convertGlobExclusions(includedFiles), + inputFileGlobLimit: undefined, + debug: true, + repoInfo: getWsGitInfo("src/"), +}); + +// Analyse +console.log("analysing"); +includedFiles.forEach(f => metrics.analyze(f)); + +// output +//console.log(metrics.totals()); +let dotGraphs = {}; +try { + console.log("generating dot graphs"); + dotGraphs = metrics.getDotGraphs(); +} catch (error) { + console.error(error); +} + +console.log("generating markdown report"); +metrics.generateReportMarkdown().then(md => { + console.log("generating HTML report"); + const htmlOutput = exportAsHtml(md, metrics.totals(), dotGraphs); + + console.log("writing report to", outputFile); + fs.writeFileSync(outputFile, htmlOutput); +}); From 3d4d3940dca2ab3a35e0e1116490ab7cc30de754 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 14 Oct 2024 12:03:09 +0400 Subject: [PATCH 036/160] Cross-port metrics script from PR #203 --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e32295d3e..fb47bb323 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "test:proposal": "forge test --match-contract OCGProposal --fork-url $FORK_TEST_RPC_URL -vvv", "test:crosschainfork": "forge test --match-contract CrossChainBridgeFork -vvv", "test:coverage": "./shell/test_coverage.sh", - "size": "forge build --sizes" + "size": "forge build --sizes", + "metrics": "node shell/metrics.js" }, "devDependencies": { "prettier": "^2.5.1", From 833ef745b4bc83f79ea64771dbe305595676a312 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 14 Oct 2024 12:04:03 +0400 Subject: [PATCH 037/160] Add solidity-code-metrics dependency from PR #203 --- package.json | 3 +- pnpm-lock.yaml | 807 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 555 insertions(+), 255 deletions(-) diff --git a/package.json b/package.json index fb47bb323..97f9d07ce 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "devDependencies": { "prettier": "^2.5.1", "prettier-plugin-solidity": "^1.0.0-beta.19", - "solhint": "^3.3.7" + "solhint": "^3.3.7", + "solidity-code-metrics": "^0.0.25" }, "prettier": { "tabWidth": 4, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 96dd62562..c97b99da4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,401 +1,676 @@ -lockfileVersion: '6.1' +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false -devDependencies: - prettier: - specifier: ^2.5.1 - version: 2.8.8 - prettier-plugin-solidity: - specifier: ^1.0.0-beta.19 - version: 1.1.3(prettier@2.8.8) - solhint: - specifier: ^3.3.7 - version: 3.4.1 +importers: + + .: + devDependencies: + prettier: + specifier: ^2.5.1 + version: 2.8.8 + prettier-plugin-solidity: + specifier: ^1.0.0-beta.19 + version: 1.1.3(prettier@2.8.8) + solhint: + specifier: ^3.3.7 + version: 3.4.1 + solidity-code-metrics: + specifier: ^0.0.25 + version: 0.0.25 packages: - /@babel/code-frame@7.21.4: + '@babel/code-frame@7.21.4': resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.18.6 - dev: true - /@babel/helper-validator-identifier@7.19.1: + '@babel/helper-validator-identifier@7.19.1': resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} - dev: true - /@babel/highlight@7.18.6: + '@babel/highlight@7.18.6': resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.19.1 - chalk: 2.4.2 - js-tokens: 4.0.0 - dev: true - /@solidity-parser/parser@0.16.0: + '@solidity-parser/parser@0.16.0': resolution: {integrity: sha512-ESipEcHyRHg4Np4SqBCfcXwyxxna1DgFVz69bgpLV8vzl/NP1DtcKsJ4dJZXWQhY/Z4J2LeKBiOkOVZn9ct33Q==} - dependencies: - antlr4ts: 0.5.0-alpha.4 - dev: true - /ajv@6.12.6: + '@solidity-parser/parser@0.16.2': + resolution: {integrity: sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==} + + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - dev: true - /ajv@8.12.0: + ajv@8.12.0: resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} - dependencies: - fast-deep-equal: 3.1.3 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - uri-js: 4.4.1 - dev: true - /ansi-regex@5.0.1: + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - dev: true - /ansi-styles@3.2.1: + ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} - dependencies: - color-convert: 1.9.3 - dev: true - /ansi-styles@4.3.0: + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 - dev: true - /antlr4@4.12.0: + antlr4@4.12.0: resolution: {integrity: sha512-23iB5IzXJZRZeK9TigzUyrNc9pSmNqAerJRBcNq1ETrmttMWRgaYZzC561IgEO3ygKsDJTYDTozABXa4b/fTQQ==} engines: {node: '>=16'} - dev: true - /antlr4ts@0.5.0-alpha.4: + antlr4ts@0.5.0-alpha.4: resolution: {integrity: sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==} - dev: true - /argparse@2.0.1: + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true - /ast-parents@0.0.1: + ast-parents@0.0.1: resolution: {integrity: sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA==} - dev: true - /astral-regex@2.0.0: + astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} - dev: true - /balanced-match@1.0.2: + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true - /brace-expansion@2.0.1: + brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - dependencies: - balanced-match: 1.0.2 - dev: true - /callsites@3.1.0: + c3-linearization@0.3.0: + resolution: {integrity: sha512-eQNsZQhFSJAhrNrITy2FpKh7EHS98q/pniDtQhndWqqsvayiPeqZ9T6I9V9PsHcm0nc+ZYJHKUvI/hh37I33HQ==} + + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - dev: true - /chalk@2.4.2: + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - dev: true - /chalk@4.1.2: + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: true - /color-convert@1.9.3: + cli-table@0.3.11: + resolution: {integrity: sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==} + engines: {node: '>= 0.2.0'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - dependencies: - color-name: 1.1.3 - dev: true - /color-convert@2.0.1: + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - dependencies: - color-name: 1.1.4 - dev: true - /color-name@1.1.3: + color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - dev: true - /color-name@1.1.4: + color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: true - /commander@10.0.1: + colors@1.0.3: + resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} + engines: {node: '>=0.1.90'} + + colors@1.4.0: + resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} + engines: {node: '>=0.1.90'} + + commander@10.0.1: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} - dev: true - /cosmiconfig@8.1.3: + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + cosmiconfig@8.1.3: resolution: {integrity: sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==} engines: {node: '>=14'} - dependencies: - import-fresh: 3.3.0 - js-yaml: 4.1.0 - parse-json: 5.2.0 - path-type: 4.0.0 - dev: true - /emoji-regex@8.0.0: + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true - /error-ex@1.3.2: + error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - dependencies: - is-arrayish: 0.2.1 - dev: true - /escape-string-regexp@1.0.5: + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - dev: true - /fast-deep-equal@3.1.3: + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: true - /fast-diff@1.2.0: + fast-diff@1.2.0: resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} - dev: true - /fast-json-stable-stringify@2.1.0: + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true - /fs.realpath@1.0.0: + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true - /glob@8.1.0: + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 5.1.6 - once: 1.4.0 - dev: true - /has-flag@3.0.0: + graphviz@0.0.9: + resolution: {integrity: sha512-SmoY2pOtcikmMCqCSy2NO1YsRfu9OO0wpTlOYW++giGjfX1a6gax/m1Fo8IdUd0/3H15cTOfR1SMKwohj4LKsg==} + engines: {node: '>=0.6.8'} + + has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} - dev: true - /has-flag@4.0.0: + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - dev: true - /ignore@5.2.4: + hasha@5.2.2: + resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} + engines: {node: '>=8'} + + ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} - dev: true - /import-fresh@3.3.0: + import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - dev: true - /inflight@1.0.6: + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - dev: true - /inherits@2.0.4: + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: true - /is-arrayish@0.2.1: + is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - dev: true - /is-fullwidth-code-point@3.0.0: + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: true - /js-tokens@4.0.0: + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: true - /js-yaml@4.1.0: + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - dependencies: - argparse: 2.0.1 - dev: true - /json-parse-even-better-errors@2.3.1: + json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - dev: true - /json-schema-traverse@0.4.1: + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true - /json-schema-traverse@1.0.0: + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - dev: true - /lines-and-columns@1.2.4: + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - dev: true - /lodash.truncate@4.4.2: + lodash.truncate@4.4.2: resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} - dev: true - /lodash@4.17.21: + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true - /lru-cache@6.0.0: + lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} - dependencies: - yallist: 4.0.0 - dev: true - /minimatch@5.1.6: + minimatch@5.1.6: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} - dependencies: - brace-expansion: 2.0.1 - dev: true - /once@1.4.0: + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - dependencies: - wrappy: 1.0.2 - dev: true - /parent-module@1.0.1: + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - dependencies: - callsites: 3.1.0 - dev: true - /parse-json@5.2.0: + parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - dependencies: - '@babel/code-frame': 7.21.4 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - dev: true - /path-type@4.0.0: + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - dev: true - /pluralize@8.0.0: + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} - dev: true - /prettier-plugin-solidity@1.1.3(prettier@2.8.8): + prettier-plugin-solidity@1.1.3: resolution: {integrity: sha512-fQ9yucPi2sBbA2U2Xjh6m4isUTJ7S7QLc/XDDsktqqxYfTwdYKJ0EnnywXHwCGAaYbQNK+HIYPL1OemxuMsgeg==} engines: {node: '>=12'} peerDependencies: prettier: '>=2.3.0 || >=3.0.0-alpha.0' - dependencies: - '@solidity-parser/parser': 0.16.0 - prettier: 2.8.8 - semver: 7.5.0 - solidity-comments-extractor: 0.0.7 - dev: true - /prettier@2.8.8: + prettier@2.8.8: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} hasBin: true - dev: true - /punycode@2.3.0: + punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} - dev: true - /require-from-string@2.0.2: + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - dev: true - /resolve-from@4.0.0: + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - dev: true - /semver@6.3.0: + semver@6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true - dev: true - /semver@7.5.0: + semver@7.5.0: resolution: {integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==} engines: {node: '>=10'} hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: true - /slice-ansi@4.0.0: + sha1-file@2.0.1: + resolution: {integrity: sha512-L4Kum9Lp8cWqcGKycZcXxR6spUoG4idDIUzAKjPiELnIZWxiFlZ5HFVzFxVxuWuGPsrraeL0JoGk0nFZ7AGFEQ==} + engines: {node: '>=10'} + + slice-ansi@4.0.0: resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} engines: {node: '>=10'} + + sloc@0.3.2: + resolution: {integrity: sha512-tnB+gi6TiFpt3qmCGfaV+78dfKwLiH5HRohkW+PnJYHNAcEdk408uxWG+F/3pu4w1eyCO2NC5CpZKuiyMac5GQ==} + hasBin: true + + solhint@3.4.1: + resolution: {integrity: sha512-pzZn2RlZhws1XwvLPVSsxfHrwsteFf5eySOhpAytzXwKQYbTCJV6z8EevYDiSVKMpWrvbKpEtJ055CuEmzp4Xg==} + hasBin: true + + solidity-code-metrics@0.0.25: + resolution: {integrity: sha512-of1wT4B5GL0/IahPttkEQUU3Bgq2PpLfQ1GcGdE8aMOhryWrEa4V5LYYd7cqKwyzWaRYIZ/pLUnaoJtCV2HfHA==} + hasBin: true + + solidity-comments-extractor@0.0.7: + resolution: {integrity: sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==} + + solidity-doppelganger@0.0.11: + resolution: {integrity: sha512-mLIuW1Gfcr49zAZuE3XiQoqvkIAOKJBsgg//3DUvX8G6PvQErfnEKSajXwrYmW27oawwB/qbdRYuFR0inpsJQQ==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + surya@0.4.12: + resolution: {integrity: sha512-DChWScPkYSeJ7nBOKnfFsQoYrFWgSdA3rgCGSjoWxe4QgCOZ5TPz27d9QpsX3fY94SfDeTmAs7x6iv2AXl+7Hg==} + hasBin: true + + table@6.8.1: + resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==} + engines: {node: '>=10.0.0'} + + temp@0.4.0: + resolution: {integrity: sha512-IsFisGgDKk7qzK9erMIkQe/XwiSUdac7z3wYOsjcLkhPBy3k1SlvLoIh2dAHIlEpgA971CgguMrx9z8fFg7tSA==} + engines: {'0': node >=0.4.0} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + treeify@1.1.0: + resolution: {integrity: sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==} + engines: {node: '>=0.6'} + + type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + +snapshots: + + '@babel/code-frame@7.21.4': + dependencies: + '@babel/highlight': 7.18.6 + + '@babel/helper-validator-identifier@7.19.1': {} + + '@babel/highlight@7.18.6': + dependencies: + '@babel/helper-validator-identifier': 7.19.1 + chalk: 2.4.2 + js-tokens: 4.0.0 + + '@solidity-parser/parser@0.16.0': + dependencies: + antlr4ts: 0.5.0-alpha.4 + + '@solidity-parser/parser@0.16.2': + dependencies: + antlr4ts: 0.5.0-alpha.4 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.12.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + + ansi-regex@5.0.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + antlr4@4.12.0: {} + + antlr4ts@0.5.0-alpha.4: {} + + argparse@2.0.1: {} + + ast-parents@0.0.1: {} + + astral-regex@2.0.0: {} + + async@3.2.6: {} + + balanced-match@1.0.2: {} + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + c3-linearization@0.3.0: {} + + callsites@3.1.0: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + cli-table@0.3.11: + dependencies: + colors: 1.0.3 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + colors@1.0.3: {} + + colors@1.4.0: {} + + commander@10.0.1: {} + + commander@11.1.0: {} + + cosmiconfig@8.1.3: + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + + emoji-regex@8.0.0: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.2.0: {} + + fast-json-stable-stringify@2.1.0: {} + + fs.realpath@1.0.0: {} + + get-caller-file@2.0.5: {} + + glob@8.1.0: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + + graphviz@0.0.9: + dependencies: + temp: 0.4.0 + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + hasha@5.2.2: + dependencies: + is-stream: 2.0.1 + type-fest: 0.8.1 + + ignore@5.2.4: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + is-arrayish@0.2.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-stream@2.0.1: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + lines-and-columns@1.2.4: {} + + lodash.truncate@4.4.2: {} + + lodash@4.17.21: {} + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.21.4 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-type@4.0.0: {} + + picomatch@2.3.1: {} + + pluralize@8.0.0: {} + + prettier-plugin-solidity@1.1.3(prettier@2.8.8): + dependencies: + '@solidity-parser/parser': 0.16.0 + prettier: 2.8.8 + semver: 7.5.0 + solidity-comments-extractor: 0.0.7 + + prettier@2.8.8: {} + + punycode@2.3.0: {} + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + semver@6.3.0: {} + + semver@7.5.0: + dependencies: + lru-cache: 6.0.0 + + sha1-file@2.0.1: + dependencies: + hasha: 5.2.2 + + slice-ansi@4.0.0: dependencies: ansi-styles: 4.3.0 astral-regex: 2.0.0 is-fullwidth-code-point: 3.0.0 - dev: true - /solhint@3.4.1: - resolution: {integrity: sha512-pzZn2RlZhws1XwvLPVSsxfHrwsteFf5eySOhpAytzXwKQYbTCJV6z8EevYDiSVKMpWrvbKpEtJ055CuEmzp4Xg==} - hasBin: true + sloc@0.3.2: + dependencies: + async: 3.2.6 + cli-table: 0.3.11 + commander: 11.1.0 + readdirp: 3.6.0 + + solhint@3.4.1: dependencies: '@solidity-parser/parser': 0.16.0 ajv: 6.12.6 @@ -416,67 +691,91 @@ packages: text-table: 0.2.0 optionalDependencies: prettier: 2.8.8 - dev: true - /solidity-comments-extractor@0.0.7: - resolution: {integrity: sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==} - dev: true + solidity-code-metrics@0.0.25: + dependencies: + '@solidity-parser/parser': 0.16.2 + glob: 8.1.0 + sloc: 0.3.2 + solidity-doppelganger: 0.0.11 + surya: 0.4.12 - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + solidity-comments-extractor@0.0.7: {} + + solidity-doppelganger@0.0.11: + dependencies: + '@solidity-parser/parser': 0.16.2 + glob: 8.1.0 + yargs: 17.7.2 + + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - dev: true - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - dev: true - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - dev: true - /table@6.8.1: - resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==} - engines: {node: '>=10.0.0'} + surya@0.4.12: + dependencies: + '@solidity-parser/parser': 0.16.2 + c3-linearization: 0.3.0 + colors: 1.4.0 + graphviz: 0.0.9 + sha1-file: 2.0.1 + treeify: 1.1.0 + yargs: 17.7.2 + + table@6.8.1: dependencies: ajv: 8.12.0 lodash.truncate: 4.4.2 slice-ansi: 4.0.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - dev: true + temp@0.4.0: {} - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + text-table@0.2.0: {} + + treeify@1.1.0: {} + + type-fest@0.8.1: {} + + uri-js@4.4.1: dependencies: punycode: 2.3.0 - dev: true - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true + wrappy@1.0.2: {} + + y18n@5.0.8: {} + + yallist@4.0.0: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 From c16a1b66bb7f4a6c98fa53e23d5ee25a7a45abb6 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 14 Oct 2024 12:14:25 +0400 Subject: [PATCH 038/160] Add audit README and code metrics --- audit/2024-10_loan-consolidator/README.md | 147 ++++ .../solidity-metrics.html | 668 ++++++++++++++++++ 2 files changed, 815 insertions(+) create mode 100644 audit/2024-10_loan-consolidator/README.md create mode 100644 audit/2024-10_loan-consolidator/solidity-metrics.html diff --git a/audit/2024-10_loan-consolidator/README.md b/audit/2024-10_loan-consolidator/README.md new file mode 100644 index 000000000..2a704f3fc --- /dev/null +++ b/audit/2024-10_loan-consolidator/README.md @@ -0,0 +1,147 @@ +# Olympus Loan Consolidator Audit + +## Purpose + +The purpose of this audit is to review the Cooler Loans LoanConsolidator contract and associated contracts. + +These contracts will be installed in the Olympus V3 "Bophades" system, based on the [Default Framework](https://palm-cause-2bd.notion.site/Default-A-Design-Pattern-for-Better-Protocol-Development-7f8ace6d263c4303b108dc5f8c3055b1). + +## Scope + +### In-Scope Contracts + +The contracts in scope for this audit are: + +- [src/](../../src) + - [interfaces/](../../src/interfaces) + - [maker-dao/](../../src/interfaces/maker-dao) + - [IERC3156FlashBorrower.sol](../../src/interfaces/maker-dao/IERC3156FlashBorrower.sol) + - [IERC3156FlashLender.sol](../../src/interfaces/maker-dao/IERC3156FlashLender.sol) + - [modules/](../../src/modules) + - [CHREG/](../../src/modules/CHREG) + - [CHREG.v1.sol](../../src/modules/CHREG/CHREG.v1.sol) + - [OlympusClearinghouseRegistry.sol](../../src/modules/CHREG/OlympusClearinghouseRegistry.sol) + - [EXREG/](../../src/modules/EXREG) + - [EXREG.v1.sol](../../src/modules/EXREG/EXREG.v1.sol) + - [OlympusExternalRegistry.sol](../../src/modules/EXREG/OlympusExternalRegistry.sol) + - [policies/](../../src/policies) + - [ExternalRegistryAdmin.sol](../../src/policies/ExternalRegistryAdmin.sol) + - [LoanConsolidator.sol](../../src/policies/LoanConsolidator.sol) + +The following pull requests can be referred to for the in-scope contracts: + +- [Clearinghouse Registry](https://github.com/OlympusDAO/bophades/pull/191) +- [LoanConsolidator v1](https://github.com/OlympusDAO/bophades/pull/397) +- [LoanConsolidator v2](https://github.com/OlympusDAO/bophades/pull/412) + +See the [solidity-metrics.html](./solidity-metrics.html) report for a summary of the code metrics for these contracts. + +### Previous Audits + +You can review previous audits here: + +- Spearbit (07/2022) + - [Report](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2022-08%20Code4rena.pdf) +- Code4rena Olympus V3 Audit (08/2022) + - [Repo](https://github.com/code-423n4/2022-08-olympus) + - [Findings](https://github.com/code-423n4/2022-08-olympus-findings) +- Kebabsec Olympus V3 Remediation and Follow-up Audits (10/2022 - 11/2022) + - [Remediation Audit Phase 1 Report](https://hackmd.io/tJdujc0gSICv06p_9GgeFQ) + - [Remediation Audit Phase 2 Report](https://hackmd.io/@12og4u7y8i/rk5PeIiEs) + - [Follow-on Audit Report](https://hackmd.io/@12og4u7y8i/Sk56otcBs) +- Cross-Chain Bridge by OtterSec (04/2023)🙏🏼 + - [Report](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/Olympus-CrossChain-Audit.pdf) +- PRICEv2 by HickupHH3 (06/2023) + - [Report](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2023_7_OlympusDAO-final.pdf) + - [Pre-Audit Commit](https://github.com/OlympusDAO/bophades/tree/17fe660525b2f0d706ca318b53111fbf103949ba) + - [Post-Remediations Commit](https://github.com/OlympusDAO/bophades/tree/9c10dc188210632b6ce46c7a836484e8e063151f) +- Cooler Loans by Sherlock (09/2023) + - [Report](https://docs.olympusdao.finance/assets/files/Cooler_Update_Audit_Report-f3f983a8ee8632637790bcc136275aa0.pdf) +- RBS 1.3 & 1.4 by HickupHH3 (11/2023) + - [Report](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/OlympusDAO%20Nov%202023.pdf) + - [Pre-Audit Commit](https://github.com/OlympusDAO/bophades/tree/7a0902cf3ced19d41aafa83e96cf235fb3f15921) + - [Post-Remediations Commit](https://github.com/OlympusDAO/bophades/tree/e61d954cc620254effb014f2d2733e59d828b5b1) + +## Architecture + +### Overview + +The diagram illustrates the architecture of the components: + +```mermaid +flowchart TD + cooler_overseer((cooler_overseer)) -- activate --> Clearinghouse + Clearinghouse -- activates clearinghouse --> CHREG + cooler_overseer((cooler_overseer)) -- deactivate --> Clearinghouse + Clearinghouse -- deactivates clearinghouse --> CHREG + + subgraph Policies + ExternalRegistryAdmin + LoanConsolidator + Clearinghouse + end + + subgraph Modules + CHREG + EXREG + end + + external_registry_admin((external_registry_admin)) -- registers contract --> ExternalRegistryAdmin + ExternalRegistryAdmin -- registers contract --> EXREG + external_registry_admin((external_registry_admin)) -- updates contract --> ExternalRegistryAdmin + ExternalRegistryAdmin -- updates contract --> EXREG + external_registry_admin((external_registry_admin)) -- deregisters contract --> ExternalRegistryAdmin + ExternalRegistryAdmin -- deregisters contract --> EXREG + + Caller((Caller)) -- determine required approvals --> LoanConsolidator + Caller -- consolidate loans --> LoanConsolidator + LoanConsolidator -- validates Clearinghouse is Olympus-owned --> CHREG + LoanConsolidator -- obtains external contract addresses --> EXREG + LoanConsolidator -- takes flashloan --> IERC3156FlashLender + LoanConsolidator -- consolidates loans --> Clearinghouse +``` + +### CHREG (Module) + +Features: + +- Activate and deactivate Clearinghouses +- Access a list of active Clearinghouses +- Access a list of all Clearinghouses (regardless of state) + +The Clearinghouse Registry is a module that requires permissioned access in order to activate/deactivate a Clearinghouse. + +In the current implementation, when a Clearinghouse policy is activated (via `activate()`) by a permissioned user (`cooler_overseer` role), the policy marks the Clearinghouse as active in the Clearinghouse Registry. + +### EXREG (Module) + +Features: + +- Tracks external addresses against an alpha-numeric name +- Access a list of registered names +- Access the current address for a given name +- Update the address for a given name +- Deregister a name + +The External Registry is a module that requires permissioned access in order to add/update/remove an address for a given name. + +### External Registry Admin (Policy) + +Features: + +- Register a name for an external address +- Update the address for a given name +- Deregister a name + +The External Registry Admin is a policy that enables modification of the EXREG module. It is gated to the `external_registry_admin` role. + +### Loan Consolidator (Policy) + +Features: + +- Enables the owner of Cooler Loans to consolidate multiple loans (within the same Cooler) into a single loan, using an ERC3156 flash loan provider. +- Has a helper function to advise the owner on the approvals of gOHM/DAI/sDAI required in order to complete the consolidation. +- The policy can optionally take a protocol fee (sent to the TRSRY) that is set to 0 by default. +- Utilises the CHREG module to obtain the list of Clearinghouse policies. +- Utilises the EXREG module to obtain the addresses of external contracts. +- The loan consolidation functionality can be activated/deactivated by the `loan_consolidator_admin` role. diff --git a/audit/2024-10_loan-consolidator/solidity-metrics.html b/audit/2024-10_loan-consolidator/solidity-metrics.html new file mode 100644 index 000000000..4d62b5a0a --- /dev/null +++ b/audit/2024-10_loan-consolidator/solidity-metrics.html @@ -0,0 +1,668 @@ + + + + Solidity Metrics + + + + +
Rendering Report...

Note: This window will update automatically. In case it is not, close the window and try again (vscode bug) :/
+ + \ No newline at end of file From 617e6884e4a172ca81b4ab7d0672fa14f35d7a17 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 14 Oct 2024 12:17:42 +0400 Subject: [PATCH 039/160] Bump pnpm version in CI --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 4c9d1f651..a18e10118 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -18,7 +18,7 @@ jobs: - uses: pnpm/action-setup@v2 with: - version: 8 + version: 9 - name: Install Node dependencies run: pnpm install From 9c83ffd63007bb4581f36c39ef997efb2889b1e9 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 16 Oct 2024 12:06:47 +0400 Subject: [PATCH 040/160] Rename EXREG to RGSTY --- audit/2024-10_loan-consolidator/README.md | 40 ++--- .../OlympusContractRegistry.sol} | 24 +-- .../EXREG.v1.sol => RGSTY/RGSTY.v1.sol} | 6 +- ...tryAdmin.sol => ContractRegistryAdmin.sol} | 60 ++++---- src/policies/LoanConsolidator.sol | 22 +-- src/scripts/deploy/DeployV2.sol | 38 ++--- src/scripts/env.json | 4 +- ...icy.sol => MockContractRegistryPolicy.sol} | 12 +- src/test/modules/{EXREG.t.sol => RGSTY.t.sol} | 144 +++++++++--------- src/test/policies/ExternalRegistryAdmin.t.sol | 66 ++++---- src/test/policies/LoanConsolidator.t.sol | 36 ++--- 11 files changed, 226 insertions(+), 226 deletions(-) rename src/modules/{EXREG/OlympusExternalRegistry.sol => RGSTY/OlympusContractRegistry.sol} (92%) rename src/modules/{EXREG/EXREG.v1.sol => RGSTY/RGSTY.v1.sol} (97%) rename src/policies/{ExternalRegistryAdmin.sol => ContractRegistryAdmin.sol} (67%) rename src/test/mocks/{MockExternalRegistryPolicy.sol => MockContractRegistryPolicy.sol} (73%) rename src/test/modules/{EXREG.t.sol => RGSTY.t.sol} (78%) diff --git a/audit/2024-10_loan-consolidator/README.md b/audit/2024-10_loan-consolidator/README.md index 2a704f3fc..f11481a0d 100644 --- a/audit/2024-10_loan-consolidator/README.md +++ b/audit/2024-10_loan-consolidator/README.md @@ -21,11 +21,11 @@ The contracts in scope for this audit are: - [CHREG/](../../src/modules/CHREG) - [CHREG.v1.sol](../../src/modules/CHREG/CHREG.v1.sol) - [OlympusClearinghouseRegistry.sol](../../src/modules/CHREG/OlympusClearinghouseRegistry.sol) - - [EXREG/](../../src/modules/EXREG) - - [EXREG.v1.sol](../../src/modules/EXREG/EXREG.v1.sol) - - [OlympusExternalRegistry.sol](../../src/modules/EXREG/OlympusExternalRegistry.sol) + - [RGSTY/](../../src/modules/RGSTY) + - [RGSTY.v1.sol](../../src/modules/RGSTY/RGSTY.v1.sol) + - [OlympusContractRegistry.sol](../../src/modules/RGSTY/OlympusContractRegistry.sol) - [policies/](../../src/policies) - - [ExternalRegistryAdmin.sol](../../src/policies/ExternalRegistryAdmin.sol) + - [ContractRegistryAdmin.sol](../../src/policies/ContractRegistryAdmin.sol) - [LoanConsolidator.sol](../../src/policies/LoanConsolidator.sol) The following pull requests can be referred to for the in-scope contracts: @@ -76,27 +76,27 @@ flowchart TD Clearinghouse -- deactivates clearinghouse --> CHREG subgraph Policies - ExternalRegistryAdmin + ContractRegistryAdmin LoanConsolidator Clearinghouse end subgraph Modules CHREG - EXREG + RGSTY end - external_registry_admin((external_registry_admin)) -- registers contract --> ExternalRegistryAdmin - ExternalRegistryAdmin -- registers contract --> EXREG - external_registry_admin((external_registry_admin)) -- updates contract --> ExternalRegistryAdmin - ExternalRegistryAdmin -- updates contract --> EXREG - external_registry_admin((external_registry_admin)) -- deregisters contract --> ExternalRegistryAdmin - ExternalRegistryAdmin -- deregisters contract --> EXREG + contract_registry_admin((contract_registry_admin)) -- registers contract --> ContractRegistryAdmin + ContractRegistryAdmin -- registers contract --> RGSTY + contract_registry_admin((contract_registry_admin)) -- updates contract --> ContractRegistryAdmin + ContractRegistryAdmin -- updates contract --> RGSTY + contract_registry_admin((contract_registry_admin)) -- deregisters contract --> ContractRegistryAdmin + ContractRegistryAdmin -- deregisters contract --> RGSTY Caller((Caller)) -- determine required approvals --> LoanConsolidator Caller -- consolidate loans --> LoanConsolidator LoanConsolidator -- validates Clearinghouse is Olympus-owned --> CHREG - LoanConsolidator -- obtains external contract addresses --> EXREG + LoanConsolidator -- obtains contract addresses --> RGSTY LoanConsolidator -- takes flashloan --> IERC3156FlashLender LoanConsolidator -- consolidates loans --> Clearinghouse ``` @@ -113,27 +113,27 @@ The Clearinghouse Registry is a module that requires permissioned access in orde In the current implementation, when a Clearinghouse policy is activated (via `activate()`) by a permissioned user (`cooler_overseer` role), the policy marks the Clearinghouse as active in the Clearinghouse Registry. -### EXREG (Module) +### RGSTY (Module) Features: -- Tracks external addresses against an alpha-numeric name +- Tracks contract addresses against an alpha-numeric name - Access a list of registered names - Access the current address for a given name - Update the address for a given name - Deregister a name -The External Registry is a module that requires permissioned access in order to add/update/remove an address for a given name. +The Contract Registry is a module that requires permissioned access in order to add/update/remove an address for a given name. -### External Registry Admin (Policy) +### Contract Registry Admin (Policy) Features: -- Register a name for an external address +- Register a name for a contract address - Update the address for a given name - Deregister a name -The External Registry Admin is a policy that enables modification of the EXREG module. It is gated to the `external_registry_admin` role. +The Contract Registry Admin is a policy that enables modification of the RGSTY module. It is gated to the `contract_registry_admin` role. ### Loan Consolidator (Policy) @@ -143,5 +143,5 @@ Features: - Has a helper function to advise the owner on the approvals of gOHM/DAI/sDAI required in order to complete the consolidation. - The policy can optionally take a protocol fee (sent to the TRSRY) that is set to 0 by default. - Utilises the CHREG module to obtain the list of Clearinghouse policies. -- Utilises the EXREG module to obtain the addresses of external contracts. +- Utilises the RGSTY module to obtain the addresses of contracts. - The loan consolidation functionality can be activated/deactivated by the `loan_consolidator_admin` role. diff --git a/src/modules/EXREG/OlympusExternalRegistry.sol b/src/modules/RGSTY/OlympusContractRegistry.sol similarity index 92% rename from src/modules/EXREG/OlympusExternalRegistry.sol rename to src/modules/RGSTY/OlympusContractRegistry.sol index f8e23d6a6..d15395bed 100644 --- a/src/modules/EXREG/OlympusExternalRegistry.sol +++ b/src/modules/RGSTY/OlympusContractRegistry.sol @@ -2,19 +2,19 @@ pragma solidity 0.8.15; import {Kernel, Module, Policy, Keycode, toKeycode} from "src/Kernel.sol"; -import {EXREGv1} from "./EXREG.v1.sol"; +import {RGSTYv1} from "./RGSTY.v1.sol"; -/// @title Olympus External Registry -/// @notice This module is used to track the address of contracts that are external to the Bophades system. -contract OlympusExternalRegistry is EXREGv1 { +/// @title Olympus Contract Registry +/// @notice This module is used to track the addresses of contracts. +contract OlympusContractRegistry is RGSTYv1 { // ========= STATE ========= // - /// @notice The keycode for the Olympus External Registry - bytes5 public constant keycode = "EXREG"; + /// @notice The keycode for the Olympus Contract Registry + bytes5 public constant keycode = "RGSTY"; // ========= CONSTRUCTOR ========= // - /// @notice Constructor for the Olympus External Registry + /// @notice Constructor for the Olympus Contract Registry /// @dev This function will revert if: /// - The provided kernel address is zero /// @@ -38,7 +38,7 @@ contract OlympusExternalRegistry is EXREGv1 { // ========= CONTRACT REGISTRATION ========= // - /// @inheritdoc EXREGv1 + /// @inheritdoc RGSTYv1 /// @dev This function performs the following steps: /// - Validates the parameters /// - Updates the contract address @@ -93,7 +93,7 @@ contract OlympusExternalRegistry is EXREGv1 { emit ContractRegistered(name_, contractAddress_); } - /// @inheritdoc EXREGv1 + /// @inheritdoc RGSTYv1 /// @dev This function performs the following steps: /// - Validates the parameters /// - Updates the contract address @@ -117,7 +117,7 @@ contract OlympusExternalRegistry is EXREGv1 { emit ContractUpdated(name_, contractAddress_); } - /// @inheritdoc EXREGv1 + /// @inheritdoc RGSTYv1 /// @dev This function performs the following steps: /// - Validates the parameters /// - Removes the contract address @@ -140,7 +140,7 @@ contract OlympusExternalRegistry is EXREGv1 { // ========= VIEW FUNCTIONS ========= // - /// @inheritdoc EXREGv1 + /// @inheritdoc RGSTYv1 /// @dev This function will revert if: /// - The contract is not registered function getContract(bytes5 name_) external view override returns (address) { @@ -151,7 +151,7 @@ contract OlympusExternalRegistry is EXREGv1 { return contractAddress; } - /// @inheritdoc EXREGv1 + /// @inheritdoc RGSTYv1 /// @dev Note that the order of the names in the array is not guaranteed to be consistent. function getContractNames() external view override returns (bytes5[] memory) { return _contractNames; diff --git a/src/modules/EXREG/EXREG.v1.sol b/src/modules/RGSTY/RGSTY.v1.sol similarity index 97% rename from src/modules/EXREG/EXREG.v1.sol rename to src/modules/RGSTY/RGSTY.v1.sol index c32700266..900ce3bff 100644 --- a/src/modules/EXREG/EXREG.v1.sol +++ b/src/modules/RGSTY/RGSTY.v1.sol @@ -3,9 +3,9 @@ pragma solidity 0.8.15; import {Module} from "src/Kernel.sol"; -/// @title External Registry -/// @notice Interface for a module that can track the addresses of external contracts -abstract contract EXREGv1 is Module { +/// @title Contract Registry +/// @notice Interface for a module that can track the addresses of contracts +abstract contract RGSTYv1 is Module { // ========= EVENTS ========= // /// @notice Emitted when a contract is registered or updated diff --git a/src/policies/ExternalRegistryAdmin.sol b/src/policies/ContractRegistryAdmin.sol similarity index 67% rename from src/policies/ExternalRegistryAdmin.sol rename to src/policies/ContractRegistryAdmin.sol index 3a151f183..3f97084d2 100644 --- a/src/policies/ExternalRegistryAdmin.sol +++ b/src/policies/ContractRegistryAdmin.sol @@ -3,16 +3,16 @@ pragma solidity 0.8.15; import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; import {RolesConsumer} from "src/modules/ROLES/OlympusRoles.sol"; -import {EXREGv1} from "src/modules/EXREG/EXREG.v1.sol"; +import {RGSTYv1} from "src/modules/RGSTY/RGSTY.v1.sol"; import {Kernel, Policy, Keycode, toKeycode, Permissions} from "src/Kernel.sol"; -/// @title ExternalRegistryAdmin -/// @notice This policy is used to register and deregister contracts in the EXREG module. +/// @title ContractRegistryAdmin +/// @notice This policy is used to register and deregister contracts in the RGSTY module. /// @dev This contract utilises the following roles: -/// - `external_registry_admin`: Can register and deregister contracts +/// - `contract_registry_admin`: Can register and deregister contracts /// -/// This policy provides permissioned access to the state-changing functions on the EXREG module. The view functions can be called directly on the module. -contract ExternalRegistryAdmin is Policy, RolesConsumer { +/// This policy provides permissioned access to the state-changing functions on the RGSTY module. The view functions can be called directly on the module. +contract ContractRegistryAdmin is Policy, RolesConsumer { // ============ ERRORS ============ // /// @notice Thrown when the address is invalid @@ -23,12 +23,12 @@ contract ExternalRegistryAdmin is Policy, RolesConsumer { // ============ STATE ============ // - /// @notice The EXREG module + /// @notice The RGSTY module /// @dev The value is set when the policy is activated - EXREGv1 internal EXREG; + RGSTYv1 internal RGSTY; - /// @notice The role for the external registry admin - bytes32 public constant EXTERNAL_REGISTRY_ADMIN_ROLE = "external_registry_admin"; + /// @notice The role for the contract registry admin + bytes32 public constant CONTRACT_REGISTRY_ADMIN_ROLE = "contract_registry_admin"; // ============ CONSTRUCTOR ============ // @@ -42,17 +42,17 @@ contract ExternalRegistryAdmin is Policy, RolesConsumer { /// @inheritdoc Policy function configureDependencies() external override returns (Keycode[] memory dependencies) { dependencies = new Keycode[](2); - dependencies[0] = toKeycode("EXREG"); + dependencies[0] = toKeycode("RGSTY"); dependencies[1] = toKeycode("ROLES"); - EXREG = EXREGv1(getModuleAddress(dependencies[0])); + RGSTY = RGSTYv1(getModuleAddress(dependencies[0])); ROLES = ROLESv1(getModuleAddress(dependencies[1])); // Verify the supported version bytes memory expected = abi.encode([1, 1]); - (uint8 EXREG_MAJOR, ) = EXREG.VERSION(); + (uint8 RGSTY_MAJOR, ) = RGSTY.VERSION(); (uint8 ROLES_MAJOR, ) = ROLES.VERSION(); - if (EXREG_MAJOR != 1 || ROLES_MAJOR != 1) revert Policy_WrongModuleVersion(expected); + if (RGSTY_MAJOR != 1 || ROLES_MAJOR != 1) revert Policy_WrongModuleVersion(expected); return dependencies; } @@ -64,12 +64,12 @@ contract ExternalRegistryAdmin is Policy, RolesConsumer { override returns (Permissions[] memory permissions) { - Keycode exregKeycode = toKeycode("EXREG"); + Keycode rgstyKeycode = toKeycode("RGSTY"); permissions = new Permissions[](3); - permissions[0] = Permissions(exregKeycode, EXREGv1.registerContract.selector); - permissions[1] = Permissions(exregKeycode, EXREGv1.updateContract.selector); - permissions[2] = Permissions(exregKeycode, EXREGv1.deregisterContract.selector); + permissions[0] = Permissions(rgstyKeycode, RGSTYv1.registerContract.selector); + permissions[1] = Permissions(rgstyKeycode, RGSTYv1.updateContract.selector); + permissions[2] = Permissions(rgstyKeycode, RGSTYv1.deregisterContract.selector); return permissions; } @@ -89,46 +89,46 @@ contract ExternalRegistryAdmin is Policy, RolesConsumer { // ============ ADMIN FUNCTIONS ============ // - /// @notice Register a contract in the external registry + /// @notice Register a contract in the contract registry /// @dev This function will revert if: /// - This contract is not activated as a policy /// - The caller does not have the required role - /// - The EXREG module reverts + /// - The RGSTY module reverts /// /// @param name_ The name of the contract /// @param contractAddress_ The address of the contract function registerContract( bytes5 name_, address contractAddress_ - ) external onlyPolicyActive onlyRole(EXTERNAL_REGISTRY_ADMIN_ROLE) { - EXREG.registerContract(name_, contractAddress_); + ) external onlyPolicyActive onlyRole(CONTRACT_REGISTRY_ADMIN_ROLE) { + RGSTY.registerContract(name_, contractAddress_); } - /// @notice Update a contract in the external registry + /// @notice Update a contract in the contract registry /// @dev This function will revert if: /// - This contract is not activated as a policy /// - The caller does not have the required role - /// - The EXREG module reverts + /// - The RGSTY module reverts /// /// @param name_ The name of the contract /// @param contractAddress_ The address of the contract function updateContract( bytes5 name_, address contractAddress_ - ) external onlyPolicyActive onlyRole(EXTERNAL_REGISTRY_ADMIN_ROLE) { - EXREG.updateContract(name_, contractAddress_); + ) external onlyPolicyActive onlyRole(CONTRACT_REGISTRY_ADMIN_ROLE) { + RGSTY.updateContract(name_, contractAddress_); } - /// @notice Deregister a contract in the external registry + /// @notice Deregister a contract in the contract registry /// @dev This function will revert if: /// - This contract is not activated as a policy /// - The caller does not have the required role - /// - The EXREG module reverts + /// - The RGSTY module reverts /// /// @param name_ The name of the contract function deregisterContract( bytes5 name_ - ) external onlyPolicyActive onlyRole(EXTERNAL_REGISTRY_ADMIN_ROLE) { - EXREG.deregisterContract(name_); + ) external onlyPolicyActive onlyRole(CONTRACT_REGISTRY_ADMIN_ROLE) { + RGSTY.deregisterContract(name_); } } diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index bc7cf570c..18e4578fd 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -8,7 +8,7 @@ import {Kernel, Keycode, toKeycode, Permissions, Policy} from "src/Kernel.sol"; import {CHREGv1} from "src/modules/CHREG/CHREG.v1.sol"; import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; import {TRSRYv1} from "src/modules/TRSRY/TRSRY.v1.sol"; -import {EXREGv1} from "src/modules/EXREG/EXREG.v1.sol"; +import {RGSTYv1} from "src/modules/RGSTY/RGSTY.v1.sol"; import {RolesConsumer} from "src/modules/ROLES/OlympusRoles.sol"; @@ -98,9 +98,9 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @dev The value is set when the policy is activated TRSRYv1 internal TRSRY; - /// @notice The external contract registry module + /// @notice The contract registry module /// @dev The value is set when the policy is activated - EXREGv1 internal EXREG; + RGSTYv1 internal RGSTY; /// @notice The DAI token /// @dev The value is set when the policy is activated @@ -163,13 +163,13 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent function configureDependencies() external override returns (Keycode[] memory dependencies) { dependencies = new Keycode[](4); dependencies[0] = toKeycode("CHREG"); - dependencies[1] = toKeycode("EXREG"); + dependencies[1] = toKeycode("RGSTY"); dependencies[2] = toKeycode("ROLES"); dependencies[3] = toKeycode("TRSRY"); // Populate module dependencies CHREG = CHREGv1(getModuleAddress(dependencies[0])); - EXREG = EXREGv1(getModuleAddress(dependencies[1])); + RGSTY = RGSTYv1(getModuleAddress(dependencies[1])); ROLES = ROLESv1(getModuleAddress(dependencies[2])); TRSRY = TRSRYv1(getModuleAddress(dependencies[3])); @@ -177,18 +177,18 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent // Modules should be sorted in alphabetical order. bytes memory expected = abi.encode([1, 1, 1, 1]); (uint8 CHREG_MAJOR, ) = CHREG.VERSION(); - (uint8 EXREG_MAJOR, ) = EXREG.VERSION(); + (uint8 RGSTY_MAJOR, ) = RGSTY.VERSION(); (uint8 ROLES_MAJOR, ) = ROLES.VERSION(); (uint8 TRSRY_MAJOR, ) = TRSRY.VERSION(); - if (CHREG_MAJOR != 1 || EXREG_MAJOR != 1 || ROLES_MAJOR != 1 || TRSRY_MAJOR != 1) + if (CHREG_MAJOR != 1 || RGSTY_MAJOR != 1 || ROLES_MAJOR != 1 || TRSRY_MAJOR != 1) revert Policy_WrongModuleVersion(expected); // Populate variables // This function will be called whenever a contract is registered or deregistered, which enables caching of the values - DAI = IERC20(EXREG.getContract("dai")); - SDAI = IERC4626(EXREG.getContract("sdai")); - GOHM = IERC20(EXREG.getContract("gohm")); - FLASH = IERC3156FlashLender(EXREG.getContract("flash")); + DAI = IERC20(RGSTY.getContract("dai")); + SDAI = IERC4626(RGSTY.getContract("sdai")); + GOHM = IERC20(RGSTY.getContract("gohm")); + FLASH = IERC3156FlashLender(RGSTY.getContract("flash")); return dependencies; } diff --git a/src/scripts/deploy/DeployV2.sol b/src/scripts/deploy/DeployV2.sol index 3b2eb8e95..14738f180 100644 --- a/src/scripts/deploy/DeployV2.sol +++ b/src/scripts/deploy/DeployV2.sol @@ -61,8 +61,8 @@ import {pOLY} from "policies/pOLY.sol"; import {ClaimTransfer} from "src/external/ClaimTransfer.sol"; import {Clearinghouse} from "policies/Clearinghouse.sol"; import {YieldRepurchaseFacility} from "policies/YieldRepurchaseFacility.sol"; -import {OlympusExternalRegistry} from "modules/EXREG/OlympusExternalRegistry.sol"; -import {ExternalRegistryAdmin} from "policies/ExternalRegistryAdmin.sol"; +import {OlympusContractRegistry} from "modules/RGSTY/OlympusContractRegistry.sol"; +import {ContractRegistryAdmin} from "policies/ContractRegistryAdmin.sol"; import {MockPriceFeed} from "test/mocks/MockPriceFeed.sol"; import {MockAuraBooster, MockAuraRewardPool, MockAuraMiningLib, MockAuraVirtualRewardPool, MockAuraStashToken} from "test/mocks/AuraMocks.sol"; @@ -89,7 +89,7 @@ contract OlympusDeploy is Script { OlympusRoles public ROLES; OlympusBoostedLiquidityRegistry public BLREG; OlympusClearinghouseRegistry public CHREG; - OlympusExternalRegistry public EXREG; + OlympusContractRegistry public RGSTY; /// Policies Operator public operator; @@ -109,7 +109,7 @@ contract OlympusDeploy is Script { BLVaultLusd public lusdVault; CrossChainBridge public bridge; LegacyBurner public legacyBurner; - ExternalRegistryAdmin public externalRegistryAdmin; + ContractRegistryAdmin public contractRegistryAdmin; LoanConsolidator public loanConsolidator; /// Other Olympus contracts @@ -196,7 +196,7 @@ contract OlympusDeploy is Script { ._deployBoostedLiquidityRegistry .selector; selectorMap["OlympusClearinghouseRegistry"] = this._deployClearinghouseRegistry.selector; - selectorMap["OlympusExternalRegistry"] = this._deployExternalRegistry.selector; + selectorMap["OlympusContractRegistry"] = this._deployContractRegistry.selector; // Policies selectorMap["Operator"] = this._deployOperator.selector; selectorMap["OlympusHeart"] = this._deployHeart.selector; @@ -221,7 +221,7 @@ contract OlympusDeploy is Script { selectorMap["Clearinghouse"] = this._deployClearinghouse.selector; selectorMap["LoanConsolidator"] = this._deployLoanConsolidator.selector; selectorMap["YieldRepurchaseFacility"] = this._deployYieldRepurchaseFacility.selector; - selectorMap["ExternalRegistryAdmin"] = this._deployExternalRegistryAdmin.selector; + selectorMap["ContractRegistryAdmin"] = this._deployContractRegistryAdmin.selector; // Governance selectorMap["Timelock"] = this._deployTimelock.selector; @@ -287,7 +287,7 @@ contract OlympusDeploy is Script { BLREG = OlympusBoostedLiquidityRegistry( envAddress("olympus.modules.OlympusBoostedLiquidityRegistry") ); - EXREG = OlympusExternalRegistry(envAddress("olympus.modules.OlympusExternalRegistry")); + RGSTY = OlympusContractRegistry(envAddress("olympus.modules.OlympusContractRegistry")); // Policies operator = Operator(envAddress("olympus.policies.Operator")); heart = OlympusHeart(envAddress("olympus.policies.OlympusHeart")); @@ -310,8 +310,8 @@ contract OlympusDeploy is Script { claimTransfer = ClaimTransfer(envAddress("olympus.claim.ClaimTransfer")); clearinghouse = Clearinghouse(envAddress("olympus.policies.Clearinghouse")); yieldRepo = YieldRepurchaseFacility(envAddress("olympus.policies.YieldRepurchaseFacility")); - externalRegistryAdmin = ExternalRegistryAdmin( - envAddress("olympus.policies.ExternalRegistryAdmin") + contractRegistryAdmin = ContractRegistryAdmin( + envAddress("olympus.policies.ContractRegistryAdmin") ); loanConsolidator = LoanConsolidator(envAddress("olympus.policies.LoanConsolidator")); @@ -1042,34 +1042,34 @@ contract OlympusDeploy is Script { return address(CHREG); } - function _deployExternalRegistry(bytes calldata) public returns (address) { + function _deployContractRegistry(bytes calldata) public returns (address) { // Decode arguments from the sequence file // None // Print the arguments console2.log(" Kernel:", address(kernel)); - // Deploy OlympusExternalRegistry + // Deploy OlympusContractRegistry vm.broadcast(); - EXREG = new OlympusExternalRegistry(address(kernel)); - console2.log("ExternalRegistry deployed at:", address(EXREG)); + RGSTY = new OlympusContractRegistry(address(kernel)); + console2.log("ContractRegistry deployed at:", address(RGSTY)); - return address(EXREG); + return address(RGSTY); } - function _deployExternalRegistryAdmin(bytes calldata) public returns (address) { + function _deployContractRegistryAdmin(bytes calldata) public returns (address) { // Decode arguments from the sequence file // None // Print the arguments console2.log(" Kernel:", address(kernel)); - // Deploy ExternalRegistryAdmin + // Deploy ContractRegistryAdmin vm.broadcast(); - externalRegistryAdmin = new ExternalRegistryAdmin(address(kernel)); - console2.log("ExternalRegistryAdmin deployed at:", address(externalRegistryAdmin)); + contractRegistryAdmin = new ContractRegistryAdmin(address(kernel)); + console2.log("ContractRegistryAdmin deployed at:", address(contractRegistryAdmin)); - return address(externalRegistryAdmin); + return address(contractRegistryAdmin); } function _deployLoanConsolidator(bytes calldata args_) public returns (address) { diff --git a/src/scripts/env.json b/src/scripts/env.json index 089523500..dc107d394 100644 --- a/src/scripts/env.json +++ b/src/scripts/env.json @@ -64,7 +64,7 @@ "OlympusVotes": "0x0000000000000000000000000000000000000000", "OlympusBoostedLiquidityRegistry": "0x375E06C694B5E50aF8be8FB03495A612eA3e2275", "OlympusClearinghouseRegistry": "0x24b96f2150BF1ed10D3e8B28Ed33E392fbB4Cad5", - "OlympusExternalRegistry": "0x0000000000000000000000000000000000000000" + "OlympusContractRegistry": "0x0000000000000000000000000000000000000000" }, "submodules": { "PRICE": { @@ -97,7 +97,7 @@ "LoanConsolidator": "0xB15bcb1b6593d85890f5287Baa2245B8A29F464a", "pOLY": "0xb37796941cA55b7E4243841930C104Ee325Da5a1", "YieldRepurchaseFacility": "0x30A967eB957E5B1eE053B75F1A57ea6bfb2e907E", - "ExternalRegistryAdmin": "0x0000000000000000000000000000000000000000" + "ContractRegistryAdmin": "0x0000000000000000000000000000000000000000" }, "legacy": { "OHM": "0x64aa3364F17a4D01c6f1751Fd97C2BD3D7e7f1D5", diff --git a/src/test/mocks/MockExternalRegistryPolicy.sol b/src/test/mocks/MockContractRegistryPolicy.sol similarity index 73% rename from src/test/mocks/MockExternalRegistryPolicy.sol rename to src/test/mocks/MockContractRegistryPolicy.sol index 8546dbe07..52a05d6ce 100644 --- a/src/test/mocks/MockExternalRegistryPolicy.sol +++ b/src/test/mocks/MockContractRegistryPolicy.sol @@ -2,25 +2,25 @@ pragma solidity 0.8.15; import {Kernel, Policy, Keycode, toKeycode, Permissions} from "src/Kernel.sol"; -import {EXREGv1} from "src/modules/EXREG/EXREG.v1.sol"; +import {RGSTYv1} from "src/modules/RGSTY/RGSTY.v1.sol"; -contract MockExternalRegistryPolicy is Policy { +contract MockContractRegistryPolicy is Policy { address public dai; - EXREGv1 public exreg; + RGSTYv1 public RGSTY; constructor(Kernel kernel_) Policy(kernel_) {} function configureDependencies() external override returns (Keycode[] memory dependencies) { dependencies = new Keycode[](1); - dependencies[0] = toKeycode("EXREG"); + dependencies[0] = toKeycode("RGSTY"); // Populate module dependencies - exreg = EXREGv1(getModuleAddress(dependencies[0])); + RGSTY = RGSTYv1(getModuleAddress(dependencies[0])); // Populate variables // This function will be called whenever a contract is registered or deregistered, which enables caching of the values - dai = exreg.getContract("dai"); + dai = RGSTY.getContract("dai"); return dependencies; } diff --git a/src/test/modules/EXREG.t.sol b/src/test/modules/RGSTY.t.sol similarity index 78% rename from src/test/modules/EXREG.t.sol rename to src/test/modules/RGSTY.t.sol index 98dbaf962..fe29d1c29 100644 --- a/src/test/modules/EXREG.t.sol +++ b/src/test/modules/RGSTY.t.sol @@ -3,14 +3,14 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; -import {MockExternalRegistryPolicy} from "test/mocks/MockExternalRegistryPolicy.sol"; +import {MockContractRegistryPolicy} from "test/mocks/MockContractRegistryPolicy.sol"; import {Kernel, Actions, Module, fromKeycode} from "src/Kernel.sol"; -import {EXREGv1} from "src/modules/EXREG/EXREG.v1.sol"; -import {OlympusExternalRegistry} from "src/modules/EXREG/OlympusExternalRegistry.sol"; +import {RGSTYv1} from "src/modules/RGSTY/RGSTY.v1.sol"; +import {OlympusContractRegistry} from "src/modules/RGSTY/OlympusContractRegistry.sol"; -contract ExternalRegistryTest is Test { - using ModuleTestFixtureGenerator for OlympusExternalRegistry; +contract ContractRegistryTest is Test { + using ModuleTestFixtureGenerator for OlympusContractRegistry; address public godmode; address public notOwner = address(0x1); @@ -19,11 +19,11 @@ contract ExternalRegistryTest is Test { address public addressTwo = address(0x3); Kernel internal _kernel; - OlympusExternalRegistry internal _exreg; - MockExternalRegistryPolicy internal _policy; - MockExternalRegistryPolicy internal _policy2; + OlympusContractRegistry internal RGSTY; + MockContractRegistryPolicy internal _policy; + MockContractRegistryPolicy internal _policy2; - // External Registry Expected events + // Contract Registry Expected events event ContractRegistered(bytes5 indexed name, address indexed contractAddress); event ContractUpdated(bytes5 indexed name, address indexed contractAddress); event ContractDeregistered(bytes5 indexed name); @@ -32,31 +32,31 @@ contract ExternalRegistryTest is Test { // Deploy Kernel and modules // This contract is the owner _kernel = new Kernel(); - _exreg = new OlympusExternalRegistry(address(_kernel)); - _policy = new MockExternalRegistryPolicy(_kernel); - _policy2 = new MockExternalRegistryPolicy(_kernel); + RGSTY = new OlympusContractRegistry(address(_kernel)); + _policy = new MockContractRegistryPolicy(_kernel); + _policy2 = new MockContractRegistryPolicy(_kernel); // Generate fixtures - godmode = _exreg.generateGodmodeFixture(type(OlympusExternalRegistry).name); + godmode = RGSTY.generateGodmodeFixture(type(OlympusContractRegistry).name); // Install modules and policies on Kernel - _kernel.executeAction(Actions.InstallModule, address(_exreg)); + _kernel.executeAction(Actions.InstallModule, address(RGSTY)); _kernel.executeAction(Actions.ActivatePolicy, godmode); } function _registerContract(bytes5 name_, address contractAddress_) internal { vm.prank(godmode); - _exreg.registerContract(name_, contractAddress_); + RGSTY.registerContract(name_, contractAddress_); } function _deregisterContract(bytes5 name_) internal { vm.prank(godmode); - _exreg.deregisterContract(name_); + RGSTY.deregisterContract(name_); } function _updateContract(bytes5 name_, address contractAddress_) internal { vm.prank(godmode); - _exreg.updateContract(name_, contractAddress_); + RGSTY.updateContract(name_, contractAddress_); } function _activatePolicyOne() internal { @@ -101,15 +101,15 @@ contract ExternalRegistryTest is Test { // [X] it sets the kernel address function test_constructor_whenKernelAddressIsZero_reverts() public { - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidAddress.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidAddress.selector)); - new OlympusExternalRegistry(address(0)); + new OlympusContractRegistry(address(0)); } function test_constructor_whenKernelAddressIsNotZero_reverts() public { - OlympusExternalRegistry exreg = new OlympusExternalRegistry(address(1)); + OlympusContractRegistry rgsty = new OlympusContractRegistry(address(1)); - assertEq(address(exreg.kernel()), address(1), "Kernel address is not set correctly"); + assertEq(address(rgsty.kernel()), address(1), "Kernel address is not set correctly"); } // registerContract @@ -140,51 +140,51 @@ contract ExternalRegistryTest is Test { ); vm.prank(notOwner); - _exreg.registerContract(bytes5("ohm"), addressOne); + RGSTY.registerContract(bytes5("ohm"), addressOne); } function test_registerContract_whenNameIsEmpty_reverts() public { - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); _registerContract(bytes5(""), addressOne); } function test_registerContract_whenNameIsZero_reverts() public { - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); _registerContract(bytes5(0), addressOne); } function test_registerContract_whenNameIsNotLowercase_reverts() public { - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); _registerContract(bytes5("Ohm"), addressOne); - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); _registerContract(bytes5("oHm"), addressOne); - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); _registerContract(bytes5("ohM"), addressOne); } function test_registerContract_whenNameContainsPunctuation_reverts() public { - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); _registerContract(bytes5("ohm!"), addressOne); - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); _registerContract(bytes5("ohm "), addressOne); - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidName.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); _registerContract(bytes5("ohm-"), addressOne); } function test_registerContract_whenNameContainsNumeral() public { _registerContract(bytes5("ohm1"), addressOne); - assertEq(_exreg.getContract(bytes5("ohm1")), addressOne); + assertEq(RGSTY.getContract(bytes5("ohm1")), addressOne); } function test_registerContract_whenContractAddressIsZero_reverts() public { - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidAddress.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidAddress.selector)); _registerContract(bytes5("ohm"), address(0)); } @@ -194,7 +194,7 @@ contract ExternalRegistryTest is Test { givenContractIsRegistered(bytes5("ohm"), addressOne) { // Expect revert - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractAlreadyRegistered.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_ContractAlreadyRegistered.selector)); // Register the second time _registerContract(bytes5("ohm"), addressTwo); @@ -209,13 +209,13 @@ contract ExternalRegistryTest is Test { _registerContract(bytes5("ohm"), addressOne); assertEq( - _exreg.getContract(bytes5("ohm")), + RGSTY.getContract(bytes5("ohm")), addressOne, "Contract address is not set correctly" ); - assertEq(_exreg.getContractNames().length, 1, "Names array is not updated correctly"); + assertEq(RGSTY.getContractNames().length, 1, "Names array is not updated correctly"); assertEq( - _exreg.getContractNames()[0], + RGSTY.getContractNames()[0], bytes5("ohm"), "Names array is not updated correctly" ); @@ -229,33 +229,33 @@ contract ExternalRegistryTest is Test { { // Assert values assertEq( - _exreg.getContract(bytes5("ohm")), + RGSTY.getContract(bytes5("ohm")), addressOne, "ohm contract address is not set correctly" ); assertEq( - _exreg.getContract(bytes5("ohm2")), + RGSTY.getContract(bytes5("ohm2")), addressTwo, "ohm2 contract address is not set correctly" ); assertEq( - _exreg.getContract(bytes5("ohm3")), + RGSTY.getContract(bytes5("ohm3")), address(0x4), "ohm3 contract address is not set correctly" ); - assertEq(_exreg.getContractNames().length, 3, "Names array is not updated correctly"); + assertEq(RGSTY.getContractNames().length, 3, "Names array is not updated correctly"); assertEq( - _exreg.getContractNames()[0], + RGSTY.getContractNames()[0], bytes5("ohm"), "Names array is not updated correctly" ); assertEq( - _exreg.getContractNames()[1], + RGSTY.getContractNames()[1], bytes5("ohm2"), "Names array is not updated correctly" ); assertEq( - _exreg.getContractNames()[2], + RGSTY.getContractNames()[2], bytes5("ohm3"), "Names array is not updated correctly" ); @@ -278,7 +278,7 @@ contract ExternalRegistryTest is Test { function test_activatePolicies_whenContractNotRegistered_reverts() public { // Expect the policy to revert - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_ContractNotRegistered.selector)); // Activate the dependent policies _activatePolicyOne(); @@ -301,11 +301,11 @@ contract ExternalRegistryTest is Test { ); vm.prank(notOwner); - _exreg.updateContract(bytes5("ohm"), addressOne); + RGSTY.updateContract(bytes5("ohm"), addressOne); } function test_updateContract_whenNameIsNotRegistered_reverts() public { - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_ContractNotRegistered.selector)); _updateContract(bytes5("ohm"), addressOne); } @@ -314,7 +314,7 @@ contract ExternalRegistryTest is Test { public givenContractIsRegistered(bytes5("ohm"), addressOne) { - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_InvalidAddress.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidAddress.selector)); _updateContract(bytes5("ohm"), address(0)); } @@ -329,7 +329,7 @@ contract ExternalRegistryTest is Test { // Assert values assertEq( - _exreg.getContract(bytes5("ohm")), + RGSTY.getContract(bytes5("ohm")), addressTwo, "Contract address is not updated correctly" ); @@ -372,11 +372,11 @@ contract ExternalRegistryTest is Test { ); vm.prank(notOwner); - _exreg.deregisterContract(bytes5("ohm")); + RGSTY.deregisterContract(bytes5("ohm")); } function test_deregisterContract_whenNameIsNotRegistered_reverts() public { - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_ContractNotRegistered.selector)); _deregisterContract(bytes5("")); } @@ -390,11 +390,11 @@ contract ExternalRegistryTest is Test { // Assert values // Deregistered contract should revert - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); - _exreg.getContract(bytes5("ohm")); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_ContractNotRegistered.selector)); + RGSTY.getContract(bytes5("ohm")); // Names array should be empty - assertEq(_exreg.getContractNames().length, 0, "Names array is not updated correctly"); + assertEq(RGSTY.getContractNames().length, 0, "Names array is not updated correctly"); } function test_deregisterContract_whenMultipleNamesAreRegistered(uint256 index_) public { @@ -419,27 +419,27 @@ contract ExternalRegistryTest is Test { // Assert values // Deregistered contract should revert - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); - _exreg.getContract(names[randomIndex]); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_ContractNotRegistered.selector)); + RGSTY.getContract(names[randomIndex]); // Other contracts should still be registered if (randomIndex != 0) { assertEq( - _exreg.getContract(names[0]), + RGSTY.getContract(names[0]), addressOne, "ohm contract address is not set correctly" ); } if (randomIndex != 1) { assertEq( - _exreg.getContract(names[1]), + RGSTY.getContract(names[1]), addressTwo, "ohm2 contract address is not set correctly" ); } if (randomIndex != 2) { assertEq( - _exreg.getContract(names[2]), + RGSTY.getContract(names[2]), address(0x4), "ohm3 contract address is not set correctly" ); @@ -455,8 +455,8 @@ contract ExternalRegistryTest is Test { } } - bytes5[] memory contractNames = _exreg.getContractNames(); - assertEq(_exreg.getContractNames().length, 2, "Names array is not updated correctly"); + bytes5[] memory contractNames = RGSTY.getContractNames(); + assertEq(RGSTY.getContractNames().length, 2, "Names array is not updated correctly"); // Check that the expected names are in the array // This is done as the order of names in the array is not guaranteed @@ -479,7 +479,7 @@ contract ExternalRegistryTest is Test { givenPolicyTwoIsActive { // Expect the policies to revert - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_ContractNotRegistered.selector)); // Deregister the contract _deregisterContract(bytes5("dai")); @@ -506,16 +506,16 @@ contract ExternalRegistryTest is Test { // [X] it returns the contract address function test_getContract_whenNameIsNotRegistered_reverts() public { - vm.expectRevert(abi.encodeWithSelector(EXREGv1.Params_ContractNotRegistered.selector)); + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_ContractNotRegistered.selector)); - _exreg.getContract(bytes5("ohm")); + RGSTY.getContract(bytes5("ohm")); } function test_getContract_whenNameIsRegistered() public { _registerContract(bytes5("ohm"), addressOne); assertEq( - _exreg.getContract(bytes5("ohm")), + RGSTY.getContract(bytes5("ohm")), addressOne, "Contract address is not set correctly" ); @@ -527,7 +527,7 @@ contract ExternalRegistryTest is Test { givenContractIsUpdated(bytes5("ohm"), addressTwo) { assertEq( - _exreg.getContract(bytes5("ohm")), + RGSTY.getContract(bytes5("ohm")), addressTwo, "Contract address is not updated correctly" ); @@ -540,7 +540,7 @@ contract ExternalRegistryTest is Test { // [X] it returns the names array function test_getContractNames_whenNoNamesAreRegistered() public { - assertEq(_exreg.getContractNames().length, 0, "Names array is not empty"); + assertEq(RGSTY.getContractNames().length, 0, "Names array is not empty"); } function test_getContractNames_whenNamesAreRegistered() @@ -549,19 +549,19 @@ contract ExternalRegistryTest is Test { givenContractIsRegistered(bytes5("ohm2"), addressTwo) givenContractIsRegistered(bytes5("ohm3"), address(0x4)) { - assertEq(_exreg.getContractNames().length, 3, "Names array is not updated correctly"); + assertEq(RGSTY.getContractNames().length, 3, "Names array is not updated correctly"); assertEq( - _exreg.getContractNames()[0], + RGSTY.getContractNames()[0], bytes5("ohm"), "Names array at index 0 is not updated correctly" ); assertEq( - _exreg.getContractNames()[1], + RGSTY.getContractNames()[1], bytes5("ohm2"), "Names array at index 1 is not updated correctly" ); assertEq( - _exreg.getContractNames()[2], + RGSTY.getContractNames()[2], bytes5("ohm3"), "Names array at index 2 is not updated correctly" ); @@ -571,14 +571,14 @@ contract ExternalRegistryTest is Test { // [X] it returns the correct keycode function test_KEYCODE() public { - assertEq(fromKeycode(_exreg.KEYCODE()), bytes5("EXREG")); + assertEq(fromKeycode(RGSTY.KEYCODE()), bytes5("RGSTY")); } // VERSION // [X] it returns the correct version function test_VERSION() public { - (uint8 major, uint8 minor) = _exreg.VERSION(); + (uint8 major, uint8 minor) = RGSTY.VERSION(); assertEq(major, 1); assertEq(minor, 0); } diff --git a/src/test/policies/ExternalRegistryAdmin.t.sol b/src/test/policies/ExternalRegistryAdmin.t.sol index 644c233f3..a08e672d9 100644 --- a/src/test/policies/ExternalRegistryAdmin.t.sol +++ b/src/test/policies/ExternalRegistryAdmin.t.sol @@ -7,14 +7,14 @@ import {Kernel, Actions} from "src/Kernel.sol"; import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; import {OlympusRoles} from "src/modules/ROLES/OlympusRoles.sol"; import {RolesAdmin} from "src/policies/RolesAdmin.sol"; -import {EXREGv1} from "src/modules/EXREG/EXREG.v1.sol"; -import {OlympusExternalRegistry} from "src/modules/EXREG/OlympusExternalRegistry.sol"; -import {ExternalRegistryAdmin} from "src/policies/ExternalRegistryAdmin.sol"; +import {RGSTYv1} from "src/modules/RGSTY/RGSTY.v1.sol"; +import {OlympusContractRegistry} from "src/modules/RGSTY/OlympusContractRegistry.sol"; +import {ContractRegistryAdmin} from "src/policies/ContractRegistryAdmin.sol"; -contract ExternalRegistryAdminTest is Test { +contract ContractRegistryAdminTest is Test { Kernel public kernel; - OlympusExternalRegistry public EXREG; - ExternalRegistryAdmin public exRegAdmin; + OlympusContractRegistry public RGSTY; + ContractRegistryAdmin public rgstyAdmin; OlympusRoles public ROLES; RolesAdmin public rolesAdmin; @@ -22,7 +22,7 @@ contract ExternalRegistryAdminTest is Test { address public notAdmin = address(0x2); address public ohm = address(0x3); - bytes32 public EXREG_ROLE = "external_registry_admin"; + bytes32 public RGSTY_ROLE = "contract_registry_admin"; function setUp() public { kernel = new Kernel(); @@ -35,27 +35,27 @@ contract ExternalRegistryAdminTest is Test { rolesAdmin = new RolesAdmin(kernel); kernel.executeAction(Actions.ActivatePolicy, address(rolesAdmin)); - // Install the EXREG module - EXREG = new OlympusExternalRegistry(address(kernel)); - kernel.executeAction(Actions.InstallModule, address(EXREG)); + // Install the RGSTY module + RGSTY = new OlympusContractRegistry(address(kernel)); + kernel.executeAction(Actions.InstallModule, address(RGSTY)); - // Set up the ExternalRegistryAdmin policy - exRegAdmin = new ExternalRegistryAdmin(address(kernel)); + // Set up the ContractRegistryAdmin policy + rgstyAdmin = new ContractRegistryAdmin(address(kernel)); } modifier givenPolicyIsActivated() { - kernel.executeAction(Actions.ActivatePolicy, address(exRegAdmin)); + kernel.executeAction(Actions.ActivatePolicy, address(rgstyAdmin)); _; } modifier givenAdminHasRole() { - rolesAdmin.grantRole(EXREG_ROLE, admin); + rolesAdmin.grantRole(RGSTY_ROLE, admin); _; } modifier givenContractIsRegistered() { vm.prank(admin); - exRegAdmin.registerContract("ohm", ohm); + rgstyAdmin.registerContract("ohm", ohm); _; } @@ -69,9 +69,9 @@ contract ExternalRegistryAdminTest is Test { // [X] it registers the contract function test_registerContract_policyNotActive_reverts() public { - vm.expectRevert(abi.encodeWithSelector(ExternalRegistryAdmin.OnlyPolicyActive.selector)); + vm.expectRevert(abi.encodeWithSelector(ContractRegistryAdmin.OnlyPolicyActive.selector)); - exRegAdmin.registerContract("ohm", ohm); + rgstyAdmin.registerContract("ohm", ohm); } function test_registerContract_callerDoesNotHaveRole_reverts() @@ -79,10 +79,10 @@ contract ExternalRegistryAdminTest is Test { givenPolicyIsActivated givenAdminHasRole { - vm.expectRevert(abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, EXREG_ROLE)); + vm.expectRevert(abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, RGSTY_ROLE)); vm.prank(notAdmin); - exRegAdmin.registerContract("ohm", ohm); + rgstyAdmin.registerContract("ohm", ohm); } function test_registerContract() @@ -91,7 +91,7 @@ contract ExternalRegistryAdminTest is Test { givenAdminHasRole givenContractIsRegistered { - assertEq(EXREG.getContract("ohm"), ohm, "contract address"); + assertEq(RGSTY.getContract("ohm"), ohm, "contract address"); } // updateContract @@ -102,9 +102,9 @@ contract ExternalRegistryAdminTest is Test { // [X] it updates the contract function test_updateContract_policyNotActive_reverts() public { - vm.expectRevert(abi.encodeWithSelector(ExternalRegistryAdmin.OnlyPolicyActive.selector)); + vm.expectRevert(abi.encodeWithSelector(ContractRegistryAdmin.OnlyPolicyActive.selector)); - exRegAdmin.updateContract("ohm", ohm); + rgstyAdmin.updateContract("ohm", ohm); } function test_updateContract_callerDoesNotHaveRole_reverts() @@ -112,10 +112,10 @@ contract ExternalRegistryAdminTest is Test { givenPolicyIsActivated givenAdminHasRole { - vm.expectRevert(abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, EXREG_ROLE)); + vm.expectRevert(abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, RGSTY_ROLE)); vm.prank(notAdmin); - exRegAdmin.updateContract("ohm", ohm); + rgstyAdmin.updateContract("ohm", ohm); } function test_updateContract() @@ -126,10 +126,10 @@ contract ExternalRegistryAdminTest is Test { { // Update the contract vm.prank(admin); - exRegAdmin.updateContract("ohm", address(0x4)); + rgstyAdmin.updateContract("ohm", address(0x4)); // Assert values - assertEq(EXREG.getContract("ohm"), address(0x4), "contract address"); + assertEq(RGSTY.getContract("ohm"), address(0x4), "contract address"); } // deregisterContract @@ -140,9 +140,9 @@ contract ExternalRegistryAdminTest is Test { // [X] it deregisters the contract function test_deregisterContract_policyNotActive_reverts() public { - vm.expectRevert(abi.encodeWithSelector(ExternalRegistryAdmin.OnlyPolicyActive.selector)); + vm.expectRevert(abi.encodeWithSelector(ContractRegistryAdmin.OnlyPolicyActive.selector)); - exRegAdmin.deregisterContract("ohm"); + rgstyAdmin.deregisterContract("ohm"); } function test_deregisterContract_callerDoesNotHaveRole_reverts() @@ -151,10 +151,10 @@ contract ExternalRegistryAdminTest is Test { givenAdminHasRole givenContractIsRegistered { - vm.expectRevert(abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, EXREG_ROLE)); + vm.expectRevert(abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, RGSTY_ROLE)); vm.prank(notAdmin); - exRegAdmin.deregisterContract("ohm"); + rgstyAdmin.deregisterContract("ohm"); } function test_deregisterContract() @@ -165,10 +165,10 @@ contract ExternalRegistryAdminTest is Test { { // Deregister the contract vm.prank(admin); - exRegAdmin.deregisterContract("ohm"); + rgstyAdmin.deregisterContract("ohm"); // Assert values - vm.expectRevert(EXREGv1.Params_ContractNotRegistered.selector); - EXREG.getContract("ohm"); + vm.expectRevert(RGSTYv1.Params_ContractNotRegistered.selector); + RGSTY.getContract("ohm"); } } diff --git a/src/test/policies/LoanConsolidator.t.sol b/src/test/policies/LoanConsolidator.t.sol index 360dbbab8..0c87a0968 100644 --- a/src/test/policies/LoanConsolidator.t.sol +++ b/src/test/policies/LoanConsolidator.t.sol @@ -12,8 +12,8 @@ import {CoolerFactory} from "src/external/cooler/CoolerFactory.sol"; import {Clearinghouse} from "src/policies/Clearinghouse.sol"; import {Cooler} from "src/external/cooler/Cooler.sol"; -import {OlympusExternalRegistry} from "src/modules/EXREG/OlympusExternalRegistry.sol"; -import {ExternalRegistryAdmin} from "src/policies/ExternalRegistryAdmin.sol"; +import {OlympusContractRegistry} from "src/modules/RGSTY/OlympusContractRegistry.sol"; +import {ContractRegistryAdmin} from "src/policies/ContractRegistryAdmin.sol"; import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; import {RolesAdmin} from "src/policies/RolesAdmin.sol"; import {TRSRYv1} from "src/modules/TRSRY/TRSRY.v1.sol"; @@ -32,8 +32,8 @@ contract LoanConsolidatorForkTest is Test { CoolerFactory public coolerFactory; Clearinghouse public clearinghouse; - OlympusExternalRegistry public EXREG; - ExternalRegistryAdmin public exregAdmin; + OlympusContractRegistry public RGSTY; + ContractRegistryAdmin public rgstyAdmin; RolesAdmin public rolesAdmin; TRSRYv1 public TRSRY; Kernel public kernel; @@ -87,27 +87,27 @@ contract LoanConsolidatorForkTest is Test { // Determine the kernel executor kernelExecutor = Kernel(kernel).executor(); - // Install EXREG (since block is pinned, it won't be installed) - EXREG = new OlympusExternalRegistry(address(kernel)); + // Install RGSTY (since block is pinned, it won't be installed) + RGSTY = new OlympusContractRegistry(address(kernel)); vm.prank(kernelExecutor); - kernel.executeAction(Actions.InstallModule, address(EXREG)); + kernel.executeAction(Actions.InstallModule, address(RGSTY)); - // Set up and install the external registry admin policy - exregAdmin = new ExternalRegistryAdmin(address(kernel)); + // Set up and install the contract registry admin policy + rgstyAdmin = new ContractRegistryAdmin(address(kernel)); vm.prank(kernelExecutor); - kernel.executeAction(Actions.ActivatePolicy, address(exregAdmin)); + kernel.executeAction(Actions.ActivatePolicy, address(rgstyAdmin)); - // Grant the external registry admin role to this contract + // Grant the contract registry admin role to this contract vm.prank(kernelExecutor); - rolesAdmin.grantRole("external_registry_admin", address(this)); + rolesAdmin.grantRole("contract_registry_admin", address(this)); - // Register the tokens with EXREG + // Register the tokens with RGSTY vm.startPrank(address(this)); - exregAdmin.registerContract("dai", address(dai)); - exregAdmin.registerContract("sdai", address(sdai)); - exregAdmin.registerContract("ohm", address(ohm)); - exregAdmin.registerContract("gohm", address(gohm)); - exregAdmin.registerContract("flash", address(lender)); + rgstyAdmin.registerContract("dai", address(dai)); + rgstyAdmin.registerContract("sdai", address(sdai)); + rgstyAdmin.registerContract("ohm", address(ohm)); + rgstyAdmin.registerContract("gohm", address(gohm)); + rgstyAdmin.registerContract("flash", address(lender)); vm.stopPrank(); admin = vm.addr(0x2); From 734bbab09173d596aec6a46b55eafe9d0219b124 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 16 Oct 2024 12:12:40 +0400 Subject: [PATCH 041/160] Rename LoanConsolidator test file --- .../{LoanConsolidator.t.sol => LoanConsolidatorFork.t.sol} | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename src/test/policies/{LoanConsolidator.t.sol => LoanConsolidatorFork.t.sol} (99%) diff --git a/src/test/policies/LoanConsolidator.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol similarity index 99% rename from src/test/policies/LoanConsolidator.t.sol rename to src/test/policies/LoanConsolidatorFork.t.sol index 0c87a0968..e219064e3 100644 --- a/src/test/policies/LoanConsolidator.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -61,7 +61,8 @@ contract LoanConsolidatorForkTest is Test { bytes32 public constant ROLE_EMERGENCY_SHUTDOWN = "emergency_shutdown"; function setUp() public { - // Mainnet Fork at current block. + // Mainnet Fork at a fixed block + // Prior to actual deployment of LoanConsolidator and RGSTY vm.createSelectFork(RPC_URL, 18762666); // Required Contracts From af0cf03ceb3c9af2bfb206f4f4d55666891da7ba Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 16 Oct 2024 12:13:05 +0400 Subject: [PATCH 042/160] Pin fork block in BLV tests --- src/test/policies/BoostedLiquidity/BLVaultLidoFork.t.sol | 5 +++++ src/test/policies/BoostedLiquidity/BLVaultLusdFork.t.sol | 5 +++++ .../policies/BoostedLiquidity/BLVaultManagerLidoFork.t.sol | 5 +++++ .../policies/BoostedLiquidity/BLVaultManagerLusdFork.t.sol | 4 ++++ 4 files changed, 19 insertions(+) diff --git a/src/test/policies/BoostedLiquidity/BLVaultLidoFork.t.sol b/src/test/policies/BoostedLiquidity/BLVaultLidoFork.t.sol index ec19f767f..27896ede6 100644 --- a/src/test/policies/BoostedLiquidity/BLVaultLidoFork.t.sol +++ b/src/test/policies/BoostedLiquidity/BLVaultLidoFork.t.sol @@ -79,7 +79,12 @@ contract BLVaultLidoTestFork is Test { uint256[] internal minAmountsOut = [0, 0]; + string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); + function setUp() public { + // Mainnet Fork at a fixed block that is known to work + vm.createSelectFork(RPC_URL, 18762666); + { // Set up users alice = payable(address(uint160(uint256(keccak256(abi.encodePacked("alice")))))); diff --git a/src/test/policies/BoostedLiquidity/BLVaultLusdFork.t.sol b/src/test/policies/BoostedLiquidity/BLVaultLusdFork.t.sol index 9e8b96e2d..b198c9dd2 100644 --- a/src/test/policies/BoostedLiquidity/BLVaultLusdFork.t.sol +++ b/src/test/policies/BoostedLiquidity/BLVaultLusdFork.t.sol @@ -71,7 +71,12 @@ contract BLVaultLusdTestFork is Test { uint256[] internal minAmountsOut = [0, 0]; + string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); + function setUp() public { + // Mainnet Fork at a fixed block that is known to work + vm.createSelectFork(RPC_URL, 18762666); + { // Set up users alice = payable(address(uint160(uint256(keccak256(abi.encodePacked("alice")))))); diff --git a/src/test/policies/BoostedLiquidity/BLVaultManagerLidoFork.t.sol b/src/test/policies/BoostedLiquidity/BLVaultManagerLidoFork.t.sol index 993f4dee0..d4f5404bd 100644 --- a/src/test/policies/BoostedLiquidity/BLVaultManagerLidoFork.t.sol +++ b/src/test/policies/BoostedLiquidity/BLVaultManagerLidoFork.t.sol @@ -75,7 +75,12 @@ contract BLVaultManagerLidoTestFork is Test { BLVaultManagerLido internal vaultManager; BLVaultLido internal vaultImplementation; + string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); + function setUp() public { + // Mainnet Fork at a fixed block that is known to work + vm.createSelectFork(RPC_URL, 18762666); + { // Set up users alice = payable(address(uint160(uint256(keccak256(abi.encodePacked("alice")))))); diff --git a/src/test/policies/BoostedLiquidity/BLVaultManagerLusdFork.t.sol b/src/test/policies/BoostedLiquidity/BLVaultManagerLusdFork.t.sol index 9e7170f85..51407682b 100644 --- a/src/test/policies/BoostedLiquidity/BLVaultManagerLusdFork.t.sol +++ b/src/test/policies/BoostedLiquidity/BLVaultManagerLusdFork.t.sol @@ -68,7 +68,11 @@ contract BLVaultManagerLusdTestFork is Test { uint256 internal constant OHM_LIMIT = 233_645e9; // $2.5m = 233,645 OHM + string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); + function setUp() public { + // Mainnet Fork at a fixed block that is known to work + vm.createSelectFork(RPC_URL, 18762666); { // Set up users alice = payable(address(uint160(uint256(keccak256(abi.encodePacked("alice")))))); From 46ff47254db1549a6c932d527a6c3cf02395300c Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 16 Oct 2024 12:14:25 +0400 Subject: [PATCH 043/160] Fix deployment sequences --- .../{external_registry.json => contract_registry.json} | 4 ++-- .../deploy/savedDeployments/cooler_loan_consolidator.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/scripts/deploy/savedDeployments/{external_registry.json => contract_registry.json} (55%) diff --git a/src/scripts/deploy/savedDeployments/external_registry.json b/src/scripts/deploy/savedDeployments/contract_registry.json similarity index 55% rename from src/scripts/deploy/savedDeployments/external_registry.json rename to src/scripts/deploy/savedDeployments/contract_registry.json index e4a3328d6..06ac8d934 100644 --- a/src/scripts/deploy/savedDeployments/external_registry.json +++ b/src/scripts/deploy/savedDeployments/contract_registry.json @@ -1,11 +1,11 @@ { "sequence": [ { - "name": "OlympusExternalRegistry", + "name": "OlympusContractRegistry", "args": {} }, { - "name": "ExternalRegistryAdmin", + "name": "ContractRegistryAdmin", "args": {} } ] diff --git a/src/scripts/deploy/savedDeployments/cooler_loan_consolidator.json b/src/scripts/deploy/savedDeployments/cooler_loan_consolidator.json index 8f14289c2..fac326d28 100644 --- a/src/scripts/deploy/savedDeployments/cooler_loan_consolidator.json +++ b/src/scripts/deploy/savedDeployments/cooler_loan_consolidator.json @@ -1,7 +1,7 @@ { "sequence": [ { - "name": "CoolerUtils", + "name": "LoanConsolidator", "args": { "feePercentage": 0 } From 688fe5169cfc4e95cca22e4a3239e0dff9e2af30 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 16 Oct 2024 12:19:06 +0400 Subject: [PATCH 044/160] Update solidity metrics --- audit/2024-10_loan-consolidator/solidity-metrics.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audit/2024-10_loan-consolidator/solidity-metrics.html b/audit/2024-10_loan-consolidator/solidity-metrics.html index 4d62b5a0a..dcbcf43f3 100644 --- a/audit/2024-10_loan-consolidator/solidity-metrics.html +++ b/audit/2024-10_loan-consolidator/solidity-metrics.html @@ -656,7 +656,7 @@ (()=>{function t(t,e,a){let s={totals:[],avg:[],keys:Object.keys(t.totals.ast).filter((t=>void 0===e||e(t)))};return s.keys.forEach((e=>{a&&t.totals.ast[e],s.totals.push(t.totals.ast[e]||0),s.avg.push(t.avg.ast[e]||0)})),s}window.addEventListener("message",(e=>{const a=e.data;switch(console.log(a),a.command){case"renderReport":s=a.value,l=new showdown.Converter({extensions:["table"]}),document.getElementById("preview").innerHTML=l.makeHtml(s.markdownTemplate),function(e,a){window.chartColors;let s=function(t){console.log(t);let e=t.avg.summary,a=t.totals.summary,s={avg:[],totals:[],keys:Array.from(new Set([...Object.keys(e),...Object.keys(a)]))};return s.keys.forEach((t=>{s.avg.push(e[t]||0),s.totals.push(a[t]||0)})),s}(e);new Chart("chart-risk-summary",{type:"radar",data:{labels:s.keys,datasets:[{data:s.totals,label:"overall",fill:0},{data:s.avg,hidden:!1,label:"average",fill:0}]},options:{maintainAspectRatio:!0,spanGaps:!1,scale:{ticks:{beginAtZero:!0,max:7}},elements:{line:{tension:1e-6}},plugins:{filler:{propagate:!1},"samples-filler-analyser":{target:"chart-analyser"},colorschemes:{scheme:"tableau.Tableau20"}}}});let l=function(t){let e={sloc:[],nsloc:[],keys:Object.keys(t.totals.nsloc).filter((t=>"total"!==t&&"commentToSourceRatio"!==t))};return e.keys.forEach((a=>{e.sloc.push(t.totals.sloc[a]||0),e.nsloc.push(t.totals.nsloc[a]||0)})),e}(e);new Chart("chart-nsloc-total",{type:"pie",data:{labels:l.keys,datasets:[{label:"sloc",data:l.sloc},{label:"normalized sloc",data:l.nsloc}]},options:{plugins:{colorschemes:{scheme:"tableau.Tableau20"}}}});var o=function(t){let e={totals:[],avg:[],keys:Object.keys(t.totals.num)};return e.keys.forEach((a=>{e.totals.push(t.totals.num[a]||0),e.avg.push(t.avg.num[a]||0)})),e}(e),n=(new Chart("chart-num-bar",{type:"bar",data:{labels:o.keys,datasets:[{label:"total",data:o.totals},{label:"average",data:o.avg,hidden:!0}]},options:{responsive:!0,legend:{position:"top"},title:{display:!0,text:"Summary"},scales:{yAxes:[{type:"logarithmic",ticks:{suggestedMin:0,beginAtZero:!0,min:0}}]}}}),t(e,(function(t){return!t.startsWith("FunctionCall:Name:")&&!t.startsWith("AssemblyCall:Name:")}),!0)),i=(new Chart("chart-num-bar-ast",{type:"bar",data:{labels:n.keys,datasets:[{label:"total",data:n.totals},{label:"average",data:n.avg,hidden:!0}]},options:{responsive:!0,legend:{position:"top"},title:{display:!0,text:"AST Elements"},scales:{yAxes:[{type:"logarithmic",ticks:{suggestedMin:0,beginAtZero:!0,min:0}}]}}}),t(e,(function(t){return t.startsWith("FunctionCall:Name:")}),!0)),r=(new Chart("chart-num-bar-ast-funccalls",{type:"bar",data:{labels:i.keys,datasets:[{label:"total",data:i.totals}]},options:{responsive:!0,legend:{position:"top"},title:{display:!0,text:"Function Calls"},scales:{yAxes:[{type:"logarithmic",ticks:{suggestedMin:0,beginAtZero:!0,min:0}}]}}}),t(e,(function(t){return t.startsWith("AssemblyCall:Name:")}),!0));new Chart("chart-num-bar-ast-asmcalls",{type:"bar",data:{labels:r.keys,datasets:[{label:"total",data:r.totals}]},options:{responsive:!0,legend:{position:"top"},title:{display:!0,text:"Assembly Calls"},scales:{yAxes:[{type:"logarithmic",ticks:{suggestedMin:0,beginAtZero:!0,min:0}}]}}})}(s.jsonData,window.chartColors),Object.entries(s.dotGraphs).forEach((t=>{let e=t[0];!function(t,e){var a=d3.select(e).graphviz();let s=d3.transition("startTransition").ease(d3.easeLinear).delay(0).duration(0);a.fade(!0).transition(s).zoomScaleExtent([0,1/0]).zoom(!0).renderDot(t),d3.selectAll(".node,.edge,.cluster")}(t[1],e)})),document.getElementById("loading-msg").style.display="none"}var s,l}),!1),window.onload=function(){"function"==typeof acquireVsCodeApi&&acquireVsCodeApi().postMessage({command:"onPageLoaded"})}})(); + Solidity Metrics + + + + + + + + - -
Rendering Report...

Note: This window will update automatically. In case it is not, close the window and try again (vscode bug) :/
- - \ No newline at end of file + + +
+ Rendering Report...

Note: This window will update automatically. In case it is not, close the + window and try again (vscode bug) :/ +
+
+ + From ea1de1d47f240bae9a0f7a2c86b21a9311ba43bc Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 28 Oct 2024 10:59:45 +0400 Subject: [PATCH 072/160] chore: linting --- audit/2024-10_loan-consolidator/README.md | 56 +++++++++++------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/audit/2024-10_loan-consolidator/README.md b/audit/2024-10_loan-consolidator/README.md index c3d119a2a..d38db4a60 100644 --- a/audit/2024-10_loan-consolidator/README.md +++ b/audit/2024-10_loan-consolidator/README.md @@ -13,20 +13,20 @@ These contracts will be installed in the Olympus V3 "Bophades" system, based on The contracts in scope for this audit are: - [src/](../../src) - - [interfaces/](../../src/interfaces) - - [maker-dao/](../../src/interfaces/maker-dao) - - [IERC3156FlashBorrower.sol](../../src/interfaces/maker-dao/IERC3156FlashBorrower.sol) - - [IERC3156FlashLender.sol](../../src/interfaces/maker-dao/IERC3156FlashLender.sol) - - [modules/](../../src/modules) - - [CHREG/](../../src/modules/CHREG) - - [CHREG.v1.sol](../../src/modules/CHREG/CHREG.v1.sol) - - [OlympusClearinghouseRegistry.sol](../../src/modules/CHREG/OlympusClearinghouseRegistry.sol) - - [RGSTY/](../../src/modules/RGSTY) - - [RGSTY.v1.sol](../../src/modules/RGSTY/RGSTY.v1.sol) - - [OlympusContractRegistry.sol](../../src/modules/RGSTY/OlympusContractRegistry.sol) - - [policies/](../../src/policies) - - [ContractRegistryAdmin.sol](../../src/policies/ContractRegistryAdmin.sol) - - [LoanConsolidator.sol](../../src/policies/LoanConsolidator.sol) + - [interfaces/](../../src/interfaces) + - [maker-dao/](../../src/interfaces/maker-dao) + - [IERC3156FlashBorrower.sol](../../src/interfaces/maker-dao/IERC3156FlashBorrower.sol) + - [IERC3156FlashLender.sol](../../src/interfaces/maker-dao/IERC3156FlashLender.sol) + - [modules/](../../src/modules) + - [CHREG/](../../src/modules/CHREG) + - [CHREG.v1.sol](../../src/modules/CHREG/CHREG.v1.sol) + - [OlympusClearinghouseRegistry.sol](../../src/modules/CHREG/OlympusClearinghouseRegistry.sol) + - [RGSTY/](../../src/modules/RGSTY) + - [RGSTY.v1.sol](../../src/modules/RGSTY/RGSTY.v1.sol) + - [OlympusContractRegistry.sol](../../src/modules/RGSTY/OlympusContractRegistry.sol) + - [policies/](../../src/policies) + - [ContractRegistryAdmin.sol](../../src/policies/ContractRegistryAdmin.sol) + - [LoanConsolidator.sol](../../src/policies/LoanConsolidator.sol) The following pull requests can be referred to for the in-scope contracts: @@ -41,26 +41,26 @@ See the [solidity-metrics.html](./solidity-metrics.html) report for a summary of You can review previous audits here: - Spearbit (07/2022) - - [Report](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2022-08%20Code4rena.pdf) + - [Report](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2022-08%20Code4rena.pdf) - Code4rena Olympus V3 Audit (08/2022) - - [Repo](https://github.com/code-423n4/2022-08-olympus) - - [Findings](https://github.com/code-423n4/2022-08-olympus-findings) + - [Repo](https://github.com/code-423n4/2022-08-olympus) + - [Findings](https://github.com/code-423n4/2022-08-olympus-findings) - Kebabsec Olympus V3 Remediation and Follow-up Audits (10/2022 - 11/2022) - - [Remediation Audit Phase 1 Report](https://hackmd.io/tJdujc0gSICv06p_9GgeFQ) - - [Remediation Audit Phase 2 Report](https://hackmd.io/@12og4u7y8i/rk5PeIiEs) - - [Follow-on Audit Report](https://hackmd.io/@12og4u7y8i/Sk56otcBs) + - [Remediation Audit Phase 1 Report](https://hackmd.io/tJdujc0gSICv06p_9GgeFQ) + - [Remediation Audit Phase 2 Report](https://hackmd.io/@12og4u7y8i/rk5PeIiEs) + - [Follow-on Audit Report](https://hackmd.io/@12og4u7y8i/Sk56otcBs) - Cross-Chain Bridge by OtterSec (04/2023)🙏🏼 - - [Report](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/Olympus-CrossChain-Audit.pdf) + - [Report](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/Olympus-CrossChain-Audit.pdf) - PRICEv2 by HickupHH3 (06/2023) - - [Report](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2023_7_OlympusDAO-final.pdf) - - [Pre-Audit Commit](https://github.com/OlympusDAO/bophades/tree/17fe660525b2f0d706ca318b53111fbf103949ba) - - [Post-Remediations Commit](https://github.com/OlympusDAO/bophades/tree/9c10dc188210632b6ce46c7a836484e8e063151f) + - [Report](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2023_7_OlympusDAO-final.pdf) + - [Pre-Audit Commit](https://github.com/OlympusDAO/bophades/tree/17fe660525b2f0d706ca318b53111fbf103949ba) + - [Post-Remediations Commit](https://github.com/OlympusDAO/bophades/tree/9c10dc188210632b6ce46c7a836484e8e063151f) - Cooler Loans by Sherlock (09/2023) - - [Report](https://docs.olympusdao.finance/assets/files/Cooler_Update_Audit_Report-f3f983a8ee8632637790bcc136275aa0.pdf) + - [Report](https://docs.olympusdao.finance/assets/files/Cooler_Update_Audit_Report-f3f983a8ee8632637790bcc136275aa0.pdf) - RBS 1.3 & 1.4 by HickupHH3 (11/2023) - - [Report](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/OlympusDAO%20Nov%202023.pdf) - - [Pre-Audit Commit](https://github.com/OlympusDAO/bophades/tree/7a0902cf3ced19d41aafa83e96cf235fb3f15921) - - [Post-Remediations Commit](https://github.com/OlympusDAO/bophades/tree/e61d954cc620254effb014f2d2733e59d828b5b1) + - [Report](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/OlympusDAO%20Nov%202023.pdf) + - [Pre-Audit Commit](https://github.com/OlympusDAO/bophades/tree/7a0902cf3ced19d41aafa83e96cf235fb3f15921) + - [Post-Remediations Commit](https://github.com/OlympusDAO/bophades/tree/e61d954cc620254effb014f2d2733e59d828b5b1) ## Architecture From c52ca86f108e209c7fc71e85e376c73571b89c68 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 28 Oct 2024 11:28:55 +0400 Subject: [PATCH 073/160] Add test for non-zero lender fee, fix issues --- src/policies/LoanConsolidator.sol | 16 ++++- src/test/mocks/MockFlashloanLender.sol | 67 ++++++++++++++++++++ src/test/policies/LoanConsolidatorFork.t.sol | 66 ++++++++++++++++++- 3 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 src/test/mocks/MockFlashloanLender.sol diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index 91746157a..511c596b9 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -624,6 +624,18 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent revert Params_InvalidClearinghouse(); } + /// @notice Assembles the parameters for a flashloan + /// + /// @param clearinghouseFrom_ Clearinghouse that issued the existing loans + /// @param clearinghouseTo_ Clearinghouse to be used to issue the consolidated loan + /// @param coolerFrom_ Cooler contract that issued the existing loans + /// @param coolerTo_ Cooler contract to be used to issue the consolidated loan + /// @param ids_ Array of loan ids to be consolidated + /// @param migrationType_ Migration type + /// @param reserveFrom_ Reserve token for the existing loans + /// @param reserveTo_ Reserve token for the consolidated loan + /// @return flashloanAmount Amount of the flashloan + /// @return flashloanParams Flashloan parameters function _getFlashloanParameters( Clearinghouse clearinghouseFrom_, Clearinghouse clearinghouseTo_, @@ -643,7 +655,9 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent uint256 protocolFee = getProtocolFee(totalPrincipal + totalInterest); // The flashloan amount is in DAI. This assumes a 1:1 exchange rate. - flashloanAmount = totalPrincipal + totalInterest; + // The flashloan amount is the total principal, without any interest + // This is because the interest is paid by the caller, not the flashloan provider + flashloanAmount = totalPrincipal; flashloanParams = FlashLoanData({ clearinghouseFrom: clearinghouseFrom_, diff --git a/src/test/mocks/MockFlashloanLender.sol b/src/test/mocks/MockFlashloanLender.sol new file mode 100644 index 000000000..e8aabad6e --- /dev/null +++ b/src/test/mocks/MockFlashloanLender.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: Unlicensed +pragma solidity ^0.8.15; + +import {IERC3156FlashLender} from "src/interfaces/maker-dao/IERC3156FlashLender.sol"; +import {IERC3156FlashBorrower} from "src/interfaces/maker-dao/IERC3156FlashBorrower.sol"; +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {console2} from "forge-std/console2.sol"; + +contract MockFlashloanLender is IERC3156FlashLender { + uint16 public feePercent; + uint16 public constant MAX_FEE_PERCENT = 10000; + + ERC20 public immutable token; + + error InvalidToken(); + + constructor(uint16 feePercent_, address token_) { + feePercent = feePercent_; + token = ERC20(token_); + } + + function setFeePercent(uint16 feePercent_) external { + feePercent = feePercent_; + } + + function maxFlashLoan(address token_) external view override returns (uint256) { + if (token_ != address(token)) revert InvalidToken(); + + return type(uint256).max; + } + + function _flashFee(uint256 amount) internal view returns (uint256) { + return (amount * feePercent) / MAX_FEE_PERCENT; + } + + function flashFee(address token_, uint256 amount) external view override returns (uint256) { + if (token_ != address(token)) revert InvalidToken(); + + return _flashFee(amount); + } + + function flashLoan( + IERC3156FlashBorrower receiver_, + address token_, + uint256 amount_, + bytes calldata data_ + ) external override returns (bool) { + if (token_ != address(token)) revert InvalidToken(); + + // Transfer the funds to the receiver + token.transfer(address(receiver_), amount_); + + // Calculate the lender fee + uint256 lenderFee = _flashFee(amount_); + + // Call the receiver's onFlashLoan function + receiver_.onFlashLoan(msg.sender, token_, amount_, lenderFee, data_); + + // Calculate the amount to be returned to the caller + uint256 amountToReturn = amount_ + lenderFee; + + // Transfer the funds back to this contract + token.transferFrom(address(receiver_), address(this), amountToReturn); + + return true; + } +} diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index 38c93c8c3..c33f39677 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.15; import {Test, console2, stdStorage, StdStorage} from "forge-std/Test.sol"; +import {MockFlashloanLender} from "src/test/mocks/MockFlashloanLender.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {IERC4626} from "forge-std/interfaces/IERC4626.sol"; @@ -315,6 +316,26 @@ contract LoanConsolidatorForkTest is Test { _; } + modifier givenMockFlashloanLender() { + lender = address(new MockFlashloanLender(0, address(dai))); + + // Swap the maker flashloan lender for our mock + vm.startPrank(address(this)); + rgstyAdmin.updateContract("flash", lender); + vm.stopPrank(); + _; + } + + modifier givenMockFlashloanLenderFee(uint16 feePercent_) { + MockFlashloanLender(lender).setFeePercent(feePercent_); + _; + } + + modifier givenMockFlashloanLenderHasBalance(uint256 balance_) { + deal(address(dai), lender, balance_); + _; + } + function _createCooler( CoolerFactory coolerFactory_, address wallet_, @@ -615,7 +636,7 @@ contract LoanConsolidatorForkTest is Test { // given the protocol fee is non-zero // [X] it transfers the protocol fee to the collector // given the lender fee is non-zero - // [ ] it transfers the lender fee to the lender + // [X] it transfers the lender fee to the lender // when the protocol fee is zero // [X] it succeeds, but does not transfer additional DAI for the fee // when the Clearinghouse is disabled @@ -1011,6 +1032,49 @@ contract LoanConsolidatorForkTest is Test { ); } + function test_consolidate_lenderFee() + public + givenAdminHasRole + givenPolicyActive + givenMockFlashloanLender + givenMockFlashloanLenderFee(100) // 1% + givenMockFlashloanLenderHasBalance(20_000_000e18) + { + uint256[] memory idsA = _idsA(); + + // Record the initial debt balance + (uint256 totalPrincipal, uint256 totalInterest) = clearinghouse.getLoanForCollateral( + _GOHM_AMOUNT + ); + + // Record the amount of DAI in the wallet + uint256 initPrincipal = dai.balanceOf(walletA); + (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( + address(clearinghouse), + address(coolerA), + idsA + ); + + // Grant approvals + _grantCallerApprovals(walletA, address(clearinghouse), idsA); + + // Calculate the expected lender fee + uint256 lenderFee = MockFlashloanLender(lender).flashFee(address(dai), totalPrincipal); + uint256 expectedLenderBalance = 20_000_000e18 + lenderFee; + + // Consolidate loans + _consolidate(idsA); + + _assertCoolerLoans(_GOHM_AMOUNT); + _assertTokenBalances( + initPrincipal - interest - protocolFee - lenderFee, + expectedLenderBalance, + protocolFee, + _GOHM_AMOUNT + ); + _assertApprovals(); + } + function test_consolidate_protocolFee() public givenAdminHasRole From 9f1ff4b28a8cbd45376ebebe74583458794ea0bd Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 28 Oct 2024 11:29:38 +0400 Subject: [PATCH 074/160] chore: linting --- src/test/policies/LoanConsolidatorFork.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index c33f39677..492c52205 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -1043,7 +1043,7 @@ contract LoanConsolidatorForkTest is Test { uint256[] memory idsA = _idsA(); // Record the initial debt balance - (uint256 totalPrincipal, uint256 totalInterest) = clearinghouse.getLoanForCollateral( + (uint256 totalPrincipal, ) = clearinghouse.getLoanForCollateral( _GOHM_AMOUNT ); From dcae82cba672057f454e10dae7ca33050394cbca Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 28 Oct 2024 11:46:18 +0400 Subject: [PATCH 075/160] Fix DAI-USDS migration logic --- src/policies/LoanConsolidator.sol | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index 511c596b9..004e193bb 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -342,15 +342,29 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent if (initiator_ != address(this)) revert OnlyThis(); // Assumptions: - // - The flashloan provider has transferred amount_ in DAI, which includes the principal and interest + // - The flashloan provider has transferred amount_ in DAI, which includes the principal + // - This contract has transferred from the caller the interest, lender fee and protocol fee to this contract - // If clearinghouseFrom is in USDS, then we need to convert the DAI to USDS in order to repay the principal and interest + // If clearinghouseFrom is in USDS, then we need to convert the flashloan DAI to USDS in order to repay the principal if ( flashLoanData.migrationType == MigrationType.USDS_DAI || flashLoanData.migrationType == MigrationType.USDS_USDS ) { - DAI.approve(address(MIGRATOR), flashLoanData.principal + flashLoanData.interest); - MIGRATOR.daiToUsds(address(this), flashLoanData.principal + flashLoanData.interest); + DAI.approve(address(MIGRATOR), flashLoanData.principal); + MIGRATOR.daiToUsds(address(this), flashLoanData.principal); + } + + // Ensure that the interest transferred from the caller is in terms of the reserveFrom token + // Fees are in terms of the reserveTo token + if ( + flashLoanData.migrationType == MigrationType.USDS_DAI + ) { + DAI.approve(address(MIGRATOR), flashLoanData.interest); + MIGRATOR.daiToUsds(address(this), flashLoanData.interest); + } + if (flashLoanData.migrationType == MigrationType.DAI_USDS) { + USDS.approve(address(MIGRATOR), flashLoanData.interest); + MIGRATOR.usdsToDai(address(this), flashLoanData.interest); } // Grant approval to the Cooler to spend the debt From 8d7997232271abba2ed98bb6fd5432c6070f49c9 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 28 Oct 2024 11:50:04 +0400 Subject: [PATCH 076/160] chore: linting --- src/policies/LoanConsolidator.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index 004e193bb..fab4e8571 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -356,9 +356,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent // Ensure that the interest transferred from the caller is in terms of the reserveFrom token // Fees are in terms of the reserveTo token - if ( - flashLoanData.migrationType == MigrationType.USDS_DAI - ) { + if (flashLoanData.migrationType == MigrationType.USDS_DAI) { DAI.approve(address(MIGRATOR), flashLoanData.interest); MIGRATOR.daiToUsds(address(this), flashLoanData.interest); } From 379000b15c804b01a595c57c577ca4a4b15e5a7c Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 28 Oct 2024 11:50:11 +0400 Subject: [PATCH 077/160] chore: linting --- src/test/policies/LoanConsolidatorFork.t.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index 492c52205..dd478e010 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -1043,9 +1043,7 @@ contract LoanConsolidatorForkTest is Test { uint256[] memory idsA = _idsA(); // Record the initial debt balance - (uint256 totalPrincipal, ) = clearinghouse.getLoanForCollateral( - _GOHM_AMOUNT - ); + (uint256 totalPrincipal, ) = clearinghouse.getLoanForCollateral(_GOHM_AMOUNT); // Record the amount of DAI in the wallet uint256 initPrincipal = dai.balanceOf(walletA); From 774d27783c1c8f7cf28673cc0dfeb58ed846b2a2 Mon Sep 17 00:00:00 2001 From: ohmzeus <93612359+chefomi@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:25:35 -0400 Subject: [PATCH 078/160] add func for transfer --- src/policies/LoanConsolidator.sol | 73 ++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index fab4e8571..113f6cd89 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -230,11 +230,14 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @notice Consolidate loans (taken with a single Cooler contract) into a single loan by using flashloans. /// + /// Unlike consolidateWithNewOwner, the owner of the new cooler must be the same as the cooler being repaid. + /// /// The caller will be required to provide additional funds to cover accrued interest on the Cooler loans and the lender and protocol fees (if applicable). Use the `requiredApprovals()` function to determine the amount of funds and approvals required. /// /// It is expected that the caller will have already provided approval for this contract to spend the required tokens. See `requiredApprovals()` for more details. /// /// @dev This function will revert if: + /// - The caller is not the 'coolerFrom' and 'coolerTo' owner. /// - The caller has not approved this contract to spend the fees in DAI. /// - The caller has not approved this contract to spend the reserve token of `clearinghouseTo_` in order to repay the flashloan. /// - The caller has not approved this contract to spend the gOHM escrowed by the target Cooler. @@ -246,7 +249,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// - The contract has not been activated as a policy. /// - Re-entrancy is detected. /// - /// For flexibility purposes, the user can either pay with DAI or sDAI. + /// For flexibility purposes, the user can either pay with DAI or sDAI. @dev still true? /// /// @param clearinghouseFrom_ Olympus Clearinghouse that issued the existing loans. /// @param clearinghouseTo_ Olympus Clearinghouse to be used to issue the consolidated loan. @@ -260,6 +263,66 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent address coolerTo_, uint256[] calldata ids_ ) public onlyPolicyActive onlyConsolidatorActive nonReentrant { + // Ensure `msg.sender` is allowed to spend cooler funds on behalf of this contract + if (Cooler(coolerFrom_).owner() != msg.sender || Cooler(coolerTo_).owner() != msg.sender) + revert OnlyCoolerOwner(); + _consolidateWithFlashLoan(clearinghouseFrom_, clearinghouseTo_, coolerFrom_, coolerTo_, ids_); + } + + /// @notice Consolidate loans (taken with a single Cooler contract) into a single loan by using flashloans. + /// + /// Unlike consolidateWithFlashLoan, the owner of the new cooler can be different from the cooler being repaid. + /// + /// The caller will be required to provide additional funds to cover accrued interest on the Cooler loans and the lender and protocol fees (if applicable). Use the `requiredApprovals()` function to determine the amount of funds and approvals required. + /// + /// It is expected that the caller will have already provided approval for this contract to spend the required tokens. See `requiredApprovals()` for more details. + /// + /// @dev This function will revert if: + /// - The caller is not the coolerFrom owner. + /// - The caller has not approved this contract to spend the fees in DAI. + /// - The caller has not approved this contract to spend the reserve token of `clearinghouseTo_` in order to repay the flashloan. + /// - The caller has not approved this contract to spend the gOHM escrowed by the target Cooler. + /// - `clearinghouseFrom_` or `clearinghouseTo_` is not registered with the Clearinghouse registry. + /// - `coolerFrom_` or `coolerTo_` is not a valid Cooler for the respective Clearinghouse. + /// - Less than two loans are being consolidated. @dev is this possible to do without? so it funcs as transfer? + /// - The available funds are less than the required flashloan amount. + /// - The contract is not active. + /// - The contract has not been activated as a policy. + /// - Re-entrancy is detected. + /// + /// For flexibility purposes, the user can either pay with DAI or sDAI. @dev still true? + /// + /// @param clearinghouseFrom_ Olympus Clearinghouse that issued the existing loans. + /// @param clearinghouseTo_ Olympus Clearinghouse to be used to issue the consolidated loan. + /// @param coolerFrom_ Cooler from which the loans will be consolidated. + /// @param coolerTo_ Cooler to which the loans will be consolidated + /// @param ids_ Array containing the ids of the loans to be consolidated. + function consolidateWithNewOwner( + address clearinghouseFrom_, + address clearinghouseTo_, + address coolerFrom_, + address coolerTo_, + uint256[] calldata ids_ + ) public onlyPolicyActive onlyConsolidatorActive nonReentrant { + // Ensure `msg.sender` is allowed to spend cooler funds on behalf of this contract + if (Cooler(coolerFrom_).owner() != msg.sender) + revert OnlyCoolerOwner(); + _consolidateWithFlashLoan(clearinghouseFrom_, clearinghouseTo_, coolerFrom_, coolerTo_, ids_); + } + + /// @notice Internal logic for loan consolidation + /// @dev Utilized by consolidateWithFlashLoan and consolidateWithNewOwner + /// @param clearinghouseFrom_ Olympus Clearinghouse that issued the existing loans. + /// @param clearinghouseTo_ Olympus Clearinghouse to be used to issue the consolidated loan. + /// @param coolerFrom_ Cooler from which the loans will be consolidated. + /// @param coolerTo_ Cooler to which the loans will be consolidated + /// @param ids_ Array containing the ids of the loans to be consolidated. + function _consolidateWithFlashLoan(address clearinghouseFrom_, + address clearinghouseTo_, + address coolerFrom_, + address coolerTo_, + uint256[] calldata ids_ + ) internal { // Validate that the Clearinghouses are registered with the Bophades kernel if (!_isValidClearinghouse(clearinghouseFrom_) || !_isValidClearinghouse(clearinghouseTo_)) revert Params_InvalidClearinghouse(); @@ -273,10 +336,6 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent // Ensure at least two loans are being consolidated if (ids_.length < 2) revert Params_InsufficientCoolerCount(); - // Ensure `msg.sender` is allowed to spend cooler funds on behalf of this contract - if (Cooler(coolerFrom_).owner() != msg.sender || Cooler(coolerTo_).owner() != msg.sender) - revert OnlyCoolerOwner(); - // Get the migration type and reserve tokens (MigrationType migrationType, IERC20 reserveFrom, IERC20 reserveTo) = _getMigrationType( clearinghouseFrom_, @@ -400,11 +459,11 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent // - reserveTo: no change, as the cooler owner received it // - gOHM: reduced by the collateral used for the consolidated loan. gOHM balance in this contract is now 0. - // The cooler owner will receive `principal` quantity of `reserveTo` tokens for the consolidated loan + // The coolerTo owner will receive `principal` quantity of `reserveTo` tokens for the consolidated loan // Transfer the amount of `reserveTo` required to repay the flash loan (debt + interest), lender fee and protocol fee // Approval must have already been granted by the Cooler owner flashLoanData.reserveTo.transferFrom( - flashLoanData.coolerFrom.owner(), + flashLoanData.coolerTo.owner(), address(this), flashLoanData.principal ); From 12ace5349ae0da08c93b7913c4cc7e475bdc5682 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 30 Oct 2024 11:33:05 +0400 Subject: [PATCH 079/160] Cleanup --- src/policies/LoanConsolidator.sol | 34 +++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index 113f6cd89..7bcc09213 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -240,7 +240,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// - The caller is not the 'coolerFrom' and 'coolerTo' owner. /// - The caller has not approved this contract to spend the fees in DAI. /// - The caller has not approved this contract to spend the reserve token of `clearinghouseTo_` in order to repay the flashloan. - /// - The caller has not approved this contract to spend the gOHM escrowed by the target Cooler. + /// - The caller has not approved this contract to spend the gOHM escrowed by `coolerFrom_`. /// - `clearinghouseFrom_` or `clearinghouseTo_` is not registered with the Clearinghouse registry. /// - `coolerFrom_` or `coolerTo_` is not a valid Cooler for the respective Clearinghouse. /// - Less than two loans are being consolidated. @@ -249,8 +249,6 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// - The contract has not been activated as a policy. /// - Re-entrancy is detected. /// - /// For flexibility purposes, the user can either pay with DAI or sDAI. @dev still true? - /// /// @param clearinghouseFrom_ Olympus Clearinghouse that issued the existing loans. /// @param clearinghouseTo_ Olympus Clearinghouse to be used to issue the consolidated loan. /// @param coolerFrom_ Cooler from which the loans will be consolidated. @@ -266,9 +264,18 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent // Ensure `msg.sender` is allowed to spend cooler funds on behalf of this contract if (Cooler(coolerFrom_).owner() != msg.sender || Cooler(coolerTo_).owner() != msg.sender) revert OnlyCoolerOwner(); - _consolidateWithFlashLoan(clearinghouseFrom_, clearinghouseTo_, coolerFrom_, coolerTo_, ids_); + + _consolidateWithFlashLoan( + clearinghouseFrom_, + clearinghouseTo_, + coolerFrom_, + coolerTo_, + ids_ + ); } + // TODO remove restriction on less than 2 loans + /// @notice Consolidate loans (taken with a single Cooler contract) into a single loan by using flashloans. /// /// Unlike consolidateWithFlashLoan, the owner of the new cooler can be different from the cooler being repaid. @@ -284,14 +291,12 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// - The caller has not approved this contract to spend the gOHM escrowed by the target Cooler. /// - `clearinghouseFrom_` or `clearinghouseTo_` is not registered with the Clearinghouse registry. /// - `coolerFrom_` or `coolerTo_` is not a valid Cooler for the respective Clearinghouse. - /// - Less than two loans are being consolidated. @dev is this possible to do without? so it funcs as transfer? + /// - Less than two loans are being consolidated. /// - The available funds are less than the required flashloan amount. /// - The contract is not active. /// - The contract has not been activated as a policy. /// - Re-entrancy is detected. /// - /// For flexibility purposes, the user can either pay with DAI or sDAI. @dev still true? - /// /// @param clearinghouseFrom_ Olympus Clearinghouse that issued the existing loans. /// @param clearinghouseTo_ Olympus Clearinghouse to be used to issue the consolidated loan. /// @param coolerFrom_ Cooler from which the loans will be consolidated. @@ -305,9 +310,15 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent uint256[] calldata ids_ ) public onlyPolicyActive onlyConsolidatorActive nonReentrant { // Ensure `msg.sender` is allowed to spend cooler funds on behalf of this contract - if (Cooler(coolerFrom_).owner() != msg.sender) - revert OnlyCoolerOwner(); - _consolidateWithFlashLoan(clearinghouseFrom_, clearinghouseTo_, coolerFrom_, coolerTo_, ids_); + if (Cooler(coolerFrom_).owner() != msg.sender) revert OnlyCoolerOwner(); + + _consolidateWithFlashLoan( + clearinghouseFrom_, + clearinghouseTo_, + coolerFrom_, + coolerTo_, + ids_ + ); } /// @notice Internal logic for loan consolidation @@ -317,7 +328,8 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @param coolerFrom_ Cooler from which the loans will be consolidated. /// @param coolerTo_ Cooler to which the loans will be consolidated /// @param ids_ Array containing the ids of the loans to be consolidated. - function _consolidateWithFlashLoan(address clearinghouseFrom_, + function _consolidateWithFlashLoan( + address clearinghouseFrom_, address clearinghouseTo_, address coolerFrom_, address coolerTo_, From ca620a7a990539becc215cc6969e99f4a9107ce2 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 30 Oct 2024 12:01:27 +0400 Subject: [PATCH 080/160] Fix issue with third-party cooler tests --- src/test/policies/LoanConsolidatorFork.t.sol | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index dd478e010..6d2c1a686 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -20,11 +20,13 @@ import {RolesAdmin} from "src/policies/RolesAdmin.sol"; import {TRSRYv1} from "src/modules/TRSRY/TRSRY.v1.sol"; import {CHREGv1} from "src/modules/CHREG/CHREG.v1.sol"; import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; +import {ClonesWithImmutableArgs} from "clones/ClonesWithImmutableArgs.sol"; import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; contract LoanConsolidatorForkTest is Test { using stdStorage for StdStorage; + using ClonesWithImmutableArgs for address; LoanConsolidator public utils; @@ -377,6 +379,18 @@ contract LoanConsolidatorForkTest is Test { console2.log("Loans 0, 1, 2 created for cooler:", address(cooler_)); } + /// @notice Creates a new Cooler clone + /// @dev Not that this will be regarded as a third-party Cooler, and rejected by LoanConsolidator, as CoolerFactory has no record of it. + function _cloneCooler( + address owner_, + address collateral_, + address debt_, + address factory_ + ) internal returns (Cooler) { + bytes memory coolerData = abi.encodePacked(owner_, collateral_, debt_, factory_); + return Cooler(address(coolerFactory.coolerImplementation()).clone(coolerData)); + } + // ===== ASSERTIONS ===== // function _assertCoolerLoans(uint256 collateral_) internal { @@ -738,7 +752,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_thirdPartyCoolerFrom_reverts() public givenPolicyActive { // Create a new Cooler // It was not created by the Clearinghouse's CoolerFactory, so should be rejected - Cooler newCooler = new Cooler(); + Cooler newCooler = _cloneCooler(walletA, address(gohm), address(dai), address(coolerFactory)); // Expect revert vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.Params_InvalidCooler.selector)); @@ -758,7 +772,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_thirdPartyCoolerTo_reverts() public givenPolicyActive { // Create a new Cooler // It was not created by the Clearinghouse's CoolerFactory, so should be rejected - Cooler newCooler = new Cooler(); + Cooler newCooler = _cloneCooler(walletA, address(gohm), address(dai), address(coolerFactory)); // Expect revert vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.Params_InvalidCooler.selector)); From 3b552f6746aede02f2ef2a4c35c4941dfa16a9f1 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 30 Oct 2024 12:09:10 +0400 Subject: [PATCH 081/160] Enable 1 loan to be consolidated across different Coolers. More thorough tests. --- src/policies/LoanConsolidator.sol | 14 +- src/test/policies/LoanConsolidatorFork.t.sol | 131 +++++++++++++++++-- 2 files changed, 129 insertions(+), 16 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index 7bcc09213..46e22fe20 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -243,7 +243,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// - The caller has not approved this contract to spend the gOHM escrowed by `coolerFrom_`. /// - `clearinghouseFrom_` or `clearinghouseTo_` is not registered with the Clearinghouse registry. /// - `coolerFrom_` or `coolerTo_` is not a valid Cooler for the respective Clearinghouse. - /// - Less than two loans are being consolidated. + /// - Consolidation is taking place within the same Cooler, and less than two loans are being consolidated. /// - The available funds are less than the required flashloan amount. /// - The contract is not active. /// - The contract has not been activated as a policy. @@ -274,8 +274,6 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent ); } - // TODO remove restriction on less than 2 loans - /// @notice Consolidate loans (taken with a single Cooler contract) into a single loan by using flashloans. /// /// Unlike consolidateWithFlashLoan, the owner of the new cooler can be different from the cooler being repaid. @@ -291,7 +289,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// - The caller has not approved this contract to spend the gOHM escrowed by the target Cooler. /// - `clearinghouseFrom_` or `clearinghouseTo_` is not registered with the Clearinghouse registry. /// - `coolerFrom_` or `coolerTo_` is not a valid Cooler for the respective Clearinghouse. - /// - Less than two loans are being consolidated. + /// - Consolidation is taking place within the same Cooler, and less than two loans are being consolidated. /// - The available funds are less than the required flashloan amount. /// - The contract is not active. /// - The contract has not been activated as a policy. @@ -323,6 +321,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @notice Internal logic for loan consolidation /// @dev Utilized by consolidateWithFlashLoan and consolidateWithNewOwner + /// /// @param clearinghouseFrom_ Olympus Clearinghouse that issued the existing loans. /// @param clearinghouseTo_ Olympus Clearinghouse to be used to issue the consolidated loan. /// @param coolerFrom_ Cooler from which the loans will be consolidated. @@ -345,8 +344,11 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent !_isValidCooler(clearinghouseTo_, coolerTo_) ) revert Params_InvalidCooler(); - // Ensure at least two loans are being consolidated - if (ids_.length < 2) revert Params_InsufficientCoolerCount(); + // If consolidating within the same Cooler, ensure that at least two loans are being consolidated + if (coolerFrom_ == coolerTo_ && ids_.length < 2) revert Params_InsufficientCoolerCount(); + + // If consolidating across different Coolers, ensure that at least one loan is being consolidated + if (coolerFrom_ != coolerTo_ && ids_.length == 0) revert Params_InsufficientCoolerCount(); // Get the migration type and reserve tokens (MigrationType migrationType, IERC20 reserveFrom, IERC20 reserveTo) = _getMigrationType( diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index 6d2c1a686..91d1ae911 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -627,18 +627,24 @@ contract LoanConsolidatorForkTest is Test { // [X] it reverts // given clearinghouseTo is not registered with CHREG // [X] it reverts - // given coolerFrom was not created by a valid CoolerFactory + // given coolerFrom was not created by clearinghouseFrom's CoolerFactory // [X] it reverts - // given coolerTo was not created by a valid CoolerFactory - // [X] it reverts - // given the caller has no loans - // [X] it reverts - // given the caller has 1 loan + // given coolerTo was not created by clearinghouseTo's CoolerFactory // [X] it reverts // given the caller is not the owner of coolerFrom // [X] it reverts // given the caller is not the owner of coolerTo // [X] it reverts + // given coolerFrom is equal to coolerTo + // given the cooler has no loans + // [X] it reverts + // given the cooler has 1 loan + // [X] it reverts + // given coolerFrom is not equal to coolerTo + // given the cooler has no loans + // [X] it reverts + // given the cooler has 1 loan + // [X] it migrates the loan to coolerTo // given reserveTo is DAI // given DAI spending approval has not been given to LoanConsolidator // [X] it reverts @@ -752,7 +758,12 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_thirdPartyCoolerFrom_reverts() public givenPolicyActive { // Create a new Cooler // It was not created by the Clearinghouse's CoolerFactory, so should be rejected - Cooler newCooler = _cloneCooler(walletA, address(gohm), address(dai), address(coolerFactory)); + Cooler newCooler = _cloneCooler( + walletA, + address(gohm), + address(dai), + address(coolerFactory) + ); // Expect revert vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.Params_InvalidCooler.selector)); @@ -772,7 +783,12 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_thirdPartyCoolerTo_reverts() public givenPolicyActive { // Create a new Cooler // It was not created by the Clearinghouse's CoolerFactory, so should be rejected - Cooler newCooler = _cloneCooler(walletA, address(gohm), address(dai), address(coolerFactory)); + Cooler newCooler = _cloneCooler( + walletA, + address(gohm), + address(dai), + address(coolerFactory) + ); // Expect revert vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.Params_InvalidCooler.selector)); @@ -789,7 +805,7 @@ contract LoanConsolidatorForkTest is Test { ); } - function test_consolidate_noLoans_reverts() public givenPolicyActive { + function test_consolidate_sameCooler_noLoans_reverts() public givenPolicyActive { // Grant approvals _grantCallerApprovals(type(uint256).max, type(uint256).max, type(uint256).max); @@ -803,7 +819,7 @@ contract LoanConsolidatorForkTest is Test { _consolidate(ids); } - function test_consolidate_oneLoan_reverts() public givenPolicyActive { + function test_consolidate_sameCooler_oneLoan_reverts() public givenPolicyActive { // Grant approvals _grantCallerApprovals(type(uint256).max, type(uint256).max, type(uint256).max); @@ -818,6 +834,101 @@ contract LoanConsolidatorForkTest is Test { _consolidate(ids); } + function test_consolidate_differentCooler_noLoans_reverts() public givenPolicyActive { + uint256[] memory idsA = _idsA(); + + // Deploy a Cooler on the USDS Clearinghouse + vm.startPrank(walletA); + address coolerUsds_ = coolerFactory.generateCooler(gohm, usds); + Cooler coolerUsds = Cooler(coolerUsds_); + vm.stopPrank(); + + (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( + address(clearinghouse), + address(coolerA), + idsA + ); + + // Grant approvals + _grantCallerApprovals(type(uint256).max, type(uint256).max, type(uint256).max); + + // Deal fees in USDS to the wallet + deal(address(usds), walletA, interest + protocolFee); + // Make sure the wallet has no DAI + deal(address(dai), walletA, 0); + + // Expect revert since no loan ids are given + vm.expectRevert( + abi.encodeWithSelector(LoanConsolidator.Params_InsufficientCoolerCount.selector) + ); + + // Consolidate loans, but give no ids + idsA = new uint256[](0); + _consolidate( + walletA, + address(clearinghouse), + address(clearinghouseUsds), + address(coolerA), + address(coolerUsds), + idsA + ); + } + + function test_consolidate_differentCooler_oneLoan() public givenPolicyActive { + uint256[] memory idsA = _idsA(); + + // Deploy a Cooler on the USDS Clearinghouse + vm.startPrank(walletA); + address coolerUsds_ = coolerFactory.generateCooler(gohm, usds); + Cooler coolerUsds = Cooler(coolerUsds_); + vm.stopPrank(); + + (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( + address(clearinghouse), + address(coolerA), + idsA + ); + + // Grant approvals + _grantCallerApprovals(type(uint256).max, type(uint256).max, type(uint256).max); + + // Deal fees in USDS to the wallet + deal(address(usds), walletA, interest + protocolFee); + // Make sure the wallet has no DAI + deal(address(dai), walletA, 0); + + // Get the loan principal before consolidation + Cooler.Loan memory loanZero = coolerA.getLoan(0); + Cooler.Loan memory loanOne = coolerA.getLoan(1); + Cooler.Loan memory loanTwo = coolerA.getLoan(2); + + // Consolidate loans, but give only one id + idsA = new uint256[](1); + idsA[0] = 0; + _consolidate( + walletA, + address(clearinghouse), + address(clearinghouseUsds), + address(coolerA), + address(coolerUsds), + idsA + ); + + // Assert that only loan 0 has been repaid + assertEq(coolerA.getLoan(0).principal, 0, "cooler DAI, loan 0: principal"); + assertEq(coolerA.getLoan(1).principal, loanOne.principal, "cooler DAI, loan 1: principal"); + assertEq(coolerA.getLoan(2).principal, loanTwo.principal, "cooler DAI, loan 2: principal"); + // Assert that loan 0 has been migrated to coolerUsds + assertEq( + coolerUsds.getLoan(0).principal, + loanZero.principal, + "cooler USDS, loan 0: principal" + ); + // Assert that coolerUsds has no other loans + vm.expectRevert(); + coolerUsds.getLoan(1); + } + function test_consolidate_callerNotOwner_coolerFrom_reverts() public givenPolicyActive { uint256[] memory idsA = _idsA(); From b832c1a0ff71993516547c15fbad6ad9475b47f9 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 30 Oct 2024 12:13:57 +0400 Subject: [PATCH 082/160] Rename consolidateWithFlashLoan -> consolidate --- src/policies/LoanConsolidator.sol | 8 ++++---- src/scripts/ops/LoanConsolidator.s.sol | 8 +------- src/test/policies/LoanConsolidatorFork.t.sol | 8 +------- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index 46e22fe20..a3c38a243 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -230,7 +230,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @notice Consolidate loans (taken with a single Cooler contract) into a single loan by using flashloans. /// - /// Unlike consolidateWithNewOwner, the owner of the new cooler must be the same as the cooler being repaid. + /// Unlike `consolidateWithNewOwner()`, the owner of the new Cooler must be the same as the Cooler being repaid. /// /// The caller will be required to provide additional funds to cover accrued interest on the Cooler loans and the lender and protocol fees (if applicable). Use the `requiredApprovals()` function to determine the amount of funds and approvals required. /// @@ -254,7 +254,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @param coolerFrom_ Cooler from which the loans will be consolidated. /// @param coolerTo_ Cooler to which the loans will be consolidated /// @param ids_ Array containing the ids of the loans to be consolidated. - function consolidateWithFlashLoan( + function consolidate( address clearinghouseFrom_, address clearinghouseTo_, address coolerFrom_, @@ -276,7 +276,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @notice Consolidate loans (taken with a single Cooler contract) into a single loan by using flashloans. /// - /// Unlike consolidateWithFlashLoan, the owner of the new cooler can be different from the cooler being repaid. + /// Unlike `consolidate()`, the owner of the new Cooler can be different from the Cooler being repaid. /// /// The caller will be required to provide additional funds to cover accrued interest on the Cooler loans and the lender and protocol fees (if applicable). Use the `requiredApprovals()` function to determine the amount of funds and approvals required. /// @@ -320,7 +320,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent } /// @notice Internal logic for loan consolidation - /// @dev Utilized by consolidateWithFlashLoan and consolidateWithNewOwner + /// @dev Utilized by `consolidate()` and `consolidateWithNewOwner()` /// /// @param clearinghouseFrom_ Olympus Clearinghouse that issued the existing loans. /// @param clearinghouseTo_ Olympus Clearinghouse to be used to issue the consolidated loan. diff --git a/src/scripts/ops/LoanConsolidator.s.sol b/src/scripts/ops/LoanConsolidator.s.sol index 1684a74d5..d6934a9e2 100644 --- a/src/scripts/ops/LoanConsolidator.s.sol +++ b/src/scripts/ops/LoanConsolidator.s.sol @@ -98,13 +98,7 @@ contract LoanConsolidatorScript is Test { // Consolidate the loans vm.startPrank(owner_); - utils.consolidateWithFlashLoan( - clearinghouseFrom_, - clearinghouseTo_, - coolerFrom_, - coolerTo_, - loanIds_ - ); + utils.consolidate(clearinghouseFrom_, clearinghouseTo_, coolerFrom_, coolerTo_, loanIds_); vm.stopPrank(); console2.log("gOHM balance after:", _gohm.balanceOf(owner_)); diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index 91d1ae911..d71aa41ab 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -262,13 +262,7 @@ contract LoanConsolidatorForkTest is Test { uint256[] memory ids_ ) internal { vm.prank(caller_); - utils.consolidateWithFlashLoan( - clearinghouseFrom_, - clearinghouseTo_, - coolerFrom_, - coolerTo_, - ids_ - ); + utils.consolidate(clearinghouseFrom_, clearinghouseTo_, coolerFrom_, coolerTo_, ids_); } function _consolidate(uint256[] memory ids_) internal { From 52aec52d54e8e698055959d8ffdddbdad219c5f4 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 30 Oct 2024 12:18:27 +0400 Subject: [PATCH 083/160] Cross-port Clearinghouse cosmetic changes from PR #424 --- src/policies/Clearinghouse.sol | 68 ++++++++++++++------------- src/proposals/OIP_XXX.sol | 2 +- src/scripts/deploy/DeployV2.sol | 2 +- src/test/policies/Clearinghouse.t.sol | 12 ++--- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/policies/Clearinghouse.sol b/src/policies/Clearinghouse.sol index 1932636ef..72345a8e0 100644 --- a/src/policies/Clearinghouse.sol +++ b/src/policies/Clearinghouse.sol @@ -48,7 +48,7 @@ contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { // --- RELEVANT CONTRACTS ---------------------------------------- ERC20 public immutable reserve; // Debt token - ERC4626 public immutable wrappedReserve; // Idle reserve will be wrapped into wrappedReserve + ERC4626 public immutable sReserve; // Idle reserve will be wrapped into sReserve ERC20 public immutable gohm; // Collateral token ERC20 public immutable ohm; // Unwrapped gOHM IStaking public immutable staking; // Necessary to unstake (and burn) OHM from defaults @@ -88,7 +88,7 @@ contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { address ohm_, address gohm_, address staking_, - address wrappedReserve_, + address sReserve_, address coolerFactory_, address kernel_ ) Policy(Kernel(kernel_)) CoolerCallback(coolerFactory_) { @@ -96,8 +96,8 @@ contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { ohm = ERC20(ohm_); gohm = ERC20(gohm_); staking = IStaking(staking_); - wrappedReserve = ERC4626(wrappedReserve_); - reserve = ERC20(wrappedReserve.asset()); + sReserve = ERC4626(sReserve_); + reserve = ERC20(sReserve.asset()); } /// @notice Default framework setup. Configure dependencies for olympus-v3 modules. @@ -147,11 +147,12 @@ contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { requests[5] = Permissions(TRSRY_KEYCODE, TRSRY.withdrawReserves.selector); } - /// @notice Returns the current version of the policy - /// @dev This is useful for distinguishing between different versions of the policy + /// @notice Returns the version of the policy. + /// + /// @return major The major version of the policy. + /// @return minor The minor version of the policy. function VERSION() external pure returns (uint8 major, uint8 minor) { - major = 1; - minor = 2; + return (1, 2); } // --- OPERATION ------------------------------------------------- @@ -186,7 +187,7 @@ contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { uint256 reqID = cooler_.requestLoan(amount_, INTEREST_RATE, LOAN_TO_COLLATERAL, DURATION); // Clear the created loan request by providing enough reserve. - wrappedReserve.withdraw(amount_, address(this), address(this)); + sReserve.withdraw(amount_, address(this), address(this)); reserve.approve(address(cooler_), amount_); uint256 loanID = cooler_.clearRequest(reqID, address(this), true); @@ -211,7 +212,7 @@ contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { // Transfer in extension interest from the caller. reserve.transferFrom(msg.sender, address(this), interestBase * times_); if (active) { - _sweepIntoDSR(interestBase * times_); + _sweepIntoSavingsVault(interestBase * times_); } else { _defund(reserve, interestBase * times_); } @@ -291,11 +292,12 @@ contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { // --- CALLBACKS ----------------------------------------------------- /// @notice Overridden callback to decrement loan receivables. + /// @param *unused loadID_ of the load. /// @param principalPaid_ in reserve. /// @param interestPaid_ in reserve. function _onRepay(uint256, uint256 principalPaid_, uint256 interestPaid_) internal override { if (active) { - _sweepIntoDSR(principalPaid_ + interestPaid_); + _sweepIntoSavingsVault(principalPaid_ + interestPaid_); } else { _defund(reserve, principalPaid_ + interestPaid_); } @@ -331,9 +333,9 @@ contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { // Sweep reserve into DSR if necessary. uint256 idle = reserve.balanceOf(address(this)); - if (idle != 0) _sweepIntoDSR(idle); + if (idle != 0) _sweepIntoSavingsVault(idle); - uint256 reserveBalance = wrappedReserve.maxWithdraw(address(this)); + uint256 reserveBalance = sReserve.maxWithdraw(address(this)); uint256 outstandingDebt = TRSRY.reserveDebt(reserve, address(this)); // Rebalance funds on hand with treasury's reserves. if (reserveBalance < maxFundAmount) { @@ -346,11 +348,11 @@ contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { amount_: outstandingDebt + fundAmount }); - // Since TRSRY holds wrappedReserve, a conversion must be done before + // Since TRSRY holds sReserve, a conversion must be done before // funding the clearinghouse. - uint256 wrappedReserveAmount = wrappedReserve.previewWithdraw(fundAmount); - TRSRY.increaseWithdrawApproval(address(this), wrappedReserve, wrappedReserveAmount); - TRSRY.withdrawReserves(address(this), wrappedReserve, wrappedReserveAmount); + uint256 sReserveAmount = sReserve.previewWithdraw(fundAmount); + TRSRY.increaseWithdrawApproval(address(this), sReserve, sReserveAmount); + TRSRY.withdrawReserves(address(this), sReserve, sReserveAmount); // Log the event. emit Rebalance(false, fundAmount); @@ -364,10 +366,10 @@ contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { amount_: (outstandingDebt > defundAmount) ? outstandingDebt - defundAmount : 0 }); - // Since TRSRY holds wrappedReserve, a conversion must be done before - // sending wrappedReserve back. - uint256 wrappedReserveAmount = wrappedReserve.previewWithdraw(defundAmount); - wrappedReserve.transfer(address(TRSRY), wrappedReserveAmount); + // Since TRSRY holds sReserve, a conversion must be done before + // sending sReserve back. + uint256 sReserveAmount = sReserve.previewWithdraw(defundAmount); + sReserve.transfer(address(TRSRY), sReserveAmount); // Log the event. emit Rebalance(true, defundAmount); @@ -376,16 +378,16 @@ contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { return true; } - /// @notice Sweep excess reserve into vault. - function sweepIntoDSR() public { + /// @notice Sweep excess reserve into savings vault. + function sweepIntoSavingsVault() public { uint256 reserveBalance = reserve.balanceOf(address(this)); - _sweepIntoDSR(reserveBalance); + _sweepIntoSavingsVault(reserveBalance); } /// @notice Sweep excess reserve into vault. - function _sweepIntoDSR(uint256 amount_) internal { - reserve.approve(address(wrappedReserve), amount_); - wrappedReserve.deposit(amount_, address(this)); + function _sweepIntoSavingsVault(uint256 amount_) internal { + reserve.approve(address(sReserve), amount_); + sReserve.deposit(amount_, address(this)); } /// @notice Public function to burn gOHM. @@ -414,9 +416,9 @@ contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { function emergencyShutdown() external onlyRole("emergency_shutdown") { active = false; - // If necessary, defund wrappedReserve. - uint256 wrappedReserveBalance = wrappedReserve.balanceOf(address(this)); - if (wrappedReserveBalance != 0) _defund(wrappedReserve, wrappedReserveBalance); + // If necessary, defund sReserve. + uint256 sReserveBalance = sReserve.balanceOf(address(this)); + if (sReserveBalance != 0) _defund(sReserve, sReserveBalance); // If necessary, defund reserve. uint256 reserveBalance = reserve.balanceOf(address(this)); @@ -440,12 +442,12 @@ contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { /// @param token_ to transfer. /// @param amount_ to transfer. function _defund(ERC20 token_, uint256 amount_) internal { - if (token_ == wrappedReserve || token_ == reserve) { + if (token_ == sReserve || token_ == reserve) { // Since users loans are denominated in reserve, the clearinghouse // debt is set in reserve terms. It must be adjusted when defunding. uint256 outstandingDebt = TRSRY.reserveDebt(reserve, address(this)); - uint256 reserveAmount = (token_ == wrappedReserve) - ? wrappedReserve.previewRedeem(amount_) + uint256 reserveAmount = (token_ == sReserve) + ? sReserve.previewRedeem(amount_) : amount_; TRSRY.setDebt({ diff --git a/src/proposals/OIP_XXX.sol b/src/proposals/OIP_XXX.sol index e42b87da3..ddda9d050 100644 --- a/src/proposals/OIP_XXX.sol +++ b/src/proposals/OIP_XXX.sol @@ -54,7 +54,7 @@ contract OIP_XXX is GovernorBravoProposal { ohm_: addresses.getAddress("olympus-legacy-ohm"), gohm_: addresses.getAddress("olympus-legacy-gohm"), staking_: addresses.getAddress("olympus-legacy-staking"), - wrappedReserve_: addresses.getAddress("external-tokens-sdai"), + sReserve_: addresses.getAddress("external-tokens-sdai"), coolerFactory_: addresses.getAddress("external-coolers-factory"), kernel_: address(_kernel) }); diff --git a/src/scripts/deploy/DeployV2.sol b/src/scripts/deploy/DeployV2.sol index 6b695ad24..d998e4202 100644 --- a/src/scripts/deploy/DeployV2.sol +++ b/src/scripts/deploy/DeployV2.sol @@ -1021,7 +1021,7 @@ contract OlympusDeploy is Script { ohm_: address(ohm), gohm_: address(gohm), staking_: address(staking), - wrappedReserve_: address(wrappedReserve), + sReserve_: address(wrappedReserve), coolerFactory_: address(coolerFactory), kernel_: address(kernel) }); diff --git a/src/test/policies/Clearinghouse.t.sol b/src/test/policies/Clearinghouse.t.sol index 317e8011f..4b8ec93a8 100644 --- a/src/test/policies/Clearinghouse.t.sol +++ b/src/test/policies/Clearinghouse.t.sol @@ -33,7 +33,7 @@ import {Clearinghouse, Cooler, CoolerFactory, CoolerCallback} from "policies/Cle // [X] Treasury approvals for the clearing house are correct. // [X] if necessary, sends excess DSR funds back to the Treasury. // [X] if a rebalances are missed, can execute several rebalances if FUND_CADENCE allows it. -// [X] sweepIntoDSR +// [X] sweepIntoSavingsVault // [X] excess DAI is deposited into DSR. // [X] defund // [X] only "cooler_overseer" can call. @@ -48,8 +48,8 @@ import {Clearinghouse, Cooler, CoolerFactory, CoolerCallback} from "policies/Cle // [X] lendToCooler // [X] only lend to coolers issued by coolerFactory. // [X] only collateral = gOHM + only debt = DAI. -// [x] user and cooler new gOHM balances are correct. -// [x] user and cooler new DAI balances are correct. +// [X] user and cooler new gOHM balances are correct. +// [X] user and cooler new DAI balances are correct. // [X] extendLoan // [X] only roll coolers issued by coolerFactory. // [X] roll by adding more collateral. @@ -524,7 +524,7 @@ contract ClearinghouseTest is Test { // Mint 1 million to clearinghouse and sweep to simulate assets being repaid dai.mint(address(clearinghouse), oneMillion); - clearinghouse.sweepIntoDSR(); + clearinghouse.sweepIntoSavingsVault(); assertEq( sdai.maxWithdraw(address(clearinghouse)), @@ -604,12 +604,12 @@ contract ClearinghouseTest is Test { // --- SWEEP INTO DSR ------------------------------------------------ - function test_sweepIntoDSR() public { + function test_sweepIntoSavingsVault() public { uint256 sdaiBal = sdai.balanceOf(address(clearinghouse)); // Mint 1 million to clearinghouse and sweep to simulate assets being repaid dai.mint(address(clearinghouse), 1e24); - clearinghouse.sweepIntoDSR(); + clearinghouse.sweepIntoSavingsVault(); assertEq(sdai.balanceOf(address(clearinghouse)), sdaiBal + 1e24); } From 6d8223378b4ac5d3ec4ba2147b5741d21e480821 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 30 Oct 2024 12:30:42 +0400 Subject: [PATCH 084/160] Cleanup test TODOs --- src/test/policies/LoanConsolidatorFork.t.sol | 25 ++++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index d71aa41ab..c3a0ecc58 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -612,7 +612,7 @@ contract LoanConsolidatorForkTest is Test { // ===== TESTS ===== // - // consolidateWithFlashLoan + // consolidate // given the contract has not been activated as a policy // [X] it reverts // given the contract has been disabled @@ -629,6 +629,8 @@ contract LoanConsolidatorForkTest is Test { // [X] it reverts // given the caller is not the owner of coolerTo // [X] it reverts + // given clearinghouseTo is disabled + // [X] it reverts // given coolerFrom is equal to coolerTo // given the cooler has no loans // [X] it reverts @@ -651,19 +653,22 @@ contract LoanConsolidatorForkTest is Test { // [X] it transfers the protocol fee to the collector // given the lender fee is non-zero // [X] it transfers the lender fee to the lender - // when the protocol fee is zero - // [X] it succeeds, but does not transfer additional DAI for the fee - // when the Clearinghouse is disabled - // [X] it reverts + // given the protocol fee is zero + // [X] it succeeds, but does not transfer additional reserveTo for the protocol fee + // given the lender fee is zero + // [X] it succeeds, but does not transfer additional reserveTo for the lender fee // when clearinghouseFrom is DAI and clearinghouseTo is USDS - // [X] the Cooler owner receives USDS + // [X] the loans on coolerFrom are migrated to coolerTo + // [X] the Cooler owner receives USDS from the new loan // when clearinghouseFrom is USDS and clearinghouseTo is DAI - // [X] the Cooler owner receives DAI + // [X] the loans on coolerFrom are migrated to coolerTo + // [X] the Cooler owner receives DAI from the new loan // when clearinghouseFrom is USDS and clearinghouseTo is USDS - // [X] the Cooler owner receives USDS + // [X] the loans on coolerFrom are migrated to coolerTo + // [X] the Cooler owner receives USDS from the new loan // when clearinghouseFrom is DAI and clearinghouseTo is DAI - // [X] the Cooler owner receives DAI - // [X] it takes a flashloan for the total debt amount + LoanConsolidator fee, and consolidates the loans into one + // [X] the loans on coolerFrom are migrated to coolerTo + // [X] the Cooler owner receives DAI from the new loan // --- consolidateWithFlashLoan -------------------------------------------- From 0915c1655c0abddc13056ed651a49ecaebe50fe0 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 30 Oct 2024 15:38:09 +0400 Subject: [PATCH 085/160] Add tests for consolidateWithNewOwner(). Add additional checks to the implementation. --- src/policies/LoanConsolidator.sol | 15 +- src/test/policies/LoanConsolidatorFork.t.sol | 1275 +++++++++++++++--- 2 files changed, 1114 insertions(+), 176 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index a3c38a243..fe96792ec 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -283,7 +283,9 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// It is expected that the caller will have already provided approval for this contract to spend the required tokens. See `requiredApprovals()` for more details. /// /// @dev This function will revert if: - /// - The caller is not the coolerFrom owner. + /// - The caller is not the `coolerFrom_` owner. + /// - `coolerFrom_` is the same as `coolerTo_` (in which case `consolidate()` should be used). + /// - The owner of `coolerFrom_` is the same as `coolerTo_` (in which case `consolidate()` should be used). /// - The caller has not approved this contract to spend the fees in DAI. /// - The caller has not approved this contract to spend the reserve token of `clearinghouseTo_` in order to repay the flashloan. /// - The caller has not approved this contract to spend the gOHM escrowed by the target Cooler. @@ -310,6 +312,12 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent // Ensure `msg.sender` is allowed to spend cooler funds on behalf of this contract if (Cooler(coolerFrom_).owner() != msg.sender) revert OnlyCoolerOwner(); + // Ensure that the caller is not trying to operate on the same Cooler + if (coolerFrom_ == coolerTo_) revert Params_InvalidCooler(); + + // Ensure that the owner of the coolerFrom_ is not the same as coolerTo_ + if (Cooler(coolerFrom_).owner() == Cooler(coolerTo_).owner()) revert Params_InvalidCooler(); + _consolidateWithFlashLoan( clearinghouseFrom_, clearinghouseTo_, @@ -322,6 +330,9 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @notice Internal logic for loan consolidation /// @dev Utilized by `consolidate()` and `consolidateWithNewOwner()` /// + /// This function assumes: + /// - The calling external-facing function has checked that the caller is permitted to operate on `coolerFrom_`. + /// /// @param clearinghouseFrom_ Olympus Clearinghouse that issued the existing loans. /// @param clearinghouseTo_ Olympus Clearinghouse to be used to issue the consolidated loan. /// @param coolerFrom_ Cooler from which the loans will be consolidated. @@ -787,8 +798,6 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent address coolerFrom_, uint256[] calldata ids_ ) external view onlyPolicyActive returns (address, uint256, address, uint256, uint256) { - if (ids_.length < 2) revert Params_InsufficientCoolerCount(); - // Cache the total principal and interest (uint256 totalPrincipal, uint256 totalInterest) = _getDebtForLoans( address(coolerFrom_), diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index c3a0ecc58..3ad096ac7 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -56,7 +56,9 @@ contract LoanConsolidatorForkTest is Test { address public kernelExecutor; address public walletA; + address public walletB; Cooler public coolerA; + Cooler public coolerB; uint256 internal constant _GOHM_AMOUNT = 3_333 * 1e18; uint256 internal constant _ONE_HUNDRED_PERCENT = 100e2; @@ -167,6 +169,7 @@ contract LoanConsolidatorForkTest is Test { utils = new LoanConsolidator(address(kernel), 0); walletA = vm.addr(0xA); + walletB = vm.addr(0xB); // Fund wallets with gOHM deal(address(gohm), walletA, _GOHM_AMOUNT); @@ -178,7 +181,7 @@ contract LoanConsolidatorForkTest is Test { deal(address(usds), address(clearinghouseUsds), 18_000_000 * 1e18); deal(address(susds), address(clearinghouseUsds), 18_000_000 * 1e18); - _createCoolers(clearinghouse, coolerFactory, walletA, dai); + _createCoolerAndLoans(clearinghouse, coolerFactory, walletA, dai); } // ===== MODIFIERS ===== // @@ -214,31 +217,52 @@ contract LoanConsolidatorForkTest is Test { } function _grantCallerApprovals( - address owner_, + address caller_, address clearinghouseTo_, + address coolerFrom_, + address coolerTo_, uint256[] memory ids_ ) internal { - // Will revert if there are less than 2 loans - if (ids_.length < 2) { - return; - } - ( , uint256 gohmApproval, address reserveTo, uint256 ownerReserveTo, uint256 callerReserveTwo - ) = utils.requiredApprovals(clearinghouseTo_, address(coolerA), ids_); + ) = utils.requiredApprovals(clearinghouseTo_, coolerFrom_, ids_); - vm.startPrank(owner_); - ERC20(reserveTo).approve(address(utils), ownerReserveTo + callerReserveTwo); - gohm.approve(address(utils), gohmApproval); - vm.stopPrank(); + // Determine the owner of coolerTo_ + address coolerToOwner = Cooler(coolerTo_).owner(); + bool coolerToOwnerIsCaller = coolerToOwner == caller_; + + // If the owner of the coolers is the same, then the caller can approve the entire amount + if (coolerToOwnerIsCaller) { + vm.startPrank(caller_); + ERC20(reserveTo).approve(address(utils), ownerReserveTo + callerReserveTwo); + gohm.approve(address(utils), gohmApproval); + vm.stopPrank(); + } + // Otherwise two different approvals are needed + else { + vm.startPrank(caller_); + ERC20(reserveTo).approve(address(utils), callerReserveTwo); + gohm.approve(address(utils), gohmApproval); + vm.stopPrank(); + + vm.startPrank(coolerToOwner); + ERC20(reserveTo).approve(address(utils), ownerReserveTo); + vm.stopPrank(); + } } function _grantCallerApprovals(uint256[] memory ids_) internal { - _grantCallerApprovals(walletA, address(clearinghouse), ids_); + _grantCallerApprovals( + walletA, + address(clearinghouse), + address(coolerA), + address(coolerA), + ids_ + ); } function _grantCallerApprovals( @@ -276,6 +300,24 @@ contract LoanConsolidatorForkTest is Test { ); } + function _consolidateWithNewOwner( + address caller_, + address clearinghouseFrom_, + address clearinghouseTo_, + address coolerFrom_, + address coolerTo_, + uint256[] memory ids_ + ) internal { + vm.prank(caller_); + utils.consolidateWithNewOwner( + clearinghouseFrom_, + clearinghouseTo_, + coolerFrom_, + coolerTo_, + ids_ + ); + } + function _getInterestDue( address cooler_, uint256[] memory ids_ @@ -338,41 +380,61 @@ contract LoanConsolidatorForkTest is Test { ERC20 token_ ) internal returns (address) { console2.log("Creating cooler..."); - console2.log("token:", address(token_)); - console2.log("wallet:", wallet_); + + if (address(token_) == address(dai)) { + console2.log("token: DAI"); + } else if (address(token_) == address(usds)) { + console2.log("token: USDS"); + } else { + console2.log("token: ", address(token_)); + } + + if (wallet_ == walletA) { + console2.log("wallet: A"); + } else if (wallet_ == walletB) { + console2.log("wallet: B"); + } else { + console2.log("wallet: ", wallet_); + } vm.startPrank(wallet_); address cooler_ = coolerFactory_.generateCooler(gohm, token_); vm.stopPrank(); + console2.log("Cooler created:", cooler_); + return cooler_; } - function _createCoolers( - Clearinghouse clearinghouse_, - CoolerFactory coolerFactory_, - address wallet_, - ERC20 token_ - ) internal { - address cooler_ = _createCooler(coolerFactory_, wallet_, token_); - coolerA = Cooler(cooler_); - + function _createLoans(Clearinghouse clearinghouse_, Cooler cooler_, address wallet_) internal { vm.startPrank(wallet_); // Approve clearinghouse to spend gOHM gohm.approve(address(clearinghouse_), _GOHM_AMOUNT); - // Loan 0 for coolerA (collateral: 2,000 gOHM) + // Loan 0 for cooler_ (collateral: 2,000 gOHM) (uint256 loan, ) = clearinghouse_.getLoanForCollateral(2_000 * 1e18); - clearinghouse_.lendToCooler(coolerA, loan); - // Loan 1 for coolerA (collateral: 1,000 gOHM) + clearinghouse_.lendToCooler(cooler_, loan); + // Loan 1 for cooler_ (collateral: 1,000 gOHM) (loan, ) = clearinghouse_.getLoanForCollateral(1_000 * 1e18); - clearinghouse_.lendToCooler(coolerA, loan); - // Loan 2 for coolerA (collateral: 333 gOHM) + clearinghouse_.lendToCooler(cooler_, loan); + // Loan 2 for cooler_ (collateral: 333 gOHM) (loan, ) = clearinghouse_.getLoanForCollateral(333 * 1e18); - clearinghouse_.lendToCooler(coolerA, loan); + clearinghouse_.lendToCooler(cooler_, loan); vm.stopPrank(); console2.log("Loans 0, 1, 2 created for cooler:", address(cooler_)); } + function _createCoolerAndLoans( + Clearinghouse clearinghouse_, + CoolerFactory coolerFactory_, + address wallet_, + ERC20 token_ + ) internal { + address cooler_ = _createCooler(coolerFactory_, wallet_, token_); + coolerA = Cooler(cooler_); + + _createLoans(clearinghouse_, coolerA, wallet_); + } + /// @notice Creates a new Cooler clone /// @dev Not that this will be regarded as a third-party Cooler, and rejected by LoanConsolidator, as CoolerFactory has no record of it. function _cloneCooler( @@ -385,6 +447,11 @@ contract LoanConsolidatorForkTest is Test { return Cooler(address(coolerFactory.coolerImplementation()).clone(coolerData)); } + modifier givenCoolerB(ERC20 token_) { + coolerB = Cooler(_createCooler(coolerFactory, walletB, token_)); + _; + } + // ===== ASSERTIONS ===== // function _assertCoolerLoans(uint256 collateral_) internal { @@ -632,14 +699,14 @@ contract LoanConsolidatorForkTest is Test { // given clearinghouseTo is disabled // [X] it reverts // given coolerFrom is equal to coolerTo - // given the cooler has no loans + // given coolerFrom has no loans specified // [X] it reverts - // given the cooler has 1 loan + // given coolerFrom has 1 loan specified // [X] it reverts // given coolerFrom is not equal to coolerTo - // given the cooler has no loans + // given coolerFrom has no loans specified // [X] it reverts - // given the cooler has 1 loan + // given coolerFrom has 1 loan specified // [X] it migrates the loan to coolerTo // given reserveTo is DAI // given DAI spending approval has not been given to LoanConsolidator @@ -670,7 +737,7 @@ contract LoanConsolidatorForkTest is Test { // [X] the loans on coolerFrom are migrated to coolerTo // [X] the Cooler owner receives DAI from the new loan - // --- consolidateWithFlashLoan -------------------------------------------- + // --- consolidate -------------------------------------------- function test_consolidate_policyNotActive_reverts() public { // Expect revert @@ -928,16 +995,13 @@ contract LoanConsolidatorForkTest is Test { coolerUsds.getLoan(1); } - function test_consolidate_callerNotOwner_coolerFrom_reverts() public givenPolicyActive { + function test_consolidate_callerNotOwner_coolerFrom_reverts() + public + givenPolicyActive + givenCoolerB(dai) + { uint256[] memory idsA = _idsA(); - // Deploy a cooler for walletB - address walletB = vm.addr(0xB); - vm.startPrank(walletB); - address coolerB_ = coolerFactory.generateCooler(gohm, dai); - Cooler coolerB = Cooler(coolerB_); - vm.stopPrank(); - // Grant approvals _grantCallerApprovals(idsA); @@ -956,16 +1020,13 @@ contract LoanConsolidatorForkTest is Test { ); } - function test_consolidate_callerNotOwner_coolerTo_reverts() public givenPolicyActive { + function test_consolidate_callerNotOwner_coolerTo_reverts() + public + givenPolicyActive + givenCoolerB(dai) + { uint256[] memory idsA = _idsA(); - // Deploy a cooler for walletB - address walletB = vm.addr(0xB); - vm.startPrank(walletB); - address coolerB_ = coolerFactory.generateCooler(gohm, dai); - Cooler coolerB = Cooler(coolerB_); - vm.stopPrank(); - // Grant approvals _grantCallerApprovals(idsA); @@ -1020,31 +1081,27 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_insufficientUsdsApproval_reverts() public givenPolicyActive { uint256[] memory idsA = _idsA(); - // Cache before it gets overwritten - address coolerDai = address(coolerA); - - // Create coolers - deal(address(gohm), walletA, _GOHM_AMOUNT); - _createCoolers(clearinghouseUsds, coolerFactory, walletA, usds); + // Create a Cooler on the USDS Clearinghouse + address coolerUsds = _createCooler(coolerFactory, walletA, usds); // Grant approvals - (, uint256 gohmApproval, , , ) = utils.requiredApprovals( - address(clearinghouseUsds), - address(coolerA), - idsA - ); + (, uint256 gohmApproval, , uint256 ownerReserveTo, uint256 callerReserveTo) = utils + .requiredApprovals(address(clearinghouseUsds), address(coolerA), idsA); _grantCallerApprovals(gohmApproval, 0, 1); + // Deal fees in USDS to the wallet + deal(address(usds), walletA, ownerReserveTo + callerReserveTo); + // Expect revert - vm.expectRevert("Dai/insufficient-allowance"); + vm.expectRevert("Usds/insufficient-allowance"); _consolidate( walletA, - address(clearinghouseUsds), address(clearinghouse), + address(clearinghouseUsds), address(coolerA), - address(coolerDai), + address(coolerUsds), idsA ); } @@ -1070,27 +1127,21 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_noProtocolFee_fuzz( uint256 loanOneCollateral_, uint256 loanTwoCollateral_ - ) public givenPolicyActive { + ) public givenPolicyActive givenCoolerB(dai) { // Bound the collateral values loanOneCollateral_ = bound(loanOneCollateral_, 1, 1e18); loanTwoCollateral_ = bound(loanTwoCollateral_, 1, 1e18); - // Set up a new wallet - address walletB = vm.addr(0xB); - // Fund the wallet with gOHM deal(address(gohm), walletB, loanOneCollateral_ + loanTwoCollateral_); - // Deploy a cooler for walletB - vm.startPrank(walletB); - address coolerB_ = coolerFactory.generateCooler(gohm, dai); - Cooler coolerB = Cooler(coolerB_); - // Approve clearinghouse to spend gOHM + vm.prank(walletB); gohm.approve(address(clearinghouse), loanOneCollateral_ + loanTwoCollateral_); // Take loans { + vm.startPrank(walletB); // Loan 0 for coolerB (uint256 loanOnePrincipal, ) = clearinghouse.getLoanForCollateral(loanOneCollateral_); clearinghouse.lendToCooler(coolerB, loanOnePrincipal); @@ -1110,7 +1161,13 @@ contract LoanConsolidatorForkTest is Test { uint256 interestDue = _getInterestDue(address(coolerB), loanIds); // Grant approvals - _grantCallerApprovals(walletB, address(clearinghouse), loanIds); + _grantCallerApprovals( + walletB, + address(clearinghouse), + address(coolerB), + address(coolerB), + loanIds + ); // Consolidate loans _consolidate( @@ -1178,7 +1235,13 @@ contract LoanConsolidatorForkTest is Test { ); // Grant approvals - _grantCallerApprovals(walletA, address(clearinghouse), idsA); + _grantCallerApprovals( + walletA, + address(clearinghouse), + address(coolerA), + address(coolerA), + idsA + ); // Calculate the expected lender fee uint256 lenderFee = MockFlashloanLender(lender).flashFee(address(dai), totalPrincipal); @@ -1214,7 +1277,13 @@ contract LoanConsolidatorForkTest is Test { ); // Grant approvals - _grantCallerApprovals(walletA, address(clearinghouse), idsA); + _grantCallerApprovals( + walletA, + address(clearinghouse), + address(coolerA), + address(coolerA), + idsA + ); // Consolidate loans _consolidate(idsA); @@ -1271,7 +1340,7 @@ contract LoanConsolidatorForkTest is Test { ); // Grant approvals - _grantCallerApprovals(walletA, address(clearinghouseUsds), idsA); + _grantCallerApprovals(walletA, address(clearinghouseUsds), coolerDai, coolerUsds, idsA); // Deal fees in USDS to the wallet deal(address(usds), walletA, interest + protocolFee); @@ -1317,7 +1386,7 @@ contract LoanConsolidatorForkTest is Test { // Create cooler loans on the USDS Clearinghouse deal(address(gohm), walletA, _GOHM_AMOUNT); - _createCoolers(clearinghouseUsds, coolerFactory, walletA, usds); + _createCoolerAndLoans(clearinghouseUsds, coolerFactory, walletA, usds); address coolerUsds = address(coolerA); (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( address(clearinghouse), @@ -1326,7 +1395,7 @@ contract LoanConsolidatorForkTest is Test { ); // Grant approvals - _grantCallerApprovals(walletA, address(clearinghouse), idsA); + _grantCallerApprovals(walletA, address(clearinghouse), coolerUsds, coolerDai, idsA); // Deal fees in DAI to the wallet deal(address(dai), walletA, interest + protocolFee); @@ -1401,7 +1470,7 @@ contract LoanConsolidatorForkTest is Test { // Create coolers deal(address(gohm), walletA, _GOHM_AMOUNT); - _createCoolers(clearinghouseUsds, coolerFactory, walletA, usds); + _createCoolerAndLoans(clearinghouseUsds, coolerFactory, walletA, usds); address coolerUsds = address(coolerA); (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( @@ -1411,7 +1480,7 @@ contract LoanConsolidatorForkTest is Test { ); // Grant approvals - _grantCallerApprovals(walletA, address(clearinghouseUsds), idsA); + _grantCallerApprovals(walletA, address(clearinghouseUsds), coolerUsds, coolerUsds, idsA); // Deal fees in USDS to the wallet deal(address(usds), walletA, interest + protocolFee); @@ -1444,103 +1513,975 @@ contract LoanConsolidatorForkTest is Test { _assertApprovals(coolerUsds, coolerUsds); } - // setFeePercentage - // when the policy is not active + // consolidateWithNewOwner + // given the contract has not been activated as a policy // [X] it reverts - // when the caller is not the admin + // given the contract has been disabled // [X] it reverts - // when the fee is > 100% + // given clearinghouseFrom is not registered with CHREG // [X] it reverts - // [X] it sets the fee percentage - - function test_setFeePercentage_whenPolicyNotActive_reverts() public givenAdminHasRole { - // Expect revert - vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyPolicyActive.selector)); - - vm.prank(admin); - utils.setFeePercentage(1000); - } - - function test_setFeePercentage_notAdmin_reverts() public givenAdminHasRole givenPolicyActive { - // Expect revert - vm.expectRevert(abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, ROLE_ADMIN)); - - // Set the fee percentage as a non-admin - utils.setFeePercentage(1000); - } - - function test_setFeePercentage_aboveMax_reverts() public givenAdminHasRole givenPolicyActive { - // Expect revert - vm.expectRevert( - abi.encodeWithSelector(LoanConsolidator.Params_FeePercentageOutOfRange.selector) - ); - - vm.prank(admin); - utils.setFeePercentage(_ONE_HUNDRED_PERCENT + 1); - } - - function test_setFeePercentage( - uint256 feePercentage_ - ) public givenAdminHasRole givenPolicyActive { - uint256 feePercentage = bound(feePercentage_, 0, _ONE_HUNDRED_PERCENT); - - vm.prank(admin); - utils.setFeePercentage(feePercentage); - - assertEq(utils.feePercentage(), feePercentage, "fee percentage"); - } - - // requiredApprovals - // when the policy is not active + // given clearinghouseTo is not registered with CHREG // [X] it reverts - // when the caller has no loans + // given coolerFrom was not created by clearinghouseFrom's CoolerFactory // [X] it reverts - // when the caller has 1 loan + // given coolerTo was not created by clearinghouseTo's CoolerFactory // [X] it reverts - // when the protocol fee is zero - // [X] it returns the correct values - // when the protocol fee is non-zero - // [X] it returns the correct values + // given the caller is not the owner of coolerFrom + // [X] it reverts + // given the owner of coolerFrom is the same as the owner of coolerTo + // [X] it reverts + // given clearinghouseTo is disabled + // [X] it reverts + // given coolerFrom is equal to coolerTo + // [X] it reverts + // given coolerFrom is not equal to coolerTo + // given coolerFrom has no loans specified + // [X] it reverts + // given coolerFrom has 1 loan specified + // [X] it migrates the loan to coolerTo + // given reserveTo is DAI + // given DAI spending approval has not been given to LoanConsolidator + // [X] it reverts + // given reserveTo is USDS + // given USDS spending approval has not been given to LoanConsolidator + // [X] it reverts + // given gOHM spending approval has not been given to LoanConsolidator + // [X] it reverts + // given the protocol fee is non-zero + // [X] it transfers the protocol fee to the collector + // given the lender fee is non-zero + // [X] it transfers the lender fee to the lender + // given the protocol fee is zero + // [X] it succeeds, but does not transfer additional reserveTo for the protocol fee + // given the lender fee is zero + // [X] it succeeds, but does not transfer additional reserveTo for the lender fee // when clearinghouseFrom is DAI and clearinghouseTo is USDS - // [X] it provides the correct values + // [X] the loans on coolerFrom are migrated to coolerTo + // [X] the Cooler owner receives USDS from the new loan // when clearinghouseFrom is USDS and clearinghouseTo is DAI - // [X] it provides the correct values + // [X] the loans on coolerFrom are migrated to coolerTo + // [X] the Cooler owner receives DAI from the new loan // when clearinghouseFrom is USDS and clearinghouseTo is USDS - // [X] it provides the correct values + // [X] the loans on coolerFrom are migrated to coolerTo + // [X] the Cooler owner receives USDS from the new loan // when clearinghouseFrom is DAI and clearinghouseTo is DAI - // [X] it provides the correct values + // [X] the loans on coolerFrom are migrated to coolerTo + // [X] the Cooler owner receives DAI from the new loan - function test_requiredApprovals_policyNotActive_reverts() public { - uint256[] memory ids = _idsA(); + // --- consolidateWithNewOwner -------------------------------------------- + function test_consolidateWithNewOwner_policyNotActive_reverts() public givenCoolerB(dai) { // Expect revert vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyPolicyActive.selector)); - utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); + // Consolidate loans for coolerA + uint256[] memory idsA = _idsA(); + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); } - function test_requiredApprovals_noLoans() public givenPolicyActive { - uint256[] memory ids = new uint256[](0); - + function test_consolidateWithNewOwner_deactivated_reverts() + public + givenAdminHasRole + givenEmergencyHasRole + givenPolicyActive + givenDeactivated + givenCoolerB(dai) + { // Expect revert - vm.expectRevert( - abi.encodeWithSelector(LoanConsolidator.Params_InsufficientCoolerCount.selector) - ); + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyConsolidatorActive.selector)); - utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); + // Consolidate loans for coolerA + uint256[] memory idsA = _idsA(); + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); } - function test_requiredApprovals_oneLoan() public givenPolicyActive { - uint256[] memory ids = new uint256[](1); - ids[0] = 0; + function test_consolidateWithNewOwner_thirdPartyClearinghouseFrom_reverts() + public + givenPolicyActive + givenCoolerB(dai) + { + // Create a new Clearinghouse + // It is not registered with CHREG, so should be rejected + Clearinghouse newClearinghouse = new Clearinghouse( + address(ohm), + address(gohm), + staking, + address(sdai), + address(coolerFactory), + address(kernel) + ); // Expect revert vm.expectRevert( - abi.encodeWithSelector(LoanConsolidator.Params_InsufficientCoolerCount.selector) + abi.encodeWithSelector(LoanConsolidator.Params_InvalidClearinghouse.selector) ); - utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); - } + // Consolidate loans + uint256[] memory idsA = _idsA(); + _consolidateWithNewOwner( + walletA, + address(newClearinghouse), + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + } + + function test_consolidateWithNewOwner_thirdPartyClearinghouseTo_reverts() + public + givenPolicyActive + givenCoolerB(dai) + { + // Create a new Clearinghouse + // It is not registered with CHREG, so should be rejected + Clearinghouse newClearinghouse = new Clearinghouse( + address(ohm), + address(gohm), + staking, + address(sdai), + address(coolerFactory), + address(kernel) + ); + + // Expect revert + vm.expectRevert( + abi.encodeWithSelector(LoanConsolidator.Params_InvalidClearinghouse.selector) + ); + + // Consolidate loans + uint256[] memory idsA = _idsA(); + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(newClearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + } + + function test_consolidateWithNewOwner_thirdPartyCoolerFrom_reverts() + public + givenPolicyActive + givenCoolerB(dai) + { + // Create a new Cooler + // It was not created by the Clearinghouse's CoolerFactory, so should be rejected + Cooler newCooler = _cloneCooler( + walletA, + address(gohm), + address(dai), + address(coolerFactory) + ); + + // Expect revert + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.Params_InvalidCooler.selector)); + + // Consolidate loans for coolerA into newCooler + uint256[] memory idsA = _idsA(); + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouse), + address(newCooler), + address(coolerB), + idsA + ); + } + + function test_consolidateWithNewOwner_thirdPartyCoolerTo_reverts() public givenPolicyActive { + // Create a new Cooler + // It was not created by the Clearinghouse's CoolerFactory, so should be rejected + Cooler newCooler = _cloneCooler( + walletA, + address(gohm), + address(dai), + address(coolerFactory) + ); + + // Expect revert + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.Params_InvalidCooler.selector)); + + // Consolidate loans for coolerA into newCooler + uint256[] memory idsA = _idsA(); + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouse), + address(coolerA), + address(newCooler), + idsA + ); + } + + function test_consolidateWithNewOwner_sameCooler_reverts() public givenPolicyActive { + uint256[] memory idsA = _idsA(); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(clearinghouse), + address(coolerA), + address(coolerA), + idsA + ); + + // Expect revert since the cooler is the same + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.Params_InvalidCooler.selector)); + + // Consolidate loans + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouse), + address(coolerA), + address(coolerA), + idsA + ); + } + + function test_consolidateWithNewOwner_sameOwner_reverts() public givenPolicyActive { + uint256[] memory idsA = _idsA(); + + // Create a new Cooler on the USDS Clearinghouse + vm.startPrank(walletA); + address coolerUsds_ = coolerFactory.generateCooler(gohm, usds); + Cooler coolerUsds = Cooler(coolerUsds_); + vm.stopPrank(); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(clearinghouse), + address(coolerA), + address(coolerA), + idsA + ); + + // Expect revert since the owner is the same + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.Params_InvalidCooler.selector)); + + // Consolidate loans + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouseUsds), + address(coolerA), + address(coolerUsds), + idsA + ); + } + + function test_consolidateWithNewOwner_noLoans_reverts() + public + givenPolicyActive + givenCoolerB(dai) + { + uint256[] memory idsA = new uint256[](0); + + (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( + address(clearinghouse), + address(coolerA), + idsA + ); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + + // Deal fees in DAI to the wallet + deal(address(dai), walletA, interest + protocolFee); + + // Expect revert since no loan ids are given + vm.expectRevert( + abi.encodeWithSelector(LoanConsolidator.Params_InsufficientCoolerCount.selector) + ); + + // Consolidate loans for coolerA + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + } + + function test_consolidateWithNewOwner_oneLoan() public givenPolicyActive givenCoolerB(dai) { + uint256[] memory idsA = new uint256[](1); + idsA[0] = 0; + + (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( + address(clearinghouse), + address(coolerA), + idsA + ); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + + // Deal fees in DAI to the wallet + deal(address(dai), walletA, interest + protocolFee); + + // Get the loan principal before consolidation + Cooler.Loan memory loanZero = coolerA.getLoan(0); + Cooler.Loan memory loanOne = coolerA.getLoan(1); + Cooler.Loan memory loanTwo = coolerA.getLoan(2); + + // Consolidate loans for coolerA + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + + // Assert that only loan 0 has been repaid + assertEq(coolerA.getLoan(0).principal, 0, "cooler A, loan 0: principal"); + assertEq(coolerA.getLoan(1).principal, loanOne.principal, "cooler A, loan 1: principal"); + assertEq(coolerA.getLoan(2).principal, loanTwo.principal, "cooler A, loan 2: principal"); + // Assert that loan 0 has been migrated to coolerB + assertEq(coolerB.getLoan(0).principal, loanZero.principal, "cooler B, loan 0: principal"); + // Assert that coolerB has no other loans + vm.expectRevert(); + coolerB.getLoan(1); + } + + function test_consolidateWithNewOwner_callerNotOwner_coolerFrom_reverts() + public + givenPolicyActive + givenCoolerB(dai) + { + uint256[] memory idsA = _idsA(); + + // Grant approvals + _grantCallerApprovals( + walletB, + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + + // Expect revert + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyCoolerOwner.selector)); + + // Consolidate loans for coolerA + // Do not perform as the cooler owner + _consolidateWithNewOwner( + walletB, + address(clearinghouse), + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + } + + function test_consolidateWithNewOwner_insufficientGOhmApproval_reverts() + public + givenPolicyActive + givenCoolerB(dai) + { + uint256[] memory idsA = _idsA(); + + // Grant approvals + (, uint256 gohmApproval, , uint256 ownerReserveTo, uint256 callerReserveTwo) = utils + .requiredApprovals(address(clearinghouse), address(coolerA), idsA); + + _grantCallerApprovals(gohmApproval - 1, ownerReserveTo + callerReserveTwo, 0); + + // Expect revert + vm.expectRevert("ERC20: transfer amount exceeds allowance"); + + // Consolidate loans for coolerA + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + } + + function test_consolidateWithNewOwner_insufficientDaiApproval_reverts() + public + givenPolicyActive + givenCoolerB(dai) + { + uint256[] memory idsA = _idsA(); + + // Grant approvals + (, uint256 gohmApproval, , , ) = utils.requiredApprovals( + address(clearinghouse), + address(coolerA), + idsA + ); + + _grantCallerApprovals(gohmApproval, 1, 0); + + // Expect revert + vm.expectRevert("Dai/insufficient-allowance"); + + // Consolidate loans for coolerA + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + } + + function test_consolidateWithNewOwner_insufficientUsdsApproval_reverts() + public + givenPolicyActive + givenCoolerB(usds) + { + uint256[] memory idsA = _idsA(); + + // Grant approvals + (, uint256 gohmApproval, , uint256 ownerReserveTo, uint256 callerReserveTo) = utils + .requiredApprovals(address(clearinghouseUsds), address(coolerA), idsA); + + _grantCallerApprovals(gohmApproval, 0, 1); + + // Deal fees in USDS to the wallet + deal(address(usds), walletA, ownerReserveTo + callerReserveTo); + + // Expect revert + vm.expectRevert("Usds/insufficient-allowance"); + + // Consolidate loans for coolerA + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouseUsds), + address(coolerA), + address(coolerB), + idsA + ); + } + + function test_consolidateWithNewOwner_noProtocolFee() + public + givenPolicyActive + givenCoolerB(dai) + { + uint256[] memory idsA = _idsA(); + + // Record the amount of DAI in the wallet + uint256 initPrincipal = dai.balanceOf(walletA); + uint256 interestDue = _getInterestDue(idsA); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + + // Consolidate loans for coolerA + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + + _assertCoolerLoansCrossClearinghouse(address(coolerA), address(coolerB), _GOHM_AMOUNT); + _assertTokenBalances( + address(dai), + address(coolerA), + address(coolerB), + initPrincipal - interestDue, + 0, + 0, + _GOHM_AMOUNT + ); + _assertApprovals(address(coolerA), address(coolerB)); + } + + function test_consolidateWithNewOwner_lenderFee() + public + givenAdminHasRole + givenPolicyActive + givenCoolerB(dai) + givenMockFlashloanLender + givenMockFlashloanLenderFee(100) // 1% + givenMockFlashloanLenderHasBalance(20_000_000e18) + { + uint256[] memory idsA = _idsA(); + + // Record the initial debt balance + (uint256 totalPrincipal, ) = clearinghouse.getLoanForCollateral(_GOHM_AMOUNT); + + // Record the amount of DAI in the wallet + uint256 initPrincipal = dai.balanceOf(walletA); + (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( + address(clearinghouse), + address(coolerA), + idsA + ); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + + // Calculate the expected lender fee + uint256 lenderFee = MockFlashloanLender(lender).flashFee(address(dai), totalPrincipal); + uint256 expectedLenderBalance = 20_000_000e18 + lenderFee; + + // Consolidate loans + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + + _assertCoolerLoansCrossClearinghouse(address(coolerA), address(coolerB), _GOHM_AMOUNT); + _assertTokenBalances( + address(dai), + address(coolerA), + address(coolerB), + initPrincipal - interest - protocolFee - lenderFee, + expectedLenderBalance, + protocolFee, + _GOHM_AMOUNT + ); + _assertApprovals(address(coolerA), address(coolerB)); + } + + function test_consolidateWithNewOwner_protocolFee() + public + givenAdminHasRole + givenPolicyActive + givenCoolerB(dai) + givenProtocolFee(1000) // 1% + { + uint256[] memory idsA = _idsA(); + + // Record the amount of DAI in the wallet + uint256 initPrincipal = dai.balanceOf(walletA); + (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( + address(clearinghouse), + address(coolerA), + idsA + ); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + + // Consolidate loans + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + + _assertCoolerLoansCrossClearinghouse(address(coolerA), address(coolerB), _GOHM_AMOUNT); + _assertTokenBalances( + address(dai), + address(coolerA), + address(coolerB), + initPrincipal - interest - protocolFee, + 0, + protocolFee, + _GOHM_AMOUNT + ); + _assertApprovals(address(coolerA), address(coolerB)); + } + + function test_consolidateWithNewOwner_disabledClearinghouse_reverts() + public + givenPolicyActive + givenCoolerB(dai) + givenEmergencyHasRole + { + // Disable the Clearinghouse + vm.prank(emergency); + clearinghouse.emergencyShutdown(); + + uint256[] memory idsA = _idsA(); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + + // Expect revert + vm.expectRevert("SavingsDai/insufficient-balance"); + + // Consolidate loans + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouse), + address(coolerA), + address(coolerB), + idsA + ); + } + + function test_consolidateWithNewOwner_protocolFee_daiToUsds() + public + givenAdminHasRole + givenPolicyActive + givenCoolerB(usds) + givenProtocolFee(1000) // 1% + { + uint256[] memory idsA = _idsA(); + + (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( + address(clearinghouseUsds), + address(coolerA), + idsA + ); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(clearinghouseUsds), + address(coolerA), + address(coolerB), + idsA + ); + + // Deal fees in USDS to the wallet + deal(address(usds), walletA, interest + protocolFee); + // Make sure the wallet has no DAI + deal(address(dai), walletA, 0); + + // Record the amount of USDS in the wallet + uint256 initPrincipal = usds.balanceOf(walletA); + + // Consolidate loans + _consolidateWithNewOwner( + walletA, + address(clearinghouse), + address(clearinghouseUsds), + address(coolerA), + address(coolerB), + idsA + ); + + _assertCoolerLoansCrossClearinghouse(address(coolerA), address(coolerB), _GOHM_AMOUNT); + _assertTokenBalances( + address(usds), + address(coolerA), + address(coolerB), + initPrincipal - interest - protocolFee, + 0, + protocolFee, + _GOHM_AMOUNT + ); + _assertApprovals(address(coolerA), address(coolerB)); + } + + function test_consolidateWithNewOwner_protocolFee_usdsToDai() + public + givenAdminHasRole + givenPolicyActive + givenCoolerB(dai) + givenProtocolFee(1000) // 1% + { + uint256[] memory idsA = _idsA(); + + // Create loans for walletA on the USDS Clearinghouse + deal(address(gohm), walletA, _GOHM_AMOUNT); + address coolerUsds = _createCooler(coolerFactory, walletA, usds); + _createLoans(clearinghouseUsds, Cooler(coolerUsds), walletA); + (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( + address(clearinghouse), + coolerUsds, + idsA + ); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(clearinghouse), + address(coolerUsds), + address(coolerB), + idsA + ); + + // Deal fees in DAI to the wallet + deal(address(dai), walletA, interest + protocolFee); + // Make sure the wallet has no USDS + deal(address(usds), walletA, 0); + + // Record the amount of DAI in the wallet + uint256 initPrincipal = dai.balanceOf(walletA); + + // Consolidate loans + _consolidateWithNewOwner( + walletA, + address(clearinghouseUsds), + address(clearinghouse), + coolerUsds, + address(coolerB), + idsA + ); + + // Check that coolerUsds has no loans + _assertCoolerLoansCrossClearinghouse(address(coolerUsds), address(coolerB), _GOHM_AMOUNT); + _assertTokenBalances( + address(dai), + address(coolerUsds), + address(coolerB), + initPrincipal - interest - protocolFee, + 0, + protocolFee, + _GOHM_AMOUNT + ); + _assertApprovals(address(coolerUsds), address(coolerB)); + } + + function test_consolidateWithNewOwner_protocolFee_usdsToUsds() + public + givenAdminHasRole + givenPolicyActive + givenCoolerB(usds) + givenProtocolFee(1000) // 1% + { + uint256[] memory idsA = _idsA(); + + // Create loans for walletA on the USDS Clearinghouse + deal(address(gohm), walletA, _GOHM_AMOUNT); + address coolerUsds = _createCooler(coolerFactory, walletA, usds); + _createLoans(clearinghouseUsds, Cooler(coolerUsds), walletA); + (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( + address(clearinghouseUsds), + coolerUsds, + idsA + ); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(clearinghouseUsds), + address(coolerUsds), + address(coolerB), + idsA + ); + + // Deal fees in USDS to the wallet + deal(address(usds), walletA, interest + protocolFee); + // Make sure the wallet has no DAI + deal(address(dai), walletA, 0); + + // Record the amount of USDS in the wallet + uint256 initPrincipal = usds.balanceOf(walletA); + + // Consolidate loans + _consolidateWithNewOwner( + walletA, + address(clearinghouseUsds), + address(clearinghouseUsds), + coolerUsds, + address(coolerB), + idsA + ); + + _assertCoolerLoansCrossClearinghouse(address(coolerUsds), address(coolerB), _GOHM_AMOUNT); + _assertTokenBalances( + address(usds), + address(coolerUsds), + address(coolerB), + initPrincipal - interest - protocolFee, + 0, + protocolFee, + _GOHM_AMOUNT + ); + _assertApprovals(address(coolerUsds), address(coolerB)); + } + + // setFeePercentage + // when the policy is not active + // [X] it reverts + // when the caller is not the admin + // [X] it reverts + // when the fee is > 100% + // [X] it reverts + // [X] it sets the fee percentage + + function test_setFeePercentage_whenPolicyNotActive_reverts() public givenAdminHasRole { + // Expect revert + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyPolicyActive.selector)); + + vm.prank(admin); + utils.setFeePercentage(1000); + } + + function test_setFeePercentage_notAdmin_reverts() public givenAdminHasRole givenPolicyActive { + // Expect revert + vm.expectRevert(abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, ROLE_ADMIN)); + + // Set the fee percentage as a non-admin + utils.setFeePercentage(1000); + } + + function test_setFeePercentage_aboveMax_reverts() public givenAdminHasRole givenPolicyActive { + // Expect revert + vm.expectRevert( + abi.encodeWithSelector(LoanConsolidator.Params_FeePercentageOutOfRange.selector) + ); + + vm.prank(admin); + utils.setFeePercentage(_ONE_HUNDRED_PERCENT + 1); + } + + function test_setFeePercentage( + uint256 feePercentage_ + ) public givenAdminHasRole givenPolicyActive { + uint256 feePercentage = bound(feePercentage_, 0, _ONE_HUNDRED_PERCENT); + + vm.prank(admin); + utils.setFeePercentage(feePercentage); + + assertEq(utils.feePercentage(), feePercentage, "fee percentage"); + } + + // requiredApprovals + // when the policy is not active + // [X] it reverts + // when the caller has no loans + // [X] it returns the correct values + // when the caller has 1 loan + // [X] it returns the correct values + // when the protocol fee is zero + // [X] it returns the correct values + // when the protocol fee is non-zero + // [X] it returns the correct values + // when clearinghouseFrom is DAI and clearinghouseTo is USDS + // [X] it provides the correct values + // when clearinghouseFrom is USDS and clearinghouseTo is DAI + // [X] it provides the correct values + // when clearinghouseFrom is USDS and clearinghouseTo is USDS + // [X] it provides the correct values + // when clearinghouseFrom is DAI and clearinghouseTo is DAI + // [X] it provides the correct values + + function test_requiredApprovals_policyNotActive_reverts() public { + uint256[] memory ids = _idsA(); + + // Expect revert + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyPolicyActive.selector)); + + utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); + } + + function test_requiredApprovals_noLoans() public givenPolicyActive { + uint256[] memory ids = new uint256[](0); + + ( + address owner, + uint256 gOhmApproval, + address reserveTo, + uint256 ownerReserveTo, + uint256 callerReserveTo + ) = utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); + + assertEq(owner, walletA, "owner"); + assertEq(gOhmApproval, 0, "gOHM approval"); + assertEq(reserveTo, address(dai), "reserveTo"); + assertEq(ownerReserveTo, 0, "ownerReserveTo"); + assertEq(callerReserveTo, 0, "callerReserveTo"); + } + + function test_requiredApprovals_oneLoan() public givenPolicyActive { + uint256[] memory ids = new uint256[](1); + ids[0] = 0; + + ( + address owner, + uint256 gOhmApproval, + address reserveTo, + uint256 ownerReserveTo, + uint256 callerReserveTo + ) = utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); + + uint256 expectedCollateral; + uint256 expectedPrincipal; + uint256 expectedInterest; + for (uint256 i = 0; i < ids.length; i++) { + Cooler.Loan memory loan = coolerA.getLoan(ids[i]); + + expectedCollateral += loan.collateral; + expectedPrincipal += loan.principal; + expectedInterest += loan.interestDue; + } + + uint256 expectedProtocolFee = 0; + uint256 expectedLenderFee = 0; + + assertEq(owner, walletA, "owner"); + assertEq(gOhmApproval, expectedCollateral, "gOHM approval"); + assertEq(reserveTo, address(dai), "reserveTo"); + assertEq(ownerReserveTo, expectedPrincipal, "ownerReserveTo"); + assertEq( + callerReserveTo, + expectedInterest + expectedProtocolFee + expectedLenderFee, + "callerReserveTo" + ); + } function test_requiredApprovals_noProtocolFee() public givenPolicyActive { uint256[] memory ids = _idsA(); @@ -1666,7 +2607,7 @@ contract LoanConsolidatorForkTest is Test { // Create Cooler loans on the USDS Clearinghouse deal(address(gohm), walletA, _GOHM_AMOUNT); - _createCoolers(clearinghouseUsds, coolerFactory, walletA, usds); + _createCoolerAndLoans(clearinghouseUsds, coolerFactory, walletA, usds); ( address owner_, @@ -1710,7 +2651,7 @@ contract LoanConsolidatorForkTest is Test { // Create Cooler loans on the USDS Clearinghouse deal(address(gohm), walletA, _GOHM_AMOUNT); - _createCoolers(clearinghouseUsds, coolerFactory, walletA, usds); + _createCoolerAndLoans(clearinghouseUsds, coolerFactory, walletA, usds); ( address owner_, @@ -1747,28 +2688,22 @@ contract LoanConsolidatorForkTest is Test { function test_requiredApprovals_fuzz( uint256 loanOneCollateral_, uint256 loanTwoCollateral_ - ) public givenPolicyActive { + ) public givenPolicyActive givenCoolerB(dai) { // Bound the collateral values loanOneCollateral_ = bound(loanOneCollateral_, 1, 1e18); loanTwoCollateral_ = bound(loanTwoCollateral_, 1, 1e18); - // Set up a new wallet - address walletB = vm.addr(0xB); - // Fund the wallet with gOHM deal(address(gohm), walletB, loanOneCollateral_ + loanTwoCollateral_); - // Deploy a cooler for walletB - vm.startPrank(walletB); - address coolerB_ = coolerFactory.generateCooler(gohm, dai); - Cooler coolerB = Cooler(coolerB_); - // Approve clearinghouse to spend gOHM + vm.prank(walletB); gohm.approve(address(clearinghouse), loanOneCollateral_ + loanTwoCollateral_); // Take loans uint256 totalPrincipal; { + vm.startPrank(walletB); // Loan 0 for coolerB (uint256 loanOnePrincipal, ) = clearinghouse.getLoanForCollateral(loanOneCollateral_); totalPrincipal += loanOnePrincipal; @@ -1804,28 +2739,22 @@ contract LoanConsolidatorForkTest is Test { function test_collateralRequired_fuzz( uint256 loanOneCollateral_, uint256 loanTwoCollateral_ - ) public givenPolicyActive { + ) public givenPolicyActive givenCoolerB(dai) { // Bound the collateral values loanOneCollateral_ = bound(loanOneCollateral_, 1, 1e18); loanTwoCollateral_ = bound(loanTwoCollateral_, 1, 1e18); - // Set up a new wallet - address walletB = vm.addr(0xB); - // Fund the wallet with gOHM deal(address(gohm), walletB, loanOneCollateral_ + loanTwoCollateral_); - // Deploy a cooler for walletB - vm.startPrank(walletB); - address coolerB_ = coolerFactory.generateCooler(gohm, dai); - Cooler coolerB = Cooler(coolerB_); - // Approve clearinghouse to spend gOHM + vm.prank(walletB); gohm.approve(address(clearinghouse), loanOneCollateral_ + loanTwoCollateral_); // Take loans uint256 totalPrincipal; { + vm.startPrank(walletB); // Loan 0 for coolerB (uint256 loanOnePrincipal, ) = clearinghouse.getLoanForCollateral(loanOneCollateral_); clearinghouse.lendToCooler(coolerB, loanOnePrincipal); From 95479d5d4a9bb941c60c7a8347709d9fc895b819 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 30 Oct 2024 15:52:43 +0400 Subject: [PATCH 086/160] Disable the contract functionality by default --- src/policies/LoanConsolidator.sol | 9 +- src/test/policies/LoanConsolidatorFork.t.sol | 211 ++++++++++++------- 2 files changed, 134 insertions(+), 86 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index fe96792ec..e528a9a0a 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -167,13 +167,12 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent if (feePercentage_ > ONE_HUNDRED_PERCENT) revert Params_FeePercentageOutOfRange(); if (kernel_ == address(0)) revert Params_InvalidAddress(); - // store protocol data + // Store protocol data feePercentage = feePercentage_; - // Set the contract to be active - // It is activated here so that it is performed by default - // However, the contract will not be useable until it has been installed as a policy - consolidatorActive = true; + // Set the contract to be disabled by default + // It must be activated as a policy and activated before being used + consolidatorActive = false; // Emit events emit FeePercentageSet(feePercentage); diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index 3ad096ac7..9c025729d 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -182,6 +182,10 @@ contract LoanConsolidatorForkTest is Test { deal(address(susds), address(clearinghouseUsds), 18_000_000 * 1e18); _createCoolerAndLoans(clearinghouse, coolerFactory, walletA, dai); + + // LoanConsolidator is deactivated by default + // Assign the emergency role so that the contract can be activated + _assignEmergencyRole(); } // ===== MODIFIERS ===== // @@ -192,10 +196,9 @@ contract LoanConsolidatorForkTest is Test { _; } - modifier givenEmergencyHasRole() { + function _assignEmergencyRole() internal { vm.prank(kernelExecutor); rolesAdmin.grantRole(ROLE_EMERGENCY_SHUTDOWN, emergency); - _; } modifier givenProtocolFee(uint256 feePercent_) { @@ -748,11 +751,19 @@ contract LoanConsolidatorForkTest is Test { _consolidate(idsA); } + function test_consolidate_defaultDeactivated_reverts() public givenPolicyActive { + // Expect revert + vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyConsolidatorActive.selector)); + + // Consolidate loans for coolerA + uint256[] memory idsA = _idsA(); + _consolidate(idsA); + } + function test_consolidate_deactivated_reverts() public - givenAdminHasRole - givenEmergencyHasRole givenPolicyActive + givenActivated givenDeactivated { // Expect revert @@ -763,7 +774,11 @@ contract LoanConsolidatorForkTest is Test { _consolidate(idsA); } - function test_consolidate_thirdPartyClearinghouseFrom_reverts() public givenPolicyActive { + function test_consolidate_thirdPartyClearinghouseFrom_reverts() + public + givenPolicyActive + givenActivated + { // Create a new Clearinghouse // It is not registered with CHREG, so should be rejected Clearinghouse newClearinghouse = new Clearinghouse( @@ -792,7 +807,11 @@ contract LoanConsolidatorForkTest is Test { ); } - function test_consolidate_thirdPartyClearinghouseTo_reverts() public givenPolicyActive { + function test_consolidate_thirdPartyClearinghouseTo_reverts() + public + givenPolicyActive + givenActivated + { // Create a new Clearinghouse // It is not registered with CHREG, so should be rejected Clearinghouse newClearinghouse = new Clearinghouse( @@ -821,7 +840,11 @@ contract LoanConsolidatorForkTest is Test { ); } - function test_consolidate_thirdPartyCoolerFrom_reverts() public givenPolicyActive { + function test_consolidate_thirdPartyCoolerFrom_reverts() + public + givenPolicyActive + givenActivated + { // Create a new Cooler // It was not created by the Clearinghouse's CoolerFactory, so should be rejected Cooler newCooler = _cloneCooler( @@ -846,7 +869,7 @@ contract LoanConsolidatorForkTest is Test { ); } - function test_consolidate_thirdPartyCoolerTo_reverts() public givenPolicyActive { + function test_consolidate_thirdPartyCoolerTo_reverts() public givenPolicyActive givenActivated { // Create a new Cooler // It was not created by the Clearinghouse's CoolerFactory, so should be rejected Cooler newCooler = _cloneCooler( @@ -871,7 +894,7 @@ contract LoanConsolidatorForkTest is Test { ); } - function test_consolidate_sameCooler_noLoans_reverts() public givenPolicyActive { + function test_consolidate_sameCooler_noLoans_reverts() public givenPolicyActive givenActivated { // Grant approvals _grantCallerApprovals(type(uint256).max, type(uint256).max, type(uint256).max); @@ -885,7 +908,7 @@ contract LoanConsolidatorForkTest is Test { _consolidate(ids); } - function test_consolidate_sameCooler_oneLoan_reverts() public givenPolicyActive { + function test_consolidate_sameCooler_oneLoan_reverts() public givenPolicyActive givenActivated { // Grant approvals _grantCallerApprovals(type(uint256).max, type(uint256).max, type(uint256).max); @@ -900,7 +923,11 @@ contract LoanConsolidatorForkTest is Test { _consolidate(ids); } - function test_consolidate_differentCooler_noLoans_reverts() public givenPolicyActive { + function test_consolidate_differentCooler_noLoans_reverts() + public + givenPolicyActive + givenActivated + { uint256[] memory idsA = _idsA(); // Deploy a Cooler on the USDS Clearinghouse @@ -940,7 +967,7 @@ contract LoanConsolidatorForkTest is Test { ); } - function test_consolidate_differentCooler_oneLoan() public givenPolicyActive { + function test_consolidate_differentCooler_oneLoan() public givenPolicyActive givenActivated { uint256[] memory idsA = _idsA(); // Deploy a Cooler on the USDS Clearinghouse @@ -998,6 +1025,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_callerNotOwner_coolerFrom_reverts() public givenPolicyActive + givenActivated givenCoolerB(dai) { uint256[] memory idsA = _idsA(); @@ -1023,6 +1051,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_callerNotOwner_coolerTo_reverts() public givenPolicyActive + givenActivated givenCoolerB(dai) { uint256[] memory idsA = _idsA(); @@ -1045,7 +1074,11 @@ contract LoanConsolidatorForkTest is Test { ); } - function test_consolidate_insufficientGOhmApproval_reverts() public givenPolicyActive { + function test_consolidate_insufficientGOhmApproval_reverts() + public + givenPolicyActive + givenActivated + { uint256[] memory idsA = _idsA(); // Grant approvals @@ -1060,7 +1093,11 @@ contract LoanConsolidatorForkTest is Test { _consolidate(idsA); } - function test_consolidate_insufficientDaiApproval_reverts() public givenPolicyActive { + function test_consolidate_insufficientDaiApproval_reverts() + public + givenPolicyActive + givenActivated + { uint256[] memory idsA = _idsA(); // Grant approvals @@ -1078,7 +1115,11 @@ contract LoanConsolidatorForkTest is Test { _consolidate(idsA); } - function test_consolidate_insufficientUsdsApproval_reverts() public givenPolicyActive { + function test_consolidate_insufficientUsdsApproval_reverts() + public + givenPolicyActive + givenActivated + { uint256[] memory idsA = _idsA(); // Create a Cooler on the USDS Clearinghouse @@ -1106,7 +1147,7 @@ contract LoanConsolidatorForkTest is Test { ); } - function test_consolidate_noProtocolFee() public givenPolicyActive { + function test_consolidate_noProtocolFee() public givenPolicyActive givenActivated { uint256[] memory idsA = _idsA(); // Record the amount of DAI in the wallet @@ -1127,7 +1168,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_noProtocolFee_fuzz( uint256 loanOneCollateral_, uint256 loanTwoCollateral_ - ) public givenPolicyActive givenCoolerB(dai) { + ) public givenPolicyActive givenActivated givenCoolerB(dai) { // Bound the collateral values loanOneCollateral_ = bound(loanOneCollateral_, 1, 1e18); loanTwoCollateral_ = bound(loanTwoCollateral_, 1, 1e18); @@ -1215,8 +1256,9 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_lenderFee() public - givenAdminHasRole givenPolicyActive + givenActivated + givenAdminHasRole givenMockFlashloanLender givenMockFlashloanLenderFee(100) // 1% givenMockFlashloanLenderHasBalance(20_000_000e18) @@ -1262,8 +1304,9 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_protocolFee() public - givenAdminHasRole givenPolicyActive + givenActivated + givenAdminHasRole givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -1296,7 +1339,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_noProtocolFee_disabledClearinghouse_reverts() public givenPolicyActive - givenEmergencyHasRole + givenActivated { // Disable the Clearinghouse vm.prank(emergency); @@ -1323,8 +1366,9 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_protocolFee_daiToUsds() public - givenAdminHasRole givenPolicyActive + givenActivated + givenAdminHasRole givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -1375,8 +1419,9 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_protocolFee_usdsToDai() public - givenAdminHasRole givenPolicyActive + givenActivated + givenAdminHasRole givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -1462,8 +1507,9 @@ contract LoanConsolidatorForkTest is Test { function test_consolidate_protocolFee_usdsToUsds() public - givenAdminHasRole givenPolicyActive + givenActivated + givenAdminHasRole givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -1588,10 +1634,10 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_deactivated_reverts() public - givenAdminHasRole - givenEmergencyHasRole givenPolicyActive + givenActivated givenDeactivated + givenAdminHasRole givenCoolerB(dai) { // Expect revert @@ -1612,6 +1658,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_thirdPartyClearinghouseFrom_reverts() public givenPolicyActive + givenActivated givenCoolerB(dai) { // Create a new Clearinghouse @@ -1645,6 +1692,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_thirdPartyClearinghouseTo_reverts() public givenPolicyActive + givenActivated givenCoolerB(dai) { // Create a new Clearinghouse @@ -1678,6 +1726,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_thirdPartyCoolerFrom_reverts() public givenPolicyActive + givenActivated givenCoolerB(dai) { // Create a new Cooler @@ -1704,7 +1753,11 @@ contract LoanConsolidatorForkTest is Test { ); } - function test_consolidateWithNewOwner_thirdPartyCoolerTo_reverts() public givenPolicyActive { + function test_consolidateWithNewOwner_thirdPartyCoolerTo_reverts() + public + givenPolicyActive + givenActivated + { // Create a new Cooler // It was not created by the Clearinghouse's CoolerFactory, so should be rejected Cooler newCooler = _cloneCooler( @@ -1729,7 +1782,11 @@ contract LoanConsolidatorForkTest is Test { ); } - function test_consolidateWithNewOwner_sameCooler_reverts() public givenPolicyActive { + function test_consolidateWithNewOwner_sameCooler_reverts() + public + givenPolicyActive + givenActivated + { uint256[] memory idsA = _idsA(); // Grant approvals @@ -1755,7 +1812,11 @@ contract LoanConsolidatorForkTest is Test { ); } - function test_consolidateWithNewOwner_sameOwner_reverts() public givenPolicyActive { + function test_consolidateWithNewOwner_sameOwner_reverts() + public + givenPolicyActive + givenActivated + { uint256[] memory idsA = _idsA(); // Create a new Cooler on the USDS Clearinghouse @@ -1790,6 +1851,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_noLoans_reverts() public givenPolicyActive + givenActivated givenCoolerB(dai) { uint256[] memory idsA = new uint256[](0); @@ -1828,7 +1890,12 @@ contract LoanConsolidatorForkTest is Test { ); } - function test_consolidateWithNewOwner_oneLoan() public givenPolicyActive givenCoolerB(dai) { + function test_consolidateWithNewOwner_oneLoan() + public + givenPolicyActive + givenActivated + givenCoolerB(dai) + { uint256[] memory idsA = new uint256[](1); idsA[0] = 0; @@ -1879,6 +1946,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_callerNotOwner_coolerFrom_reverts() public givenPolicyActive + givenActivated givenCoolerB(dai) { uint256[] memory idsA = _idsA(); @@ -1910,6 +1978,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_insufficientGOhmApproval_reverts() public givenPolicyActive + givenActivated givenCoolerB(dai) { uint256[] memory idsA = _idsA(); @@ -1937,6 +2006,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_insufficientDaiApproval_reverts() public givenPolicyActive + givenActivated givenCoolerB(dai) { uint256[] memory idsA = _idsA(); @@ -1967,6 +2037,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_insufficientUsdsApproval_reverts() public givenPolicyActive + givenActivated givenCoolerB(usds) { uint256[] memory idsA = _idsA(); @@ -1997,6 +2068,7 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_noProtocolFee() public givenPolicyActive + givenActivated givenCoolerB(dai) { uint256[] memory idsA = _idsA(); @@ -2039,9 +2111,10 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_lenderFee() public - givenAdminHasRole givenPolicyActive + givenActivated givenCoolerB(dai) + givenAdminHasRole givenMockFlashloanLender givenMockFlashloanLenderFee(100) // 1% givenMockFlashloanLenderHasBalance(20_000_000e18) @@ -2097,9 +2170,10 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_protocolFee() public - givenAdminHasRole givenPolicyActive + givenActivated givenCoolerB(dai) + givenAdminHasRole givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -2147,8 +2221,8 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_disabledClearinghouse_reverts() public givenPolicyActive + givenActivated givenCoolerB(dai) - givenEmergencyHasRole { // Disable the Clearinghouse vm.prank(emergency); @@ -2181,9 +2255,10 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_protocolFee_daiToUsds() public - givenAdminHasRole givenPolicyActive + givenActivated givenCoolerB(usds) + givenAdminHasRole givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -2236,9 +2311,10 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_protocolFee_usdsToDai() public - givenAdminHasRole givenPolicyActive + givenActivated givenCoolerB(dai) + givenAdminHasRole givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -2296,9 +2372,10 @@ contract LoanConsolidatorForkTest is Test { function test_consolidateWithNewOwner_protocolFee_usdsToUsds() public - givenAdminHasRole givenPolicyActive + givenActivated givenCoolerB(usds) + givenAdminHasRole givenProtocolFee(1000) // 1% { uint256[] memory idsA = _idsA(); @@ -2519,8 +2596,8 @@ contract LoanConsolidatorForkTest is Test { function test_requiredApprovals_protocolFee() public - givenAdminHasRole givenPolicyActive + givenAdminHasRole givenProtocolFee(1000) // 1% { uint256[] memory ids = _idsA(); @@ -2559,8 +2636,8 @@ contract LoanConsolidatorForkTest is Test { function test_requiredApprovals_protocolFee_daiToUsds() public - givenAdminHasRole givenPolicyActive + givenAdminHasRole givenProtocolFee(1000) // 1% { uint256[] memory ids = _idsA(); @@ -2599,8 +2676,8 @@ contract LoanConsolidatorForkTest is Test { function test_requiredApprovals_protocolFee_usdsToDai() public - givenAdminHasRole givenPolicyActive + givenAdminHasRole givenProtocolFee(1000) // 1% { uint256[] memory ids = _idsA(); @@ -2643,8 +2720,8 @@ contract LoanConsolidatorForkTest is Test { function test_requiredApprovals_protocolFee_usdsToUsds() public - givenAdminHasRole givenPolicyActive + givenAdminHasRole givenProtocolFee(1000) // 1% { uint256[] memory ids = _idsA(); @@ -2845,8 +2922,8 @@ contract LoanConsolidatorForkTest is Test { function test_fundsRequired_interestDue() public - givenAdminHasRole givenPolicyActive + givenAdminHasRole givenProtocolFee(1000) { // Warp to the future, so that there is interest due @@ -2879,8 +2956,8 @@ contract LoanConsolidatorForkTest is Test { function test_fundsRequired_toUsds() public - givenAdminHasRole givenPolicyActive + givenAdminHasRole givenProtocolFee(1000) { uint256[] memory ids = _idsA(); @@ -2908,7 +2985,7 @@ contract LoanConsolidatorForkTest is Test { assertEq(protocolFee, expectedProtocolFee, "protocolFee"); } - function test_fundsRequired_toDai() public givenAdminHasRole givenPolicyActive { + function test_fundsRequired_toDai() public givenPolicyActive givenAdminHasRole { uint256[] memory ids = _idsA(); // Calculate the interest due @@ -2965,7 +3042,7 @@ contract LoanConsolidatorForkTest is Test { assertEq(address(utils.kernel()), address(kernel), "kernel"); assertEq(utils.feePercentage(), feePercentage, "fee percentage"); - assertEq(utils.consolidatorActive(), true, "consolidator active"); + assertEq(utils.consolidatorActive(), false, "consolidator should be inactive"); } // activate @@ -2980,11 +3057,7 @@ contract LoanConsolidatorForkTest is Test { // [X] it does nothing // [X] it sets the active flag to true - function test_activate_policyNotActive_reverts() - public - givenAdminHasRole - givenEmergencyHasRole - { + function test_activate_policyNotActive_reverts() public givenAdminHasRole { // Expect revert vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyPolicyActive.selector)); @@ -2994,10 +3067,9 @@ contract LoanConsolidatorForkTest is Test { function test_activate_notAdminOrEmergency_reverts() public - givenAdminHasRole - givenEmergencyHasRole givenPolicyActive givenDeactivated + givenAdminHasRole { // Expect revert bytes memory err = abi.encodeWithSelector( @@ -3011,10 +3083,9 @@ contract LoanConsolidatorForkTest is Test { function test_activate_asAdmin_reverts() public - givenAdminHasRole - givenEmergencyHasRole givenPolicyActive givenDeactivated + givenAdminHasRole { // Expect revert bytes memory err = abi.encodeWithSelector( @@ -3029,10 +3100,9 @@ contract LoanConsolidatorForkTest is Test { function test_activate_asEmergency() public - givenAdminHasRole - givenEmergencyHasRole givenPolicyActive givenDeactivated + givenAdminHasRole { vm.prank(emergency); utils.activate(); @@ -3040,12 +3110,7 @@ contract LoanConsolidatorForkTest is Test { assertTrue(utils.consolidatorActive(), "consolidator active"); } - function test_activate_asEmergency_alreadyActive() - public - givenAdminHasRole - givenEmergencyHasRole - givenPolicyActive - { + function test_activate_asEmergency_alreadyActive() public givenPolicyActive givenAdminHasRole { vm.prank(emergency); utils.activate(); @@ -3064,11 +3129,7 @@ contract LoanConsolidatorForkTest is Test { // [X] it does nothing // [X] it sets the active flag to false - function test_deactivate_policyNotActive_reverts() - public - givenAdminHasRole - givenEmergencyHasRole - { + function test_deactivate_policyNotActive_reverts() public givenAdminHasRole { // Expect revert vm.expectRevert(abi.encodeWithSelector(LoanConsolidator.OnlyPolicyActive.selector)); @@ -3078,9 +3139,8 @@ contract LoanConsolidatorForkTest is Test { function test_deactivate_notAdminOrEmergency_reverts() public - givenAdminHasRole - givenEmergencyHasRole givenPolicyActive + givenAdminHasRole { // Expect revert vm.expectRevert( @@ -3090,12 +3150,7 @@ contract LoanConsolidatorForkTest is Test { utils.deactivate(); } - function test_deactivate_asAdmin_reverts() - public - givenAdminHasRole - givenEmergencyHasRole - givenPolicyActive - { + function test_deactivate_asAdmin_reverts() public givenPolicyActive givenAdminHasRole { // Expect revert vm.expectRevert( abi.encodeWithSelector(ROLESv1.ROLES_RequireRole.selector, ROLE_EMERGENCY_SHUTDOWN) @@ -3105,12 +3160,7 @@ contract LoanConsolidatorForkTest is Test { utils.deactivate(); } - function test_deactivate_asEmergency() - public - givenAdminHasRole - givenEmergencyHasRole - givenPolicyActive - { + function test_deactivate_asEmergency() public givenPolicyActive givenAdminHasRole { vm.prank(emergency); utils.deactivate(); @@ -3119,10 +3169,9 @@ contract LoanConsolidatorForkTest is Test { function test_deactivate_asEmergency_alreadyDeactivated() public - givenAdminHasRole - givenEmergencyHasRole givenPolicyActive givenDeactivated + givenAdminHasRole { vm.prank(emergency); utils.deactivate(); From 1937b86ffcb9147bfc8efcddca6321cb1f37b6ff Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 30 Oct 2024 16:16:27 +0400 Subject: [PATCH 087/160] WIP OCG proposal --- src/proposals/LoanConsolidator.sol | 172 ++++++++++++++++++ src/proposals/addresses.json | 7 +- .../proposals/LoanConsolidatorProposal.t.sol | 88 +++++++++ 3 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 src/proposals/LoanConsolidator.sol create mode 100644 src/test/proposals/LoanConsolidatorProposal.t.sol diff --git a/src/proposals/LoanConsolidator.sol b/src/proposals/LoanConsolidator.sol new file mode 100644 index 000000000..faede1b95 --- /dev/null +++ b/src/proposals/LoanConsolidator.sol @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; +import {console2} from "forge-std/console2.sol"; + +// OCG Proposal Simulator +import {Addresses} from "proposal-sim/addresses/Addresses.sol"; +import {GovernorBravoProposal} from "proposal-sim/proposals/OlympusGovernorBravoProposal.sol"; +// Interfaces +import {IERC20} from "forge-std/interfaces/IERC20.sol"; +import {IERC4626} from "forge-std/interfaces/IERC4626.sol"; +// Olympus Kernel, Modules, and Policies +import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; +import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; +import {RolesAdmin} from "src/policies/RolesAdmin.sol"; +import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; +import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; + +/// @notice Activates an updated LoanConsolidator policy. +contract LoanConsolidatorProposal is GovernorBravoProposal { + Kernel internal _kernel; + + // Returns the id of the proposal. + function id() public view override returns (uint256) { + return 3; + } + + // Returns the name of the proposal. + function name() public pure override returns (string memory) { + return "LoanConsolidator and Contract Registry Activation"; + } + + // Provides a brief description of the proposal. + function description() public pure override returns (string memory) { + // TODO add link to audit report + return string.concat( + "# LoanConsolidator and Contract Registry Activation\n\n", + "This proposal activates the LoanConsolidator policy and installs the Contract Registry module (and associated ContractRegistryAdmin configuration policy).\n\n", + "The Contract Registry module is used to register commonly-used addresses that can be referenced by other contracts. These addresses are marked as either mutable or immutable.\n\n", + "The previous version of LoanConsolidator contained logic that, combined with infinite approvals, enabled an attacker to steal funds from users of the CoolerUtils contract (as it was known at the time).\n\n", + "This version introduces the following:\n\n", + "- Strict checks on callers, ownership and Clearinghouse validity\n", + "- Allows for migration of loans from one Clearinghouse to another (in preparation for a USDS Clearinghouse)\n", + "- Allows for migration of loans from one owner to another\n\n", + "The audit report can be found at:\n\n", + "## Assumptions\n\n", + "- The Contract Registry module has been deployed and activated as a module by the DAO MS.\n", + "- The ContractRegistryAdmin policy has been deployed and activated as a policy by the DAO MS.\n", + "- The LoanConsolidator contract has been deployed and activated as a policy by the DAO MS.\n", + "- However, the consolidator is disabled by default.\n\n", + "## Proposal Steps\n\n", + "1. Grant the `loan_consolidator_admin` role to the OCG Timelock.\n", + "2. Grant the `contract_registry_admin` role to the OCG Timelock.\n", + "3. Activate the LoanConsolidator." + ); + } + + // No deploy actions needed + function _deploy(Addresses addresses, address) internal override { + // Cache the kernel address in state + _kernel = Kernel(addresses.getAddress("olympus-kernel")); + } + + function _afterDeploy(Addresses addresses, address deployer) internal override {} + + // Sets up actions for the proposal + function _build(Addresses addresses) internal override { + address rolesAdmin = addresses.getAddress("olympus-policy-roles-admin"); + address timelock = addresses.getAddress("olympus-timelock"); + address loanConsolidator = addresses.getAddress("olympus-policy-loan-consolidator"); + + // STEP 1: Grant the `loan_consolidator_admin` role to the OCG Timelock + _pushAction( + rolesAdmin, + abi.encodeWithSelector( + RolesAdmin.grantRole.selector, + bytes32("loan_consolidator_admin"), + timelock + ), + "Grant loan_consolidator_admin to Timelock" + ); + + // STEP 2: Grant the `contract_registry_admin` role to the OCG Timelock + _pushAction( + rolesAdmin, + abi.encodeWithSelector(RolesAdmin.grantRole.selector, bytes32("contract_registry_admin"), timelock), + "Grant contract_registry_admin to Timelock" + ); + + // STEP 3: Activate the LoanConsolidator + _pushAction( + loanConsolidator, + abi.encodeWithSelector(LoanConsolidator.activate.selector), + "Activate LoanConsolidator" + ); + } + + // Executes the proposal actions. + function _run(Addresses addresses, address) internal override { + // Simulates actions on TimelockController + _simulateActions( + address(_kernel), + addresses.getAddress("olympus-governor"), + addresses.getAddress("olympus-legacy-gohm"), + addresses.getAddress("proposer") + ); + } + + // Validates the post-execution state. + function _validate(Addresses addresses, address) internal override { + // Load the contract addresses + ROLESv1 roles = ROLESv1(addresses.getAddress("olympus-module-roles")); + RolesAdmin rolesAdmin = RolesAdmin(addresses.getAddress("olympus-policy-roles-admin")); + address timelock = addresses.getAddress("olympus-timelock"); + LoanConsolidator loanConsolidator = LoanConsolidator( + addresses.getAddress("olympus-policy-loan-consolidator") + ); + address emergencyMS = addresses.getAddress("olympus-multisig-emergency"); + + // Validate that the emergency MS has the emergency shutdown role + require( + roles.hasRole(emergencyMS, bytes32("emergency_shutdown")), + "Emergency MS does not have the emergency_shutdown role" + ); + + // Validate that the OCG timelock has the emergency shutdown role + require( + roles.hasRole(timelock, bytes32("emergency_shutdown")), + "Timelock does not have the emergency_shutdown role" + ); + + // Validate that the OCG timelock has the loan_consolidator_admin role + require( + roles.hasRole(timelock, bytes32("loan_consolidator_admin")), + "Timelock does not have the loan_consolidator_admin role" + ); + + // Validate that the OCG timelock has the contract_registry_admin role + require( + roles.hasRole(timelock, bytes32("contract_registry_admin")), + "Timelock does not have the contract_registry_admin role" + ); + + // Validate that the LoanConsolidator is active + require(loanConsolidator.consolidatorActive(), "LoanConsolidator is not active"); + } +} + +import {ScriptSuite} from "proposal-sim/script/ScriptSuite.s.sol"; + +// @notice GovernorBravoScript is a script that runs BRAVO_01 proposal. +// BRAVO_01 proposal deploys a Vault contract and an ERC20 token contract +// Then the proposal transfers ownership of both Vault and ERC20 to the timelock address +// Finally the proposal whitelist the ERC20 token in the Vault contract +// @dev Use this script to simulates or run a single proposal +// Use this as a template to create your own script +// `forge script script/GovernorBravo.s.sol:GovernorBravoScript -vvvv --rpc-url {rpc} --broadcast --verify --etherscan-api-key {key}` +contract LoanConsolidatorProposalScript is ScriptSuite { + string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; + + constructor() ScriptSuite(ADDRESSES_PATH, new LoanConsolidatorProposal()) {} + + function run() public override { + // set debug mode to true and run it to build the actions list + proposal.setDebug(true); + + // run the proposal to build it + proposal.run(addresses, address(0)); + + // get the calldata for the proposal, doing so in debug mode prints it to the console + proposal.getCalldata(); + } +} diff --git a/src/proposals/addresses.json b/src/proposals/addresses.json index deafc7865..fa27fa7d8 100644 --- a/src/proposals/addresses.json +++ b/src/proposals/addresses.json @@ -78,5 +78,10 @@ "addr": "0x6CAfd730Dc199Df73C16420C4fCAb18E3afbfA59", "name": "olympus-module-roles", "chainId": 1 + }, + { + "addr": "0x0000000000000000000000000000000000000000", + "name": "olympus-policy-loan-consolidator", + "chainId": 1 } -] \ No newline at end of file +] diff --git a/src/test/proposals/LoanConsolidatorProposal.t.sol b/src/test/proposals/LoanConsolidatorProposal.t.sol new file mode 100644 index 000000000..315876353 --- /dev/null +++ b/src/test/proposals/LoanConsolidatorProposal.t.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +// Proposal test-suite imports +import "forge-std/Test.sol"; +import {TestSuite} from "proposal-sim/test/TestSuite.t.sol"; +import {Addresses} from "proposal-sim/addresses/Addresses.sol"; +import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; +import {RolesAdmin} from "policies/RolesAdmin.sol"; +import {GovernorBravoDelegator} from "src/external/governance/GovernorBravoDelegator.sol"; +import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; +import {Timelock} from "src/external/governance/Timelock.sol"; + +// LoanConsolidator imports +import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; +import {LoanConsolidator} from "proposals/LoanConsolidator.sol"; + +/// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. +/// @dev Update the `setUp` function to deploy your proposal and set the submission +/// flag to `true` once the proposal has been submitted on-chain. +/// Note: this will fail if the OCGPermissions script has not been run yet. +contract LoanConsolidatorProposalTest is Test { + string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; + TestSuite public suite; + Addresses public addresses; + + // Wether the proposal has been submitted or not. + // If true, the framework will check that calldatas match. + bool public hasBeenSubmitted; + + string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); + + /// @notice Creates a sandboxed environment from a mainnet fork. + function setUp() public virtual { + // Mainnet Fork at a fixed block + // Prior to the proposal deployment (otherwise it will fail) + vm.createSelectFork(RPC_URL, 21070000); + + address kernel = addresses.getAddress("olympus-kernel"); + + // TODO RGSTY, ContractRegistryAdmin, LoanConsolidator + + /// @dev Deploy your proposal + LoanConsolidatorProposal proposal = new LoanConsolidatorProposal(); + + /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. + hasBeenSubmitted = false; + + /// [DO NOT DELETE] + /// @notice This section is used to simulate the proposal on the mainnet fork. + { + // Populate addresses array + address[] memory proposalsAddresses = new address[](1); + proposalsAddresses[0] = address(proposal); + + // Deploy TestSuite contract + suite = new TestSuite(ADDRESSES_PATH, proposalsAddresses); + + // Set addresses object + addresses = suite.addresses(); + + // Set debug mode + suite.setDebug(true); + // Execute proposals + suite.testProposals(); + + // Proposals execution may change addresses, so we need to update the addresses object. + addresses = suite.addresses(); + + // Check if simulated calldatas match the ones from mainnet. + if (hasBeenSubmitted) { + address governor = addresses.getAddress("olympus-governor"); + bool[] memory matches = suite.checkProposalCalldatas(governor); + for (uint256 i; i < matches.length; i++) { + assertTrue(matches[i]); + } + } else { + console.log("\n\n------- Calldata check (simulation vs mainnet) -------\n"); + console.log("Proposal has NOT been submitted on-chain yet.\n"); + } + } + } + + // [DO NOT DELETE] Dummy test to ensure `setUp` is executed and the proposal simulated. + function testProposal_simulate() public { + assertTrue(true); + } +} From 9692a920def107cfada3c0bdb85eef0757385900 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 31 Oct 2024 10:51:39 +0400 Subject: [PATCH 088/160] Fix compilation error --- src/test/proposals/LoanConsolidatorProposal.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/proposals/LoanConsolidatorProposal.t.sol b/src/test/proposals/LoanConsolidatorProposal.t.sol index 315876353..53616e5cf 100644 --- a/src/test/proposals/LoanConsolidatorProposal.t.sol +++ b/src/test/proposals/LoanConsolidatorProposal.t.sol @@ -13,7 +13,7 @@ import {Timelock} from "src/external/governance/Timelock.sol"; // LoanConsolidator imports import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; -import {LoanConsolidator} from "proposals/LoanConsolidator.sol"; +import {LoanConsolidatorProposal} from "proposals/LoanConsolidator.sol"; /// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. /// @dev Update the `setUp` function to deploy your proposal and set the submission From f368b39efb23dadcf6761f21e851d3c538f59ec1 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 31 Oct 2024 11:00:51 +0400 Subject: [PATCH 089/160] Rename proposal file. Linting. --- .../{LoanConsolidator.sol => LoanConsolidatorProposal.sol} | 5 ++--- src/test/proposals/LoanConsolidatorProposal.t.sol | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) rename src/proposals/{LoanConsolidator.sol => LoanConsolidatorProposal.sol} (97%) diff --git a/src/proposals/LoanConsolidator.sol b/src/proposals/LoanConsolidatorProposal.sol similarity index 97% rename from src/proposals/LoanConsolidator.sol rename to src/proposals/LoanConsolidatorProposal.sol index faede1b95..489dcaa11 100644 --- a/src/proposals/LoanConsolidator.sol +++ b/src/proposals/LoanConsolidatorProposal.sol @@ -20,7 +20,7 @@ contract LoanConsolidatorProposal is GovernorBravoProposal { Kernel internal _kernel; // Returns the id of the proposal. - function id() public view override returns (uint256) { + function id() public pure override returns (uint256) { return 3; } @@ -106,10 +106,9 @@ contract LoanConsolidatorProposal is GovernorBravoProposal { } // Validates the post-execution state. - function _validate(Addresses addresses, address) internal override { + function _validate(Addresses addresses, address) internal view override { // Load the contract addresses ROLESv1 roles = ROLESv1(addresses.getAddress("olympus-module-roles")); - RolesAdmin rolesAdmin = RolesAdmin(addresses.getAddress("olympus-policy-roles-admin")); address timelock = addresses.getAddress("olympus-timelock"); LoanConsolidator loanConsolidator = LoanConsolidator( addresses.getAddress("olympus-policy-loan-consolidator") diff --git a/src/test/proposals/LoanConsolidatorProposal.t.sol b/src/test/proposals/LoanConsolidatorProposal.t.sol index 53616e5cf..2c30fae3b 100644 --- a/src/test/proposals/LoanConsolidatorProposal.t.sol +++ b/src/test/proposals/LoanConsolidatorProposal.t.sol @@ -13,7 +13,7 @@ import {Timelock} from "src/external/governance/Timelock.sol"; // LoanConsolidator imports import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; -import {LoanConsolidatorProposal} from "proposals/LoanConsolidator.sol"; +import {LoanConsolidatorProposal} from "src/proposals/LoanConsolidatorProposal.sol"; /// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. /// @dev Update the `setUp` function to deploy your proposal and set the submission From 4767cebd664e576ad18bc6d0596464dd032d6c92 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 31 Oct 2024 12:42:51 +0400 Subject: [PATCH 090/160] Split into two OCG proposals: ContractRegistry and LoanConsolidator. Add multisig batches for installation. --- src/proposals/ContractRegistryProposal.sol | 235 ++++++++++++++++++ src/proposals/LoanConsolidatorProposal.sol | 54 ++-- src/proposals/addresses.json | 5 + .../ops/batches/ContractRegistryInstall.sol | 51 ++++ .../ops/batches/LoanConsolidatorInstall.sol | 42 ++++ .../proposals/ContractRegistryProposal.t.sol | 88 +++++++ .../proposals/LoanConsolidatorProposal.t.sol | 7 +- 7 files changed, 446 insertions(+), 36 deletions(-) create mode 100644 src/proposals/ContractRegistryProposal.sol create mode 100644 src/scripts/ops/batches/ContractRegistryInstall.sol create mode 100644 src/scripts/ops/batches/LoanConsolidatorInstall.sol create mode 100644 src/test/proposals/ContractRegistryProposal.t.sol diff --git a/src/proposals/ContractRegistryProposal.sol b/src/proposals/ContractRegistryProposal.sol new file mode 100644 index 000000000..7ca43ed37 --- /dev/null +++ b/src/proposals/ContractRegistryProposal.sol @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; +import {console2} from "forge-std/console2.sol"; +import {ScriptSuite} from "proposal-sim/script/ScriptSuite.s.sol"; + +// OCG Proposal Simulator +import {Addresses} from "proposal-sim/addresses/Addresses.sol"; +import {GovernorBravoProposal} from "proposal-sim/proposals/OlympusGovernorBravoProposal.sol"; +// Interfaces +import {IERC20} from "forge-std/interfaces/IERC20.sol"; +import {IERC4626} from "forge-std/interfaces/IERC4626.sol"; +// Olympus Kernel, Modules, and Policies +import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; +import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; +import {RolesAdmin} from "src/policies/RolesAdmin.sol"; +import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; +import {ContractRegistryAdmin} from "src/policies/ContractRegistryAdmin.sol"; +import {RGSTYv1} from "src/modules/RGSTY/RGSTY.v1.sol"; + +/// @notice Activates the contract registry module and associated configuration policy. +contract ContractRegistryProposal is GovernorBravoProposal { + Kernel internal _kernel; + + // Immutable contract addresses + address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; + address public constant SDAI = 0x83F20F44975D03b1b09e64809B757c47f942BEeA; + address public constant USDS = 0xdC035D45d973E3EC169d2276DDab16f1e407384F; + address public constant SUSDS = 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD; + address public constant GOHM = 0x0ab87046fBb341D058F17CBC4c1133F25a20a52f; + address public constant OHM = 0x64aa3364F17a4D01c6f1751Fd97C2BD3D7e7f1D5; + + // Mutable contract addresses + address public constant FLASH_LENDER = 0x60744434d6339a6B27d73d9Eda62b6F66a0a04FA; + address public constant DAI_USDS_MIGRATOR = 0x3225737a9Bbb6473CB4a45b7244ACa2BeFdB276A; + + // Returns the id of the proposal. + function id() public pure override returns (uint256) { + return 3; + } + + // Returns the name of the proposal. + function name() public pure override returns (string memory) { + return "Contract Registry Activation"; + } + + // Provides a brief description of the proposal. + function description() public pure override returns (string memory) { + // TODO add link to audit report + return + string.concat( + "# Contract Registry Activation\n\n", + "This proposal activates the RGSTY module (and associated ContractRegistryAdmin configuration policy).\n\n", + "The RGSTY module is used to register commonly-used addresses that can be referenced by other contracts. These addresses are marked as either mutable or immutable.\n\n", + "The ContractRegistryAdmin policy is used to manage the addresses registered in the RGSTY module.\n\n", + "The RGSTY module will be used by the LoanConsolidator policy to lookup contract addresses. In order to roll-out the improved LoanConsolidator, this proposal must be executed first.\n\n", + "The audit report can be found at:\n\n", + "## Assumptions\n\n", + "- The RGSTY module has been deployed and activated as a module by the DAO MS.\n", + "- The ContractRegistryAdmin policy has been deployed and activated as a policy by the DAO MS.\n\n", + "## Proposal Steps\n\n", + "1. Grant the `contract_registry_admin` role to the OCG Timelock.\n", + "2. Register immutable addresses for DAI, SDAI, USDS, SUSDS, GOHM and OHM.\n", + "3. Register mutable addresses for the Flash Lender and DAI-USDS Migrator contracts." + ); + } + + // No deploy actions needed + function _deploy(Addresses addresses, address) internal override { + // Cache the kernel address in state + _kernel = Kernel(addresses.getAddress("olympus-kernel")); + } + + function _afterDeploy(Addresses addresses, address deployer) internal override {} + + // Sets up actions for the proposal + function _build(Addresses addresses) internal override { + address rolesAdmin = addresses.getAddress("olympus-policy-roles-admin"); + address timelock = addresses.getAddress("olympus-timelock"); + address contractRegistryAdmin = addresses.getAddress( + "olympus-policy-contract-registry-admin" + ); + + // STEP 1: Grant the `contract_registry_admin` role to the OCG Timelock + _pushAction( + rolesAdmin, + abi.encodeWithSelector( + RolesAdmin.grantRole.selector, + bytes32("contract_registry_admin"), + timelock + ), + "Grant contract_registry_admin to Timelock" + ); + + // STEP 2: Register immutable addresses for DAI, SDAI, USDS, SUSDS, GOHM and OHM + _pushAction( + contractRegistryAdmin, + abi.encodeWithSelector( + ContractRegistryAdmin.registerImmutableContract.selector, + "dai", + DAI + ), + "Register immutable DAI address" + ); + _pushAction( + contractRegistryAdmin, + abi.encodeWithSelector( + ContractRegistryAdmin.registerImmutableContract.selector, + "sdai", + SDAI + ), + "Register immutable SDAI address" + ); + _pushAction( + contractRegistryAdmin, + abi.encodeWithSelector( + ContractRegistryAdmin.registerImmutableContract.selector, + "usds", + USDS + ), + "Register immutable USDS address" + ); + _pushAction( + contractRegistryAdmin, + abi.encodeWithSelector( + ContractRegistryAdmin.registerImmutableContract.selector, + "susds", + SUSDS + ), + "Register immutable SUSDS address" + ); + _pushAction( + contractRegistryAdmin, + abi.encodeWithSelector( + ContractRegistryAdmin.registerImmutableContract.selector, + "gohm", + GOHM + ), + "Register immutable GOHM address" + ); + _pushAction( + contractRegistryAdmin, + abi.encodeWithSelector( + ContractRegistryAdmin.registerImmutableContract.selector, + "ohm", + OHM + ), + "Register immutable OHM address" + ); + + // STEP 3: Register mutable addresses for the Flash Lender and DAI-USDS Migrator contracts + _pushAction( + contractRegistryAdmin, + abi.encodeWithSelector( + ContractRegistryAdmin.registerContract.selector, + "flash", + FLASH_LENDER + ), + "Register mutable Flash Lender address" + ); + _pushAction( + contractRegistryAdmin, + abi.encodeWithSelector( + ContractRegistryAdmin.registerContract.selector, + "dmgtr", + DAI_USDS_MIGRATOR + ), + "Register mutable DAI-USDS Migrator address" + ); + } + + // Executes the proposal actions. + function _run(Addresses addresses, address) internal override { + // Simulates actions on TimelockController + _simulateActions( + address(_kernel), + addresses.getAddress("olympus-governor"), + addresses.getAddress("olympus-legacy-gohm"), + addresses.getAddress("proposer") + ); + } + + // Validates the post-execution state. + function _validate(Addresses addresses, address) internal view override { + // Load the contract addresses + ROLESv1 roles = ROLESv1(addresses.getAddress("olympus-module-roles")); + address timelock = addresses.getAddress("olympus-timelock"); + address rgsty = addresses.getAddress("olympus-module-rgsty"); + RGSTYv1 RGSTY = RGSTYv1(rgsty); + + // Validate that the OCG timelock has the contract_registry_admin role + require( + roles.hasRole(timelock, bytes32("contract_registry_admin")), + "Timelock does not have the contract_registry_admin role" + ); + + // Validate that the DAI, SDAI, USDS, SUSDS, GOHM and OHM addresses are registered and immutable + require(RGSTY.getImmutableContract("dai") == DAI, "DAI address is not immutable"); + require(RGSTY.getImmutableContract("sdai") == SDAI, "SDAI address is not immutable"); + require(RGSTY.getImmutableContract("usds") == USDS, "USDS address is not immutable"); + require(RGSTY.getImmutableContract("susds") == SUSDS, "SUSDS address is not immutable"); + require(RGSTY.getImmutableContract("gohm") == GOHM, "GOHM address is not immutable"); + require(RGSTY.getImmutableContract("ohm") == OHM, "OHM address is not immutable"); + + // Validate that the Flash Lender and DAI-USDS Migrator addresses are registered and mutable + require(RGSTY.getContract("flash") == FLASH_LENDER, "Flash Lender address is not mutable"); + require( + RGSTY.getContract("dmgtr") == DAI_USDS_MIGRATOR, + "DAI-USDS Migrator address is not mutable" + ); + } +} + +// @notice GovernorBravoScript is a script that runs BRAVO_01 proposal. +// BRAVO_01 proposal deploys a Vault contract and an ERC20 token contract +// Then the proposal transfers ownership of both Vault and ERC20 to the timelock address +// Finally the proposal whitelist the ERC20 token in the Vault contract +// @dev Use this script to simulates or run a single proposal +// Use this as a template to create your own script +// `forge script script/GovernorBravo.s.sol:GovernorBravoScript -vvvv --rpc-url {rpc} --broadcast --verify --etherscan-api-key {key}` +contract ContractRegistryProposalScript is ScriptSuite { + string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; + + constructor() ScriptSuite(ADDRESSES_PATH, new ContractRegistryProposal()) {} + + function run() public override { + // set debug mode to true and run it to build the actions list + proposal.setDebug(true); + + // run the proposal to build it + proposal.run(addresses, address(0)); + + // get the calldata for the proposal, doing so in debug mode prints it to the console + proposal.getCalldata(); + } +} diff --git a/src/proposals/LoanConsolidatorProposal.sol b/src/proposals/LoanConsolidatorProposal.sol index 489dcaa11..04310725b 100644 --- a/src/proposals/LoanConsolidatorProposal.sol +++ b/src/proposals/LoanConsolidatorProposal.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {console2} from "forge-std/console2.sol"; +import {ScriptSuite} from "proposal-sim/script/ScriptSuite.s.sol"; // OCG Proposal Simulator import {Addresses} from "proposal-sim/addresses/Addresses.sol"; @@ -21,7 +22,7 @@ contract LoanConsolidatorProposal is GovernorBravoProposal { // Returns the id of the proposal. function id() public pure override returns (uint256) { - return 3; + return 4; } // Returns the name of the proposal. @@ -32,26 +33,26 @@ contract LoanConsolidatorProposal is GovernorBravoProposal { // Provides a brief description of the proposal. function description() public pure override returns (string memory) { // TODO add link to audit report - return string.concat( - "# LoanConsolidator and Contract Registry Activation\n\n", - "This proposal activates the LoanConsolidator policy and installs the Contract Registry module (and associated ContractRegistryAdmin configuration policy).\n\n", - "The Contract Registry module is used to register commonly-used addresses that can be referenced by other contracts. These addresses are marked as either mutable or immutable.\n\n", - "The previous version of LoanConsolidator contained logic that, combined with infinite approvals, enabled an attacker to steal funds from users of the CoolerUtils contract (as it was known at the time).\n\n", - "This version introduces the following:\n\n", - "- Strict checks on callers, ownership and Clearinghouse validity\n", - "- Allows for migration of loans from one Clearinghouse to another (in preparation for a USDS Clearinghouse)\n", - "- Allows for migration of loans from one owner to another\n\n", - "The audit report can be found at:\n\n", - "## Assumptions\n\n", - "- The Contract Registry module has been deployed and activated as a module by the DAO MS.\n", - "- The ContractRegistryAdmin policy has been deployed and activated as a policy by the DAO MS.\n", - "- The LoanConsolidator contract has been deployed and activated as a policy by the DAO MS.\n", - "- However, the consolidator is disabled by default.\n\n", - "## Proposal Steps\n\n", - "1. Grant the `loan_consolidator_admin` role to the OCG Timelock.\n", - "2. Grant the `contract_registry_admin` role to the OCG Timelock.\n", - "3. Activate the LoanConsolidator." - ); + return + string.concat( + "# LoanConsolidator and Contract Registry Activation\n\n", + "This proposal activates the LoanConsolidator policy and installs the Contract Registry module (and associated ContractRegistryAdmin configuration policy).\n\n", + "The Contract Registry module is used to register commonly-used addresses that can be referenced by other contracts. These addresses are marked as either mutable or immutable.\n\n", + "The previous version of LoanConsolidator contained logic that, combined with infinite approvals, enabled an attacker to steal funds from users of the CoolerUtils contract (as it was known at the time).\n\n", + "This version introduces the following:\n\n", + "- Strict checks on callers, ownership and Clearinghouse validity\n", + "- Allows for migration of loans from one Clearinghouse to another (in preparation for a USDS Clearinghouse)\n", + "- Allows for migration of loans from one owner to another\n\n", + "The audit report can be found at:\n\n", + "## Assumptions\n\n", + "- The Contract Registry module has been deployed and activated as a module by the DAO MS.\n", + "- The ContractRegistryAdmin policy has been deployed and activated as a policy by the DAO MS.\n", + "- The mutable and immutable contract addresses required by LoanConsolidator have been registered in the Contract Registry.\n", + "- The LoanConsolidator contract has been deployed and activated as a policy by the DAO MS.\n\n", + "## Proposal Steps\n\n", + "1. Grant the `loan_consolidator_admin` role to the OCG Timelock.\n", + "2. Activate the LoanConsolidator." + ); } // No deploy actions needed @@ -79,14 +80,7 @@ contract LoanConsolidatorProposal is GovernorBravoProposal { "Grant loan_consolidator_admin to Timelock" ); - // STEP 2: Grant the `contract_registry_admin` role to the OCG Timelock - _pushAction( - rolesAdmin, - abi.encodeWithSelector(RolesAdmin.grantRole.selector, bytes32("contract_registry_admin"), timelock), - "Grant contract_registry_admin to Timelock" - ); - - // STEP 3: Activate the LoanConsolidator + // STEP 2: Activate the LoanConsolidator _pushAction( loanConsolidator, abi.encodeWithSelector(LoanConsolidator.activate.selector), @@ -144,8 +138,6 @@ contract LoanConsolidatorProposal is GovernorBravoProposal { } } -import {ScriptSuite} from "proposal-sim/script/ScriptSuite.s.sol"; - // @notice GovernorBravoScript is a script that runs BRAVO_01 proposal. // BRAVO_01 proposal deploys a Vault contract and an ERC20 token contract // Then the proposal transfers ownership of both Vault and ERC20 to the timelock address diff --git a/src/proposals/addresses.json b/src/proposals/addresses.json index fa27fa7d8..4c7a9ee53 100644 --- a/src/proposals/addresses.json +++ b/src/proposals/addresses.json @@ -83,5 +83,10 @@ "addr": "0x0000000000000000000000000000000000000000", "name": "olympus-policy-loan-consolidator", "chainId": 1 + }, + { + "addr": "0x0000000000000000000000000000000000000000", + "name": "olympus-policy-contract-registry-admin", + "chainId": 1 } ] diff --git a/src/scripts/ops/batches/ContractRegistryInstall.sol b/src/scripts/ops/batches/ContractRegistryInstall.sol new file mode 100644 index 000000000..b58a12c21 --- /dev/null +++ b/src/scripts/ops/batches/ContractRegistryInstall.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity 0.8.15; + +import {console2} from "forge-std/console2.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {OlyBatch} from "src/scripts/ops/OlyBatch.sol"; + +// Bophades +import {Kernel, Actions} from "src/Kernel.sol"; + +/// @notice Installs the RGSTY module and the ContractRegistryAdmin policy +contract ContractRegistryInstall is OlyBatch { + address kernel; + address rolesAdmin; + address rgsty; + address contractRegistryAdmin; + + function loadEnv() internal override { + // Load contract addresses from the environment file + kernel = envAddress("current", "olympus.Kernel"); + rolesAdmin = envAddress("current", "olympus.policies.RolesAdmin"); + rgsty = envAddress("current", "olympus.modules.RGSTY"); + contractRegistryAdmin = envAddress("current", "olympus.policies.ContractRegistryAdmin"); + } + + // Entry point for the batch #1 + function script1_install(bool send_) external isDaoBatch(send_) { + // RGSTY Install Script + + // Validate addresses + require(rgsty != address(0), "RGSTY address is not set"); + require(contractRegistryAdmin != address(0), "ContractRegistryAdmin address is not set"); + + // A. Kernel Actions + // A.1. Install the RGSTY module on the kernel + addToBatch( + kernel, + abi.encodeWithSelector(Kernel.executeAction.selector, Actions.InstallModule, rgsty) + ); + + // A.2. Install the ContractRegistryAdmin policy on the kernel + addToBatch( + kernel, + abi.encodeWithSelector( + Kernel.executeAction.selector, + Actions.ActivatePolicy, + contractRegistryAdmin + ) + ); + } +} diff --git a/src/scripts/ops/batches/LoanConsolidatorInstall.sol b/src/scripts/ops/batches/LoanConsolidatorInstall.sol new file mode 100644 index 000000000..84c731012 --- /dev/null +++ b/src/scripts/ops/batches/LoanConsolidatorInstall.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity 0.8.15; + +import {console2} from "forge-std/console2.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {OlyBatch} from "src/scripts/ops/OlyBatch.sol"; + +// Bophades +import {Kernel, Actions} from "src/Kernel.sol"; + +/// @notice Installs the LoanConsolidator policy +contract LoanConsolidatorInstall is OlyBatch { + address kernel; + address rolesAdmin; + address loanConsolidator; + + function loadEnv() internal override { + // Load contract addresses from the environment file + kernel = envAddress("current", "olympus.Kernel"); + rolesAdmin = envAddress("current", "olympus.policies.RolesAdmin"); + loanConsolidator = envAddress("current", "olympus.policies.LoanConsolidator"); + } + + // Entry point for the batch #1 + function script1_install(bool send_) external isDaoBatch(send_) { + // LoanConsolidator Install Script + + // Validate addresses + require(loanConsolidator != address(0), "LoanConsolidator address is not set"); + + // A. Kernel Actions + // A.1. Install the LoanConsolidator policy on the kernel + addToBatch( + kernel, + abi.encodeWithSelector( + Kernel.executeAction.selector, + Actions.ActivatePolicy, + loanConsolidator + ) + ); + } +} diff --git a/src/test/proposals/ContractRegistryProposal.t.sol b/src/test/proposals/ContractRegistryProposal.t.sol new file mode 100644 index 000000000..0131b21e3 --- /dev/null +++ b/src/test/proposals/ContractRegistryProposal.t.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +// Proposal test-suite imports +import "forge-std/Test.sol"; +import {TestSuite} from "proposal-sim/test/TestSuite.t.sol"; +import {Addresses} from "proposal-sim/addresses/Addresses.sol"; +import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; +import {RolesAdmin} from "policies/RolesAdmin.sol"; +import {GovernorBravoDelegator} from "src/external/governance/GovernorBravoDelegator.sol"; +import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; +import {Timelock} from "src/external/governance/Timelock.sol"; + +// ContractRegistry +import {OlympusContractRegistry} from "src/modules/RGSTY/OlympusContractRegistry.sol"; +import {ContractRegistryAdmin} from "src/policies/ContractRegistryAdmin.sol"; + +import {ContractRegistryProposal} from "src/proposals/ContractRegistryProposal.sol"; + +/// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. +/// @dev Update the `setUp` function to deploy your proposal and set the submission +/// flag to `true` once the proposal has been submitted on-chain. +/// Note: this will fail if the OCGPermissions script has not been run yet. +contract ContractRegistryProposalTest is Test { + string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; + TestSuite public suite; + Addresses public addresses; + + // Wether the proposal has been submitted or not. + // If true, the framework will check that calldatas match. + bool public hasBeenSubmitted; + + string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); + + /// @notice Creates a sandboxed environment from a mainnet fork. + function setUp() public virtual { + // Mainnet Fork at a fixed block + // Prior to the proposal deployment (otherwise it will fail) + vm.createSelectFork(RPC_URL, 21070000); + + // TODO update addresses when RGSTY and ContractRegistryAdmin are deployed + + /// @dev Deploy your proposal + ContractRegistryProposal proposal = new ContractRegistryProposal(); + + /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. + hasBeenSubmitted = false; + + /// [DO NOT DELETE] + /// @notice This section is used to simulate the proposal on the mainnet fork. + { + // Populate addresses array + address[] memory proposalsAddresses = new address[](1); + proposalsAddresses[0] = address(proposal); + + // Deploy TestSuite contract + suite = new TestSuite(ADDRESSES_PATH, proposalsAddresses); + + // Set addresses object + addresses = suite.addresses(); + + // Set debug mode + suite.setDebug(true); + // Execute proposals + suite.testProposals(); + + // Proposals execution may change addresses, so we need to update the addresses object. + addresses = suite.addresses(); + + // Check if simulated calldatas match the ones from mainnet. + if (hasBeenSubmitted) { + address governor = addresses.getAddress("olympus-governor"); + bool[] memory matches = suite.checkProposalCalldatas(governor); + for (uint256 i; i < matches.length; i++) { + assertTrue(matches[i]); + } + } else { + console.log("\n\n------- Calldata check (simulation vs mainnet) -------\n"); + console.log("Proposal has NOT been submitted on-chain yet.\n"); + } + } + } + + // [DO NOT DELETE] Dummy test to ensure `setUp` is executed and the proposal simulated. + function testProposal_simulate() public { + assertTrue(true); + } +} diff --git a/src/test/proposals/LoanConsolidatorProposal.t.sol b/src/test/proposals/LoanConsolidatorProposal.t.sol index 2c30fae3b..efde73939 100644 --- a/src/test/proposals/LoanConsolidatorProposal.t.sol +++ b/src/test/proposals/LoanConsolidatorProposal.t.sol @@ -11,8 +11,7 @@ import {GovernorBravoDelegator} from "src/external/governance/GovernorBravoDeleg import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; import {Timelock} from "src/external/governance/Timelock.sol"; -// LoanConsolidator imports -import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; +// Proposal imports import {LoanConsolidatorProposal} from "src/proposals/LoanConsolidatorProposal.sol"; /// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. @@ -36,9 +35,7 @@ contract LoanConsolidatorProposalTest is Test { // Prior to the proposal deployment (otherwise it will fail) vm.createSelectFork(RPC_URL, 21070000); - address kernel = addresses.getAddress("olympus-kernel"); - - // TODO RGSTY, ContractRegistryAdmin, LoanConsolidator + // TODO update addresses when LoanConsolidator is deployed /// @dev Deploy your proposal LoanConsolidatorProposal proposal = new LoanConsolidatorProposal(); From c7788d4df01701a76cfae6b25ee2fa6ab64b51d8 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 31 Oct 2024 12:43:24 +0400 Subject: [PATCH 091/160] More robust detection of OCG proposals when running "pnpm run test:proposal". Exclude from unit tests script. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0e5bf0365..c1e19a912 100644 --- a/package.json +++ b/package.json @@ -24,9 +24,9 @@ "lint": "pnpm run prettier && pnpm run solhint && pnpm run markdownlint", "lint:check": "pnpm run prettier:check && pnpm run solhint:check && pnpm run markdownlint:check", "test": "./shell/test_all.sh", - "test:unit": "forge test --no-match-contract '(Fork|OCGProposal)' -vvv", + "test:unit": "forge test --no-match-contract '(Fork)' --no-match-path 'src/test/proposals/*.t.sol' -vvv", "test:fork": "forge test --match-contract $1.*Fork$ --fork-url $FORK_TEST_RPC_URL -vvv", - "test:proposal": "forge test --match-contract OCGProposal --fork-url $FORK_TEST_RPC_URL -vvv", + "test:proposal": "forge test --match-path 'src/test/proposals/*.t.sol' --fork-url $FORK_TEST_RPC_URL -vvv", "test:crosschainfork": "forge test --match-contract CrossChainBridgeFork -vvv", "test:coverage": "./shell/test_coverage.sh", "size": "forge clean && forge build --sizes --skip test --skip '*/*Mock*.sol' --skip 'UniswapDeployer.sol'", From 51b961bc78968600bd30975d26f0f111614559e1 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 31 Oct 2024 12:43:47 +0400 Subject: [PATCH 092/160] Split proposal simulation tests into own github workflow --- .github/workflows/CI.yml | 5 ----- .github/workflows/OCG.yml | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/OCG.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 90c6a2e45..69d3d704d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -39,11 +39,6 @@ jobs: - name: Run unit tests run: pnpm run test:unit - - name: Run proposal simulation tests - run: pnpm run test:proposal - env: - FORK_TEST_RPC_URL: ${{ secrets.FORK_TEST_RPC_URL }} - - name: Run fork tests run: pnpm run test:fork env: diff --git a/.github/workflows/OCG.yml b/.github/workflows/OCG.yml new file mode 100644 index 000000000..64445db08 --- /dev/null +++ b/.github/workflows/OCG.yml @@ -0,0 +1,39 @@ +name: OCG Proposals +on: + push: + branches: + - master + pull_request: + +jobs: + run-ci: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - uses: pnpm/action-setup@v2 + with: + version: 9 + + - name: Install Node dependencies + run: pnpm install + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly-ca67d15f4abd46394b324c50e21e66f306a1162d + + - name: Install Foundry dependencies + run: forge install + + - name: Run proposal simulation tests + run: pnpm run test:proposal + env: + FORK_TEST_RPC_URL: ${{ secrets.FORK_TEST_RPC_URL }} From 80eda976d9ecd6f71472d102b44b5bdf07244796 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 31 Oct 2024 12:53:44 +0400 Subject: [PATCH 093/160] Add link to audit report --- src/proposals/ContractRegistryProposal.sol | 3 +-- src/proposals/LoanConsolidatorProposal.sol | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/proposals/ContractRegistryProposal.sol b/src/proposals/ContractRegistryProposal.sol index 7ca43ed37..f23f38f65 100644 --- a/src/proposals/ContractRegistryProposal.sol +++ b/src/proposals/ContractRegistryProposal.sol @@ -45,7 +45,6 @@ contract ContractRegistryProposal is GovernorBravoProposal { // Provides a brief description of the proposal. function description() public pure override returns (string memory) { - // TODO add link to audit report return string.concat( "# Contract Registry Activation\n\n", @@ -53,7 +52,7 @@ contract ContractRegistryProposal is GovernorBravoProposal { "The RGSTY module is used to register commonly-used addresses that can be referenced by other contracts. These addresses are marked as either mutable or immutable.\n\n", "The ContractRegistryAdmin policy is used to manage the addresses registered in the RGSTY module.\n\n", "The RGSTY module will be used by the LoanConsolidator policy to lookup contract addresses. In order to roll-out the improved LoanConsolidator, this proposal must be executed first.\n\n", - "The audit report can be found at:\n\n", + "[View the audit report here](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2024_10_LoanConsolidator_Audit.pdf)\n\n", "## Assumptions\n\n", "- The RGSTY module has been deployed and activated as a module by the DAO MS.\n", "- The ContractRegistryAdmin policy has been deployed and activated as a policy by the DAO MS.\n\n", diff --git a/src/proposals/LoanConsolidatorProposal.sol b/src/proposals/LoanConsolidatorProposal.sol index 04310725b..b50b7d221 100644 --- a/src/proposals/LoanConsolidatorProposal.sol +++ b/src/proposals/LoanConsolidatorProposal.sol @@ -32,7 +32,6 @@ contract LoanConsolidatorProposal is GovernorBravoProposal { // Provides a brief description of the proposal. function description() public pure override returns (string memory) { - // TODO add link to audit report return string.concat( "# LoanConsolidator and Contract Registry Activation\n\n", @@ -43,7 +42,7 @@ contract LoanConsolidatorProposal is GovernorBravoProposal { "- Strict checks on callers, ownership and Clearinghouse validity\n", "- Allows for migration of loans from one Clearinghouse to another (in preparation for a USDS Clearinghouse)\n", "- Allows for migration of loans from one owner to another\n\n", - "The audit report can be found at:\n\n", + "[View the audit report here](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2024_10_LoanConsolidator_Audit.pdf)\n\n", "## Assumptions\n\n", "- The Contract Registry module has been deployed and activated as a module by the DAO MS.\n", "- The ContractRegistryAdmin policy has been deployed and activated as a policy by the DAO MS.\n", From 153795c03f98c316c4ca092c9b09860115c2398e Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 31 Oct 2024 13:07:35 +0400 Subject: [PATCH 094/160] Fix fork test script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c1e19a912..07ab9b06f 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "lint:check": "pnpm run prettier:check && pnpm run solhint:check && pnpm run markdownlint:check", "test": "./shell/test_all.sh", "test:unit": "forge test --no-match-contract '(Fork)' --no-match-path 'src/test/proposals/*.t.sol' -vvv", - "test:fork": "forge test --match-contract $1.*Fork$ --fork-url $FORK_TEST_RPC_URL -vvv", + "test:fork": "forge test --match-contract 'Fork' --fork-url $FORK_TEST_RPC_URL -vvv", "test:proposal": "forge test --match-path 'src/test/proposals/*.t.sol' --fork-url $FORK_TEST_RPC_URL -vvv", "test:crosschainfork": "forge test --match-contract CrossChainBridgeFork -vvv", "test:coverage": "./shell/test_coverage.sh", From 8a34c4ce9ebb7ad361eb68b4ac48e21d802c30fc Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 31 Oct 2024 13:09:37 +0400 Subject: [PATCH 095/160] Unpin foundry version --- .github/workflows/CI.yml | 2 -- .github/workflows/OCG.yml | 2 -- 2 files changed, 4 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 69d3d704d..4d3579f4e 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -27,8 +27,6 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly-ca67d15f4abd46394b324c50e21e66f306a1162d - name: Install Foundry dependencies run: forge install diff --git a/.github/workflows/OCG.yml b/.github/workflows/OCG.yml index 64445db08..ee219d15b 100644 --- a/.github/workflows/OCG.yml +++ b/.github/workflows/OCG.yml @@ -27,8 +27,6 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly-ca67d15f4abd46394b324c50e21e66f306a1162d - name: Install Foundry dependencies run: forge install From 09a9ecdf574f04fd41c349046bcd5d4d8232f6f2 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 31 Oct 2024 13:15:29 +0400 Subject: [PATCH 096/160] Exclude cross-chain bridge fork --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 07ab9b06f..cbf15f3ad 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,9 @@ "lint:check": "pnpm run prettier:check && pnpm run solhint:check && pnpm run markdownlint:check", "test": "./shell/test_all.sh", "test:unit": "forge test --no-match-contract '(Fork)' --no-match-path 'src/test/proposals/*.t.sol' -vvv", - "test:fork": "forge test --match-contract 'Fork' --fork-url $FORK_TEST_RPC_URL -vvv", + "test:fork": "forge test --match-contract 'Fork' --no-match-contract 'CrossChainBridgeFork' --fork-url $FORK_TEST_RPC_URL -vvv", "test:proposal": "forge test --match-path 'src/test/proposals/*.t.sol' --fork-url $FORK_TEST_RPC_URL -vvv", - "test:crosschainfork": "forge test --match-contract CrossChainBridgeFork -vvv", + "test:crosschainfork": "forge test --match-contract 'CrossChainBridgeFork' -vvv", "test:coverage": "./shell/test_coverage.sh", "size": "forge clean && forge build --sizes --skip test --skip '*/*Mock*.sol' --skip 'UniswapDeployer.sol'", "metrics": "node shell/metrics.js" From 1ffea94f0f5d83e0670f53cb6ec76880bf59d1fe Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 31 Oct 2024 16:24:45 +0400 Subject: [PATCH 097/160] Fixes two issues. Add tests for consolidation between clearinghouses with higher/lower LTC. Fixes: - Incorrect clearinghouse used for collateral calculation - Shift GOHM.transferFrom() after collateral calculation --- src/policies/LoanConsolidator.sol | 21 +- src/test/lib/ClearinghouseHigherLTC.sol | 486 +++++++++++++++++++ src/test/lib/ClearinghouseLowerLTC.sol | 486 +++++++++++++++++++ src/test/policies/LoanConsolidatorFork.t.sol | 308 +++++++++++- 4 files changed, 1293 insertions(+), 8 deletions(-) create mode 100644 src/test/lib/ClearinghouseHigherLTC.sol create mode 100644 src/test/lib/ClearinghouseLowerLTC.sol diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index e528a9a0a..75b4cb2b1 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -453,20 +453,23 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent address(flashLoanData.coolerFrom), flashLoanData.principal + flashLoanData.interest ); - // Iterate over all batches, repay the debt and collect the collateral + // Iterate over all batches, repay the debt _repayDebtForLoans(address(flashLoanData.coolerFrom), flashLoanData.ids); // State: // - reserveFrom: reduced by principal and interest, should be 0 // - reserveTo: no change, should be 0 - // - gOHM: increased by the collateral returned + // - gOHM: no change, should be 0 // Calculate the amount of collateral that will be needed for the consolidated loan - uint256 consolidatedLoanCollateral = flashLoanData.clearinghouseFrom.getCollateralForLoan( + // This is performed on the destination Clearinghouse, since it will be the one issuing the consolidated loan + uint256 consolidatedLoanCollateral = flashLoanData.clearinghouseTo.getCollateralForLoan( flashLoanData.principal ); - // If the collateral required is greater than the collateral that was returned, then transfer gOHM from the cooler owner - // This can happen as the collateral required for the consolidated loan can be greater than the sum of the collateral of the loans being consolidated + // Transfer the collateral from the cooler owner to this contract + // consolidatedLoanCollateral > returned collateral if clearinghouseFrom has a higher LTC than clearinghouseTo + // consolidatedLoanCollateral > returned collateral due to rounding + // consolidatedLoanCollateral < returned collateral if clearinghouseFrom has a lower LTC than clearinghouseTo if (consolidatedLoanCollateral > GOHM.balanceOf(address(this))) { GOHM.transferFrom( flashLoanData.coolerFrom.owner(), @@ -474,6 +477,10 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent consolidatedLoanCollateral - GOHM.balanceOf(address(this)) ); } + // State: + // - reserveFrom: no change + // - reserveTo: no change + // - gOHM: increased by consolidatedLoanCollateral // Take a new Cooler loan for the principal required GOHM.approve(address(flashLoanData.clearinghouseTo), consolidatedLoanCollateral); @@ -621,8 +628,8 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent totalCollateral += collateralReturned; } - // Transfers all of the gOHM collateral to this contract - GOHM.transferFrom(cooler.owner(), address(this), totalCollateral); + // Upon repayment, the collateral is released to the owner + // After this function concludes, the contract needs to transfer the collateral to itself } function _isValidClearinghouse(address clearinghouse_) internal view returns (bool) { diff --git a/src/test/lib/ClearinghouseHigherLTC.sol b/src/test/lib/ClearinghouseHigherLTC.sol new file mode 100644 index 000000000..d07903dc6 --- /dev/null +++ b/src/test/lib/ClearinghouseHigherLTC.sol @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {ERC4626} from "solmate/mixins/ERC4626.sol"; +import {IStaking} from "interfaces/IStaking.sol"; + +import {CoolerFactory, Cooler} from "src/external/cooler/CoolerFactory.sol"; +import {CoolerCallback} from "src/external/cooler/CoolerCallback.sol"; + +import "src/Kernel.sol"; +import {TRSRYv1} from "modules/TRSRY/TRSRY.v1.sol"; +import {MINTRv1} from "modules/MINTR/MINTR.v1.sol"; +import {CHREGv1} from "modules/CHREG/CHREG.v1.sol"; +import {ROLESv1, RolesConsumer} from "modules/ROLES/OlympusRoles.sol"; + +/// @notice Copy of the Clearinghouse policy with a higher LTC +contract ClearinghouseHigherLTC is Policy, RolesConsumer, CoolerCallback { + // --- ERRORS ---------------------------------------------------- + + error BadEscrow(); + error DurationMaximum(); + error OnlyBurnable(); + error TooEarlyToFund(); + error LengthDiscrepancy(); + error OnlyBorrower(); + error NotLender(); + + // --- EVENTS ---------------------------------------------------- + + /// @notice Logs whenever the Clearinghouse is initialized or reactivated. + event Activate(); + /// @notice Logs whenever the Clearinghouse is deactivated. + event Deactivate(); + /// @notice Logs whenever the treasury is defunded. + event Defund(address token, uint256 amount); + /// @notice Logs the balance change (in reserve terms) whenever a rebalance occurs. + event Rebalance(bool defund, uint256 reserveAmount); + + // --- RELEVANT CONTRACTS ---------------------------------------- + + ERC20 public immutable reserve; // Debt token + ERC4626 public immutable sReserve; // Idle reserve will be wrapped into sReserve + ERC20 public immutable gohm; // Collateral token + ERC20 public immutable ohm; // Unwrapped gOHM + IStaking public immutable staking; // Necessary to unstake (and burn) OHM from defaults + + // --- MODULES --------------------------------------------------- + + CHREGv1 public CHREG; // Olympus V3 Clearinghouse Registry Module + MINTRv1 public MINTR; // Olympus V3 Minter Module + TRSRYv1 public TRSRY; // Olympus V3 Treasury Module + + // --- PARAMETER BOUNDS ------------------------------------------ + + uint256 public constant INTEREST_RATE = 5e15; // 0.5% anually + uint256 public constant LOAN_TO_COLLATERAL = 4000e18; + uint256 public constant DURATION = 121 days; // Four months + uint256 public constant FUND_CADENCE = 7 days; // One week + uint256 public constant FUND_AMOUNT = 18_000_000e18; // 18 million + uint256 public constant MAX_REWARD = 1e17; // 0.1 gOHM + + // --- STATE VARIABLES ------------------------------------------- + + /// @notice determines whether the contract can be funded or not. + bool public active; + + /// @notice timestamp at which the next rebalance can occur. + uint256 public fundTime; + + /// @notice Outstanding receivables. + /// Incremented when a loan is taken or rolled. + /// Decremented when a loan is repaid or collateral is burned. + uint256 public interestReceivables; + uint256 public principalReceivables; + + // --- INITIALIZATION -------------------------------------------- + + constructor( + address ohm_, + address gohm_, + address staking_, + address sReserve_, + address coolerFactory_, + address kernel_ + ) Policy(Kernel(kernel_)) CoolerCallback(coolerFactory_) { + // Store the relevant contracts. + ohm = ERC20(ohm_); + gohm = ERC20(gohm_); + staking = IStaking(staking_); + sReserve = ERC4626(sReserve_); + reserve = ERC20(sReserve.asset()); + } + + /// @notice Default framework setup. Configure dependencies for olympus-v3 modules. + /// @dev This function will be called when the `executor` installs the Clearinghouse + /// policy in the olympus-v3 `Kernel`. + function configureDependencies() external override returns (Keycode[] memory dependencies) { + dependencies = new Keycode[](4); + dependencies[0] = toKeycode("CHREG"); + dependencies[1] = toKeycode("MINTR"); + dependencies[2] = toKeycode("ROLES"); + dependencies[3] = toKeycode("TRSRY"); + + CHREG = CHREGv1(getModuleAddress(toKeycode("CHREG"))); + MINTR = MINTRv1(getModuleAddress(toKeycode("MINTR"))); + ROLES = ROLESv1(getModuleAddress(toKeycode("ROLES"))); + TRSRY = TRSRYv1(getModuleAddress(toKeycode("TRSRY"))); + + (uint8 CHREG_MAJOR, ) = CHREG.VERSION(); + (uint8 MINTR_MAJOR, ) = MINTR.VERSION(); + (uint8 ROLES_MAJOR, ) = ROLES.VERSION(); + (uint8 TRSRY_MAJOR, ) = TRSRY.VERSION(); + + // Ensure Modules are using the expected major version. + // Modules should be sorted in alphabetical order. + bytes memory expected = abi.encode([1, 1, 1, 1]); + if (CHREG_MAJOR != 1 || MINTR_MAJOR != 1 || ROLES_MAJOR != 1 || TRSRY_MAJOR != 1) + revert Policy_WrongModuleVersion(expected); + + // Approve MINTR for burning OHM (called here so that it is re-approved on updates) + ohm.approve(address(MINTR), type(uint256).max); + } + + /// @notice Default framework setup. Request permissions for interacting with olympus-v3 modules. + /// @dev This function will be called when the `executor` installs the Clearinghouse + /// policy in the olympus-v3 `Kernel`. + function requestPermissions() external view override returns (Permissions[] memory requests) { + Keycode CHREG_KEYCODE = toKeycode("CHREG"); + Keycode MINTR_KEYCODE = toKeycode("MINTR"); + Keycode TRSRY_KEYCODE = toKeycode("TRSRY"); + + requests = new Permissions[](6); + requests[0] = Permissions(CHREG_KEYCODE, CHREG.activateClearinghouse.selector); + requests[1] = Permissions(CHREG_KEYCODE, CHREG.deactivateClearinghouse.selector); + requests[2] = Permissions(MINTR_KEYCODE, MINTR.burnOhm.selector); + requests[3] = Permissions(TRSRY_KEYCODE, TRSRY.setDebt.selector); + requests[4] = Permissions(TRSRY_KEYCODE, TRSRY.increaseWithdrawApproval.selector); + requests[5] = Permissions(TRSRY_KEYCODE, TRSRY.withdrawReserves.selector); + } + + /// @notice Returns the version of the policy. + /// + /// @return major The major version of the policy. + /// @return minor The minor version of the policy. + function VERSION() external pure returns (uint8 major, uint8 minor) { + return (1, 2); + } + + // --- OPERATION ------------------------------------------------- + + /// @notice Lend to a cooler. + /// @dev To simplify the UX and easily ensure that all holders get the same terms, + /// this function requests a new loan and clears it in the same transaction. + /// @param cooler_ to lend to. + /// @param amount_ of reserve to lend. + /// @return the id of the granted loan. + function lendToCooler(Cooler cooler_, uint256 amount_) external returns (uint256) { + // Attempt a Clearinghouse <> Treasury rebalance. + rebalance(); + + // Validate that cooler was deployed by the trusted factory. + if (!factory.created(address(cooler_))) revert OnlyFromFactory(); + + // Validate cooler collateral and debt tokens. + if (cooler_.collateral() != gohm || cooler_.debt() != reserve) revert BadEscrow(); + + // Transfer in collateral owed + uint256 collateral = cooler_.collateralFor(amount_, LOAN_TO_COLLATERAL); + gohm.transferFrom(msg.sender, address(this), collateral); + + // Increment interest to be expected + (, uint256 interest) = getLoanForCollateral(collateral); + interestReceivables += interest; + principalReceivables += amount_; + + // Create a new loan request. + gohm.approve(address(cooler_), collateral); + uint256 reqID = cooler_.requestLoan(amount_, INTEREST_RATE, LOAN_TO_COLLATERAL, DURATION); + + // Clear the created loan request by providing enough reserve. + sReserve.withdraw(amount_, address(this), address(this)); + reserve.approve(address(cooler_), amount_); + uint256 loanID = cooler_.clearRequest(reqID, address(this), true); + + return loanID; + } + + /// @notice Extend the loan expiry by repaying the extension interest in advance. + /// The extension cost is paid by the caller. If a third-party executes the + /// extension, the loan period is extended, but the borrower debt does not increase. + /// @param cooler_ holding the loan to be extended. + /// @param loanID_ index of loan in loans[]. + /// @param times_ Amount of times that the fixed-term loan duration is extended. + function extendLoan(Cooler cooler_, uint256 loanID_, uint8 times_) external { + Cooler.Loan memory loan = cooler_.getLoan(loanID_); + + // Validate that cooler was deployed by the trusted factory. + if (!factory.created(address(cooler_))) revert OnlyFromFactory(); + + // Calculate extension interest based on the remaining principal. + uint256 interestBase = interestForLoan(loan.principal, loan.request.duration); + + // Transfer in extension interest from the caller. + reserve.transferFrom(msg.sender, address(this), interestBase * times_); + if (active) { + _sweepIntoSavingsVault(interestBase * times_); + } else { + _defund(reserve, interestBase * times_); + } + + // Signal to cooler that loan should be extended. + cooler_.extendLoanTerms(loanID_, times_); + } + + /// @notice Batch several default claims to save gas. + /// The elements on both arrays must be paired based on their index. + /// @dev Implements an auction style reward system that linearly increases up to a max reward. + /// @param coolers_ Array of contracts where the default must be claimed. + /// @param loans_ Array of defaulted loan ids. + function claimDefaulted(address[] calldata coolers_, uint256[] calldata loans_) external { + uint256 loans = loans_.length; + if (loans != coolers_.length) revert LengthDiscrepancy(); + + uint256 keeperRewards; + uint256 totalInterest; + uint256 totalPrincipal; + for (uint256 i = 0; i < loans; ) { + // Validate that cooler was deployed by the trusted factory. + if (!factory.created(coolers_[i])) revert OnlyFromFactory(); + + // Validate that loan was written by clearinghouse. + if (Cooler(coolers_[i]).getLoan(loans_[i]).lender != address(this)) revert NotLender(); + + // Claim defaults and update cached metrics. + (uint256 principal, uint256 interest, uint256 collateral, uint256 elapsed) = Cooler( + coolers_[i] + ).claimDefaulted(loans_[i]); + + unchecked { + // Cannot overflow due to max supply limits for both tokens + totalPrincipal += principal; + totalInterest += interest; + // There will not exist more than 2**256 loans + ++i; + } + + // Cap rewards to 5% of the collateral to avoid OHM holder's dillution. + uint256 maxAuctionReward = (collateral * 5e16) / 1e18; + + // Cap rewards to avoid exorbitant amounts. + uint256 maxReward = (maxAuctionReward < MAX_REWARD) ? maxAuctionReward : MAX_REWARD; + + // Calculate rewards based on the elapsed time since default. + keeperRewards = (elapsed < 7 days) + ? keeperRewards + (maxReward * elapsed) / 7 days + : keeperRewards + maxReward; + } + + // Decrement loan receivables. + interestReceivables = (interestReceivables > totalInterest) + ? interestReceivables - totalInterest + : 0; + principalReceivables = (principalReceivables > totalPrincipal) + ? principalReceivables - totalPrincipal + : 0; + + // Update outstanding debt owed to the Treasury upon default. + uint256 outstandingDebt = TRSRY.reserveDebt(reserve, address(this)); + + // debt owed to TRSRY = user debt - user interest + TRSRY.setDebt({ + debtor_: address(this), + token_: reserve, + amount_: (outstandingDebt > totalPrincipal) ? outstandingDebt - totalPrincipal : 0 + }); + + // Reward keeper. + gohm.transfer(msg.sender, keeperRewards); + // Burn the outstanding collateral of defaulted loans. + burn(); + } + + // --- CALLBACKS ----------------------------------------------------- + + /// @notice Overridden callback to decrement loan receivables. + /// @param *unused loadID_ of the load. + /// @param principalPaid_ in reserve. + /// @param interestPaid_ in reserve. + function _onRepay(uint256, uint256 principalPaid_, uint256 interestPaid_) internal override { + if (active) { + _sweepIntoSavingsVault(principalPaid_ + interestPaid_); + } else { + _defund(reserve, principalPaid_ + interestPaid_); + } + + // Decrement loan receivables. + interestReceivables = (interestReceivables > interestPaid_) + ? interestReceivables - interestPaid_ + : 0; + principalReceivables = (principalReceivables > principalPaid_) + ? principalReceivables - principalPaid_ + : 0; + } + + /// @notice Unused callback since defaults are handled by the clearinghouse. + /// @dev Overriden and left empty to save gas. + function _onDefault(uint256, uint256, uint256, uint256) internal override {} + + // --- FUNDING --------------------------------------------------- + + /// @notice Fund loan liquidity from treasury. + /// @dev Exposure is always capped at FUND_AMOUNT and rebalanced at up to FUND_CADANCE. + /// If several rebalances are available (because some were missed), calling this + /// function several times won't impact the funds controlled by the contract. + /// If the emergency shutdown is triggered, a rebalance will send funds back to + /// the treasury. + /// @return False if too early to rebalance. Otherwise, true. + function rebalance() public returns (bool) { + // If the contract is deactivated, defund. + uint256 maxFundAmount = active ? FUND_AMOUNT : 0; + // Update funding schedule if necessary. + if (fundTime > block.timestamp) return false; + fundTime += FUND_CADENCE; + + // Sweep reserve into DSR if necessary. + uint256 idle = reserve.balanceOf(address(this)); + if (idle != 0) _sweepIntoSavingsVault(idle); + + uint256 reserveBalance = sReserve.maxWithdraw(address(this)); + uint256 outstandingDebt = TRSRY.reserveDebt(reserve, address(this)); + // Rebalance funds on hand with treasury's reserves. + if (reserveBalance < maxFundAmount) { + // Since users loans are denominated in reserve, the clearinghouse + // debt is set in reserve terms. It must be adjusted when funding. + uint256 fundAmount = maxFundAmount - reserveBalance; + TRSRY.setDebt({ + debtor_: address(this), + token_: reserve, + amount_: outstandingDebt + fundAmount + }); + + // Since TRSRY holds sReserve, a conversion must be done before + // funding the clearinghouse. + uint256 sReserveAmount = sReserve.previewWithdraw(fundAmount); + TRSRY.increaseWithdrawApproval(address(this), sReserve, sReserveAmount); + TRSRY.withdrawReserves(address(this), sReserve, sReserveAmount); + + // Log the event. + emit Rebalance(false, fundAmount); + } else if (reserveBalance > maxFundAmount) { + // Since users loans are denominated in reserve, the clearinghouse + // debt is set in reserve terms. It must be adjusted when defunding. + uint256 defundAmount = reserveBalance - maxFundAmount; + TRSRY.setDebt({ + debtor_: address(this), + token_: reserve, + amount_: (outstandingDebt > defundAmount) ? outstandingDebt - defundAmount : 0 + }); + + // Since TRSRY holds sReserve, a conversion must be done before + // sending sReserve back. + uint256 sReserveAmount = sReserve.previewWithdraw(defundAmount); + sReserve.transfer(address(TRSRY), sReserveAmount); + + // Log the event. + emit Rebalance(true, defundAmount); + } + + return true; + } + + /// @notice Sweep excess reserve into savings vault. + function sweepIntoSavingsVault() public { + uint256 reserveBalance = reserve.balanceOf(address(this)); + _sweepIntoSavingsVault(reserveBalance); + } + + /// @notice Sweep excess reserve into vault. + function _sweepIntoSavingsVault(uint256 amount_) internal { + reserve.approve(address(sReserve), amount_); + sReserve.deposit(amount_, address(this)); + } + + /// @notice Public function to burn gOHM. + /// @dev Can be used to burn any gOHM defaulted using the Cooler instead of the Clearinghouse. + function burn() public { + uint256 gohmBalance = gohm.balanceOf(address(this)); + // Unstake and burn gOHM holdings. + gohm.approve(address(staking), gohmBalance); + MINTR.burnOhm(address(this), staking.unstake(address(this), gohmBalance, false, false)); + } + + // --- ADMIN --------------------------------------------------- + + /// @notice Activate the contract. + function activate() external onlyRole("cooler_overseer") { + active = true; + fundTime = block.timestamp; + + // Signal to CHREG that the contract has been activated. + CHREG.activateClearinghouse(address(this)); + + emit Activate(); + } + + /// @notice Deactivate the contract and return funds to treasury. + function emergencyShutdown() external onlyRole("emergency_shutdown") { + active = false; + + // If necessary, defund sReserve. + uint256 sReserveBalance = sReserve.balanceOf(address(this)); + if (sReserveBalance != 0) _defund(sReserve, sReserveBalance); + + // If necessary, defund reserve. + uint256 reserveBalance = reserve.balanceOf(address(this)); + if (reserveBalance != 0) _defund(reserve, reserveBalance); + + // Signal to CHREG that the contract has been deactivated. + CHREG.deactivateClearinghouse(address(this)); + + emit Deactivate(); + } + + /// @notice Return funds to treasury. + /// @param token_ to transfer. + /// @param amount_ to transfer. + function defund(ERC20 token_, uint256 amount_) external onlyRole("cooler_overseer") { + if (token_ == gohm) revert OnlyBurnable(); + _defund(token_, amount_); + } + + /// @notice Internal function to return funds to treasury. + /// @param token_ to transfer. + /// @param amount_ to transfer. + function _defund(ERC20 token_, uint256 amount_) internal { + if (token_ == sReserve || token_ == reserve) { + // Since users loans are denominated in reserve, the clearinghouse + // debt is set in reserve terms. It must be adjusted when defunding. + uint256 outstandingDebt = TRSRY.reserveDebt(reserve, address(this)); + uint256 reserveAmount = (token_ == sReserve) + ? sReserve.previewRedeem(amount_) + : amount_; + + TRSRY.setDebt({ + debtor_: address(this), + token_: reserve, + amount_: (outstandingDebt > reserveAmount) ? outstandingDebt - reserveAmount : 0 + }); + } + + // Defund and log the event + token_.transfer(address(TRSRY), amount_); + emit Defund(address(token_), amount_); + } + + // --- AUX FUNCTIONS --------------------------------------------- + + /// @notice view function computing collateral for a loan amount. + function getCollateralForLoan(uint256 principal_) external pure returns (uint256) { + return (principal_ * 1e18) / LOAN_TO_COLLATERAL; + } + + /// @notice view function computing loan for a collateral amount. + /// @param collateral_ amount of gOHM. + /// @return debt (amount to be lent + interest) for a given collateral amount. + function getLoanForCollateral(uint256 collateral_) public pure returns (uint256, uint256) { + uint256 principal = (collateral_ * LOAN_TO_COLLATERAL) / 1e18; + uint256 interest = interestForLoan(principal, DURATION); + return (principal, interest); + } + + /// @notice view function to compute the interest for given principal amount. + /// @param principal_ amount of reserve being lent. + /// @param duration_ elapsed time in seconds. + function interestForLoan(uint256 principal_, uint256 duration_) public pure returns (uint256) { + uint256 interestPercent = (INTEREST_RATE * duration_) / 365 days; + return (principal_ * interestPercent) / 1e18; + } + + /// @notice Get total receivable reserve for the treasury. + /// Includes both principal and interest. + function getTotalReceivables() external view returns (uint256) { + return principalReceivables + interestReceivables; + } +} diff --git a/src/test/lib/ClearinghouseLowerLTC.sol b/src/test/lib/ClearinghouseLowerLTC.sol new file mode 100644 index 000000000..b65086549 --- /dev/null +++ b/src/test/lib/ClearinghouseLowerLTC.sol @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {ERC4626} from "solmate/mixins/ERC4626.sol"; +import {IStaking} from "interfaces/IStaking.sol"; + +import {CoolerFactory, Cooler} from "src/external/cooler/CoolerFactory.sol"; +import {CoolerCallback} from "src/external/cooler/CoolerCallback.sol"; + +import "src/Kernel.sol"; +import {TRSRYv1} from "modules/TRSRY/TRSRY.v1.sol"; +import {MINTRv1} from "modules/MINTR/MINTR.v1.sol"; +import {CHREGv1} from "modules/CHREG/CHREG.v1.sol"; +import {ROLESv1, RolesConsumer} from "modules/ROLES/OlympusRoles.sol"; + +/// @notice Copy of the Clearinghouse policy with a lower LTC +contract ClearinghouseLowerLTC is Policy, RolesConsumer, CoolerCallback { + // --- ERRORS ---------------------------------------------------- + + error BadEscrow(); + error DurationMaximum(); + error OnlyBurnable(); + error TooEarlyToFund(); + error LengthDiscrepancy(); + error OnlyBorrower(); + error NotLender(); + + // --- EVENTS ---------------------------------------------------- + + /// @notice Logs whenever the Clearinghouse is initialized or reactivated. + event Activate(); + /// @notice Logs whenever the Clearinghouse is deactivated. + event Deactivate(); + /// @notice Logs whenever the treasury is defunded. + event Defund(address token, uint256 amount); + /// @notice Logs the balance change (in reserve terms) whenever a rebalance occurs. + event Rebalance(bool defund, uint256 reserveAmount); + + // --- RELEVANT CONTRACTS ---------------------------------------- + + ERC20 public immutable reserve; // Debt token + ERC4626 public immutable sReserve; // Idle reserve will be wrapped into sReserve + ERC20 public immutable gohm; // Collateral token + ERC20 public immutable ohm; // Unwrapped gOHM + IStaking public immutable staking; // Necessary to unstake (and burn) OHM from defaults + + // --- MODULES --------------------------------------------------- + + CHREGv1 public CHREG; // Olympus V3 Clearinghouse Registry Module + MINTRv1 public MINTR; // Olympus V3 Minter Module + TRSRYv1 public TRSRY; // Olympus V3 Treasury Module + + // --- PARAMETER BOUNDS ------------------------------------------ + + uint256 public constant INTEREST_RATE = 5e15; // 0.5% anually + uint256 public constant LOAN_TO_COLLATERAL = 2000e18; + uint256 public constant DURATION = 121 days; // Four months + uint256 public constant FUND_CADENCE = 7 days; // One week + uint256 public constant FUND_AMOUNT = 18_000_000e18; // 18 million + uint256 public constant MAX_REWARD = 1e17; // 0.1 gOHM + + // --- STATE VARIABLES ------------------------------------------- + + /// @notice determines whether the contract can be funded or not. + bool public active; + + /// @notice timestamp at which the next rebalance can occur. + uint256 public fundTime; + + /// @notice Outstanding receivables. + /// Incremented when a loan is taken or rolled. + /// Decremented when a loan is repaid or collateral is burned. + uint256 public interestReceivables; + uint256 public principalReceivables; + + // --- INITIALIZATION -------------------------------------------- + + constructor( + address ohm_, + address gohm_, + address staking_, + address sReserve_, + address coolerFactory_, + address kernel_ + ) Policy(Kernel(kernel_)) CoolerCallback(coolerFactory_) { + // Store the relevant contracts. + ohm = ERC20(ohm_); + gohm = ERC20(gohm_); + staking = IStaking(staking_); + sReserve = ERC4626(sReserve_); + reserve = ERC20(sReserve.asset()); + } + + /// @notice Default framework setup. Configure dependencies for olympus-v3 modules. + /// @dev This function will be called when the `executor` installs the Clearinghouse + /// policy in the olympus-v3 `Kernel`. + function configureDependencies() external override returns (Keycode[] memory dependencies) { + dependencies = new Keycode[](4); + dependencies[0] = toKeycode("CHREG"); + dependencies[1] = toKeycode("MINTR"); + dependencies[2] = toKeycode("ROLES"); + dependencies[3] = toKeycode("TRSRY"); + + CHREG = CHREGv1(getModuleAddress(toKeycode("CHREG"))); + MINTR = MINTRv1(getModuleAddress(toKeycode("MINTR"))); + ROLES = ROLESv1(getModuleAddress(toKeycode("ROLES"))); + TRSRY = TRSRYv1(getModuleAddress(toKeycode("TRSRY"))); + + (uint8 CHREG_MAJOR, ) = CHREG.VERSION(); + (uint8 MINTR_MAJOR, ) = MINTR.VERSION(); + (uint8 ROLES_MAJOR, ) = ROLES.VERSION(); + (uint8 TRSRY_MAJOR, ) = TRSRY.VERSION(); + + // Ensure Modules are using the expected major version. + // Modules should be sorted in alphabetical order. + bytes memory expected = abi.encode([1, 1, 1, 1]); + if (CHREG_MAJOR != 1 || MINTR_MAJOR != 1 || ROLES_MAJOR != 1 || TRSRY_MAJOR != 1) + revert Policy_WrongModuleVersion(expected); + + // Approve MINTR for burning OHM (called here so that it is re-approved on updates) + ohm.approve(address(MINTR), type(uint256).max); + } + + /// @notice Default framework setup. Request permissions for interacting with olympus-v3 modules. + /// @dev This function will be called when the `executor` installs the Clearinghouse + /// policy in the olympus-v3 `Kernel`. + function requestPermissions() external view override returns (Permissions[] memory requests) { + Keycode CHREG_KEYCODE = toKeycode("CHREG"); + Keycode MINTR_KEYCODE = toKeycode("MINTR"); + Keycode TRSRY_KEYCODE = toKeycode("TRSRY"); + + requests = new Permissions[](6); + requests[0] = Permissions(CHREG_KEYCODE, CHREG.activateClearinghouse.selector); + requests[1] = Permissions(CHREG_KEYCODE, CHREG.deactivateClearinghouse.selector); + requests[2] = Permissions(MINTR_KEYCODE, MINTR.burnOhm.selector); + requests[3] = Permissions(TRSRY_KEYCODE, TRSRY.setDebt.selector); + requests[4] = Permissions(TRSRY_KEYCODE, TRSRY.increaseWithdrawApproval.selector); + requests[5] = Permissions(TRSRY_KEYCODE, TRSRY.withdrawReserves.selector); + } + + /// @notice Returns the version of the policy. + /// + /// @return major The major version of the policy. + /// @return minor The minor version of the policy. + function VERSION() external pure returns (uint8 major, uint8 minor) { + return (1, 2); + } + + // --- OPERATION ------------------------------------------------- + + /// @notice Lend to a cooler. + /// @dev To simplify the UX and easily ensure that all holders get the same terms, + /// this function requests a new loan and clears it in the same transaction. + /// @param cooler_ to lend to. + /// @param amount_ of reserve to lend. + /// @return the id of the granted loan. + function lendToCooler(Cooler cooler_, uint256 amount_) external returns (uint256) { + // Attempt a Clearinghouse <> Treasury rebalance. + rebalance(); + + // Validate that cooler was deployed by the trusted factory. + if (!factory.created(address(cooler_))) revert OnlyFromFactory(); + + // Validate cooler collateral and debt tokens. + if (cooler_.collateral() != gohm || cooler_.debt() != reserve) revert BadEscrow(); + + // Transfer in collateral owed + uint256 collateral = cooler_.collateralFor(amount_, LOAN_TO_COLLATERAL); + gohm.transferFrom(msg.sender, address(this), collateral); + + // Increment interest to be expected + (, uint256 interest) = getLoanForCollateral(collateral); + interestReceivables += interest; + principalReceivables += amount_; + + // Create a new loan request. + gohm.approve(address(cooler_), collateral); + uint256 reqID = cooler_.requestLoan(amount_, INTEREST_RATE, LOAN_TO_COLLATERAL, DURATION); + + // Clear the created loan request by providing enough reserve. + sReserve.withdraw(amount_, address(this), address(this)); + reserve.approve(address(cooler_), amount_); + uint256 loanID = cooler_.clearRequest(reqID, address(this), true); + + return loanID; + } + + /// @notice Extend the loan expiry by repaying the extension interest in advance. + /// The extension cost is paid by the caller. If a third-party executes the + /// extension, the loan period is extended, but the borrower debt does not increase. + /// @param cooler_ holding the loan to be extended. + /// @param loanID_ index of loan in loans[]. + /// @param times_ Amount of times that the fixed-term loan duration is extended. + function extendLoan(Cooler cooler_, uint256 loanID_, uint8 times_) external { + Cooler.Loan memory loan = cooler_.getLoan(loanID_); + + // Validate that cooler was deployed by the trusted factory. + if (!factory.created(address(cooler_))) revert OnlyFromFactory(); + + // Calculate extension interest based on the remaining principal. + uint256 interestBase = interestForLoan(loan.principal, loan.request.duration); + + // Transfer in extension interest from the caller. + reserve.transferFrom(msg.sender, address(this), interestBase * times_); + if (active) { + _sweepIntoSavingsVault(interestBase * times_); + } else { + _defund(reserve, interestBase * times_); + } + + // Signal to cooler that loan should be extended. + cooler_.extendLoanTerms(loanID_, times_); + } + + /// @notice Batch several default claims to save gas. + /// The elements on both arrays must be paired based on their index. + /// @dev Implements an auction style reward system that linearly increases up to a max reward. + /// @param coolers_ Array of contracts where the default must be claimed. + /// @param loans_ Array of defaulted loan ids. + function claimDefaulted(address[] calldata coolers_, uint256[] calldata loans_) external { + uint256 loans = loans_.length; + if (loans != coolers_.length) revert LengthDiscrepancy(); + + uint256 keeperRewards; + uint256 totalInterest; + uint256 totalPrincipal; + for (uint256 i = 0; i < loans; ) { + // Validate that cooler was deployed by the trusted factory. + if (!factory.created(coolers_[i])) revert OnlyFromFactory(); + + // Validate that loan was written by clearinghouse. + if (Cooler(coolers_[i]).getLoan(loans_[i]).lender != address(this)) revert NotLender(); + + // Claim defaults and update cached metrics. + (uint256 principal, uint256 interest, uint256 collateral, uint256 elapsed) = Cooler( + coolers_[i] + ).claimDefaulted(loans_[i]); + + unchecked { + // Cannot overflow due to max supply limits for both tokens + totalPrincipal += principal; + totalInterest += interest; + // There will not exist more than 2**256 loans + ++i; + } + + // Cap rewards to 5% of the collateral to avoid OHM holder's dillution. + uint256 maxAuctionReward = (collateral * 5e16) / 1e18; + + // Cap rewards to avoid exorbitant amounts. + uint256 maxReward = (maxAuctionReward < MAX_REWARD) ? maxAuctionReward : MAX_REWARD; + + // Calculate rewards based on the elapsed time since default. + keeperRewards = (elapsed < 7 days) + ? keeperRewards + (maxReward * elapsed) / 7 days + : keeperRewards + maxReward; + } + + // Decrement loan receivables. + interestReceivables = (interestReceivables > totalInterest) + ? interestReceivables - totalInterest + : 0; + principalReceivables = (principalReceivables > totalPrincipal) + ? principalReceivables - totalPrincipal + : 0; + + // Update outstanding debt owed to the Treasury upon default. + uint256 outstandingDebt = TRSRY.reserveDebt(reserve, address(this)); + + // debt owed to TRSRY = user debt - user interest + TRSRY.setDebt({ + debtor_: address(this), + token_: reserve, + amount_: (outstandingDebt > totalPrincipal) ? outstandingDebt - totalPrincipal : 0 + }); + + // Reward keeper. + gohm.transfer(msg.sender, keeperRewards); + // Burn the outstanding collateral of defaulted loans. + burn(); + } + + // --- CALLBACKS ----------------------------------------------------- + + /// @notice Overridden callback to decrement loan receivables. + /// @param *unused loadID_ of the load. + /// @param principalPaid_ in reserve. + /// @param interestPaid_ in reserve. + function _onRepay(uint256, uint256 principalPaid_, uint256 interestPaid_) internal override { + if (active) { + _sweepIntoSavingsVault(principalPaid_ + interestPaid_); + } else { + _defund(reserve, principalPaid_ + interestPaid_); + } + + // Decrement loan receivables. + interestReceivables = (interestReceivables > interestPaid_) + ? interestReceivables - interestPaid_ + : 0; + principalReceivables = (principalReceivables > principalPaid_) + ? principalReceivables - principalPaid_ + : 0; + } + + /// @notice Unused callback since defaults are handled by the clearinghouse. + /// @dev Overriden and left empty to save gas. + function _onDefault(uint256, uint256, uint256, uint256) internal override {} + + // --- FUNDING --------------------------------------------------- + + /// @notice Fund loan liquidity from treasury. + /// @dev Exposure is always capped at FUND_AMOUNT and rebalanced at up to FUND_CADANCE. + /// If several rebalances are available (because some were missed), calling this + /// function several times won't impact the funds controlled by the contract. + /// If the emergency shutdown is triggered, a rebalance will send funds back to + /// the treasury. + /// @return False if too early to rebalance. Otherwise, true. + function rebalance() public returns (bool) { + // If the contract is deactivated, defund. + uint256 maxFundAmount = active ? FUND_AMOUNT : 0; + // Update funding schedule if necessary. + if (fundTime > block.timestamp) return false; + fundTime += FUND_CADENCE; + + // Sweep reserve into DSR if necessary. + uint256 idle = reserve.balanceOf(address(this)); + if (idle != 0) _sweepIntoSavingsVault(idle); + + uint256 reserveBalance = sReserve.maxWithdraw(address(this)); + uint256 outstandingDebt = TRSRY.reserveDebt(reserve, address(this)); + // Rebalance funds on hand with treasury's reserves. + if (reserveBalance < maxFundAmount) { + // Since users loans are denominated in reserve, the clearinghouse + // debt is set in reserve terms. It must be adjusted when funding. + uint256 fundAmount = maxFundAmount - reserveBalance; + TRSRY.setDebt({ + debtor_: address(this), + token_: reserve, + amount_: outstandingDebt + fundAmount + }); + + // Since TRSRY holds sReserve, a conversion must be done before + // funding the clearinghouse. + uint256 sReserveAmount = sReserve.previewWithdraw(fundAmount); + TRSRY.increaseWithdrawApproval(address(this), sReserve, sReserveAmount); + TRSRY.withdrawReserves(address(this), sReserve, sReserveAmount); + + // Log the event. + emit Rebalance(false, fundAmount); + } else if (reserveBalance > maxFundAmount) { + // Since users loans are denominated in reserve, the clearinghouse + // debt is set in reserve terms. It must be adjusted when defunding. + uint256 defundAmount = reserveBalance - maxFundAmount; + TRSRY.setDebt({ + debtor_: address(this), + token_: reserve, + amount_: (outstandingDebt > defundAmount) ? outstandingDebt - defundAmount : 0 + }); + + // Since TRSRY holds sReserve, a conversion must be done before + // sending sReserve back. + uint256 sReserveAmount = sReserve.previewWithdraw(defundAmount); + sReserve.transfer(address(TRSRY), sReserveAmount); + + // Log the event. + emit Rebalance(true, defundAmount); + } + + return true; + } + + /// @notice Sweep excess reserve into savings vault. + function sweepIntoSavingsVault() public { + uint256 reserveBalance = reserve.balanceOf(address(this)); + _sweepIntoSavingsVault(reserveBalance); + } + + /// @notice Sweep excess reserve into vault. + function _sweepIntoSavingsVault(uint256 amount_) internal { + reserve.approve(address(sReserve), amount_); + sReserve.deposit(amount_, address(this)); + } + + /// @notice Public function to burn gOHM. + /// @dev Can be used to burn any gOHM defaulted using the Cooler instead of the Clearinghouse. + function burn() public { + uint256 gohmBalance = gohm.balanceOf(address(this)); + // Unstake and burn gOHM holdings. + gohm.approve(address(staking), gohmBalance); + MINTR.burnOhm(address(this), staking.unstake(address(this), gohmBalance, false, false)); + } + + // --- ADMIN --------------------------------------------------- + + /// @notice Activate the contract. + function activate() external onlyRole("cooler_overseer") { + active = true; + fundTime = block.timestamp; + + // Signal to CHREG that the contract has been activated. + CHREG.activateClearinghouse(address(this)); + + emit Activate(); + } + + /// @notice Deactivate the contract and return funds to treasury. + function emergencyShutdown() external onlyRole("emergency_shutdown") { + active = false; + + // If necessary, defund sReserve. + uint256 sReserveBalance = sReserve.balanceOf(address(this)); + if (sReserveBalance != 0) _defund(sReserve, sReserveBalance); + + // If necessary, defund reserve. + uint256 reserveBalance = reserve.balanceOf(address(this)); + if (reserveBalance != 0) _defund(reserve, reserveBalance); + + // Signal to CHREG that the contract has been deactivated. + CHREG.deactivateClearinghouse(address(this)); + + emit Deactivate(); + } + + /// @notice Return funds to treasury. + /// @param token_ to transfer. + /// @param amount_ to transfer. + function defund(ERC20 token_, uint256 amount_) external onlyRole("cooler_overseer") { + if (token_ == gohm) revert OnlyBurnable(); + _defund(token_, amount_); + } + + /// @notice Internal function to return funds to treasury. + /// @param token_ to transfer. + /// @param amount_ to transfer. + function _defund(ERC20 token_, uint256 amount_) internal { + if (token_ == sReserve || token_ == reserve) { + // Since users loans are denominated in reserve, the clearinghouse + // debt is set in reserve terms. It must be adjusted when defunding. + uint256 outstandingDebt = TRSRY.reserveDebt(reserve, address(this)); + uint256 reserveAmount = (token_ == sReserve) + ? sReserve.previewRedeem(amount_) + : amount_; + + TRSRY.setDebt({ + debtor_: address(this), + token_: reserve, + amount_: (outstandingDebt > reserveAmount) ? outstandingDebt - reserveAmount : 0 + }); + } + + // Defund and log the event + token_.transfer(address(TRSRY), amount_); + emit Defund(address(token_), amount_); + } + + // --- AUX FUNCTIONS --------------------------------------------- + + /// @notice view function computing collateral for a loan amount. + function getCollateralForLoan(uint256 principal_) external pure returns (uint256) { + return (principal_ * 1e18) / LOAN_TO_COLLATERAL; + } + + /// @notice view function computing loan for a collateral amount. + /// @param collateral_ amount of gOHM. + /// @return debt (amount to be lent + interest) for a given collateral amount. + function getLoanForCollateral(uint256 collateral_) public pure returns (uint256, uint256) { + uint256 principal = (collateral_ * LOAN_TO_COLLATERAL) / 1e18; + uint256 interest = interestForLoan(principal, DURATION); + return (principal, interest); + } + + /// @notice view function to compute the interest for given principal amount. + /// @param principal_ amount of reserve being lent. + /// @param duration_ elapsed time in seconds. + function interestForLoan(uint256 principal_, uint256 duration_) public pure returns (uint256) { + uint256 interestPercent = (INTEREST_RATE * duration_) / 365 days; + return (principal_ * interestPercent) / 1e18; + } + + /// @notice Get total receivable reserve for the treasury. + /// Includes both principal and interest. + function getTotalReceivables() external view returns (uint256) { + return principalReceivables + interestReceivables; + } +} diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index 9c025729d..2a3418baf 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -24,6 +24,9 @@ import {ClonesWithImmutableArgs} from "clones/ClonesWithImmutableArgs.sol"; import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; +import {ClearinghouseLowerLTC} from "src/test/lib/ClearinghouseLowerLTC.sol"; +import {ClearinghouseHigherLTC} from "src/test/lib/ClearinghouseHigherLTC.sol"; + contract LoanConsolidatorForkTest is Test { using stdStorage for StdStorage; using ClonesWithImmutableArgs for address; @@ -455,6 +458,66 @@ contract LoanConsolidatorForkTest is Test { _; } + function _createClearinghouseWithLowerLTC() internal returns (Clearinghouse) { + uint256 registryCountBefore = CHREG.registryCount(); + + ClearinghouseLowerLTC newClearinghouse = new ClearinghouseLowerLTC( + address(ohm), + address(gohm), + address(staking), + address(sdai), + address(coolerFactory), + address(kernel) + ); + + // Activate as a policy + vm.prank(kernelExecutor); + kernel.executeAction(Actions.ActivatePolicy, address(newClearinghouse)); + + // Activate the new clearinghouse + newClearinghouse.activate(); + // Rebalance the new clearinghouse + newClearinghouse.rebalance(); + + // Increment the CHREG registry count + // The registryCount does not seem to be incremented on the fork, which is... weird. + stdstore.target(address(CHREG)).sig("registryCount()").checked_write( + registryCountBefore + 1 + ); + + return Clearinghouse(address(newClearinghouse)); + } + + function _createClearinghouseWithHigherLTC() internal returns (Clearinghouse) { + uint256 registryCountBefore = CHREG.registryCount(); + + ClearinghouseHigherLTC newClearinghouse = new ClearinghouseHigherLTC( + address(ohm), + address(gohm), + address(staking), + address(sdai), + address(coolerFactory), + address(kernel) + ); + + // Activate as a policy + vm.prank(kernelExecutor); + kernel.executeAction(Actions.ActivatePolicy, address(newClearinghouse)); + + // Activate the new clearinghouse + newClearinghouse.activate(); + // Rebalance the new clearinghouse + newClearinghouse.rebalance(); + + // Increment the CHREG registry count + // The registryCount does not seem to be incremented on the fork, which is... weird. + stdstore.target(address(CHREG)).sig("registryCount()").checked_write( + registryCountBefore + 1 + ); + + return Clearinghouse(address(newClearinghouse)); + } + // ===== ASSERTIONS ===== // function _assertCoolerLoans(uint256 collateral_) internal { @@ -739,6 +802,12 @@ contract LoanConsolidatorForkTest is Test { // when clearinghouseFrom is DAI and clearinghouseTo is DAI // [X] the loans on coolerFrom are migrated to coolerTo // [X] the Cooler owner receives DAI from the new loan + // given clearinghouseFrom has a lower LTC than clearinghouseTo + // [X] the cooler owner receives a new loan for the old principal amount based on a higher LTC/higher collateral amount + // given clearinghouseFrom has a higher LTC than clearinghouseTo + // given the cooler owner does not have enough collateral for the new loan + // [X] it reverts + // [X] the Cooler owner receives a new loan for the old principal amount based on a lower LTC/lower collateral amount // --- consolidate -------------------------------------------- @@ -1559,6 +1628,140 @@ contract LoanConsolidatorForkTest is Test { _assertApprovals(coolerUsds, coolerUsds); } + function test_consolidate_clearinghouseFromLowerLTC() public givenPolicyActive givenActivated { + // Create a new Clearinghouse with a higher LTC + Clearinghouse newClearinghouse = _createClearinghouseWithHigherLTC(); + + // Calculate the collateral required for the existing loans + (uint256 existingPrincipal, ) = clearinghouse.getLoanForCollateral(_GOHM_AMOUNT); + uint256 newCollateralRequired = newClearinghouse.getCollateralForLoan(existingPrincipal); + + uint256[] memory idsA = _idsA(); + + // Record the amount of DAI in the wallet + uint256 initPrincipal = dai.balanceOf(walletA); + uint256 interestDue = _getInterestDue(idsA); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(newClearinghouse), + address(coolerA), + address(coolerA), + idsA + ); + + // Consolidate loans + _consolidate( + walletA, + address(clearinghouse), + address(newClearinghouse), + address(coolerA), + address(coolerA), + idsA + ); + + _assertCoolerLoans(newCollateralRequired); + _assertApprovals(); + + // WalletA should have received the principal amount + assertEq(dai.balanceOf(walletA), initPrincipal - interestDue, "walletA: dai balance"); + // Balance of gOHM should be the old collateral amount - new collateral required + assertEq( + gohm.balanceOf(walletA), + _GOHM_AMOUNT - newCollateralRequired, + "walletA: gOHM balance" + ); + // Balance of gOHM in the cooler should be the new collateral required + assertEq(gohm.balanceOf(address(coolerA)), newCollateralRequired, "coolerA: gOHM balance"); + // Balance of gOHM on the LoanConsolidator should be 0 + assertEq(gohm.balanceOf(address(utils)), 0, "policy: gOHM balance"); + } + + function test_consolidate_clearinghouseFromHigherLTC_insufficientCollateral_reverts() + public + givenPolicyActive + givenActivated + { + // Create a new Clearinghouse with a lower LTC + Clearinghouse newClearinghouse = _createClearinghouseWithLowerLTC(); + + // Do NOT deal more collateral to the wallet + + uint256[] memory idsA = _idsA(); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(newClearinghouse), + address(coolerA), + address(coolerA), + idsA + ); + + // Expect revert + vm.expectRevert("ERC20: transfer amount exceeds balance"); + + // Consolidate loans + _consolidate( + walletA, + address(clearinghouse), + address(newClearinghouse), + address(coolerA), + address(coolerA), + idsA + ); + } + + function test_consolidate_clearinghouseFromHigherLTC() public givenPolicyActive givenActivated { + // Create a new Clearinghouse with a lower LTC + Clearinghouse newClearinghouse = _createClearinghouseWithLowerLTC(); + + // Calculate the collateral required for the existing loans + (uint256 existingPrincipal, ) = clearinghouse.getLoanForCollateral(_GOHM_AMOUNT); + uint256 newCollateralRequired = newClearinghouse.getCollateralForLoan(existingPrincipal); + + // Deal the difference in collateral to the wallet + deal(address(gohm), walletA, newCollateralRequired - _GOHM_AMOUNT); + + uint256[] memory idsA = _idsA(); + + // Record the amount of DAI in the wallet + uint256 initPrincipal = dai.balanceOf(walletA); + uint256 interestDue = _getInterestDue(idsA); + + // Grant approvals + _grantCallerApprovals( + walletA, + address(newClearinghouse), + address(coolerA), + address(coolerA), + idsA + ); + + // Consolidate loans + _consolidate( + walletA, + address(clearinghouse), + address(newClearinghouse), + address(coolerA), + address(coolerA), + idsA + ); + + _assertCoolerLoans(newCollateralRequired); + _assertApprovals(); + + // WalletA should have received the principal amount + assertEq(dai.balanceOf(walletA), initPrincipal - interestDue, "walletA: dai balance"); + // Balance of gOHM should be 0, as the new collateral amount was used + assertEq(gohm.balanceOf(walletA), 0, "walletA: gOHM balance"); + // Balance of gOHM in the cooler should be the new collateral required + assertEq(gohm.balanceOf(address(coolerA)), newCollateralRequired, "coolerA: gOHM balance"); + // Balance of gOHM on the LoanConsolidator should be 0 + assertEq(gohm.balanceOf(address(utils)), 0, "policy: gOHM balance"); + } + // consolidateWithNewOwner // given the contract has not been activated as a policy // [X] it reverts @@ -2495,6 +2698,10 @@ contract LoanConsolidatorForkTest is Test { // [X] it provides the correct values // when clearinghouseFrom is DAI and clearinghouseTo is DAI // [X] it provides the correct values + // given clearinghouseFrom has a lower LTC than clearinghouseTo + // [X] gOHM approval is higher than the collateral amount for the existing loans + // given clearinghouseFrom has a higher LTC than clearinghouseTo + // [X] gOHM approval is lower than the collateral amount for the existing loans function test_requiredApprovals_policyNotActive_reverts() public { uint256[] memory ids = _idsA(); @@ -2810,8 +3017,49 @@ contract LoanConsolidatorForkTest is Test { assertEq(gohmApproval, clearinghouse.getCollateralForLoan(totalPrincipal), "gOHM approval"); } + function test_requiredApprovals_clearinghouseFromLowerLTC() public givenPolicyActive { + // Create a new Clearinghouse with a higher LTC + Clearinghouse newClearinghouse = _createClearinghouseWithHigherLTC(); + + // Calculate the collateral required for the existing loans + (uint256 existingPrincipal, ) = clearinghouse.getLoanForCollateral(_GOHM_AMOUNT); + uint256 newCollateralRequired = newClearinghouse.getCollateralForLoan(existingPrincipal); + + uint256[] memory loanIds = _idsA(); + (, uint256 gohmApproval, , , ) = utils.requiredApprovals( + address(newClearinghouse), + address(coolerA), + loanIds + ); + + assertEq(gohmApproval, newCollateralRequired, "gOHM approval"); + } + + function test_requiredApprovals_clearinghouseFromHigherLTC() public givenPolicyActive { + // Create a new Clearinghouse with a lower LTC + Clearinghouse newClearinghouse = _createClearinghouseWithLowerLTC(); + + // Calculate the collateral required for the existing loans + (uint256 existingPrincipal, ) = clearinghouse.getLoanForCollateral(_GOHM_AMOUNT); + uint256 newCollateralRequired = newClearinghouse.getCollateralForLoan(existingPrincipal); + + uint256[] memory loanIds = _idsA(); + (, uint256 gohmApproval, , , ) = utils.requiredApprovals( + address(newClearinghouse), + address(coolerA), + loanIds + ); + + assertEq(gohmApproval, newCollateralRequired, "gOHM approval"); + } + // collateralRequired - // [X] it returns the correct values + // given clearinghouseFrom has the same LTC as clearinghouseTo + // [X] it returns the correct values + // given clearinghouseFrom has a lower LTC than clearinghouseTo + // [X] additional collateral is 0 + // given clearinghouseFrom has a higher LTC than clearinghouseTo + // [X] additional collateral is required function test_collateralRequired_fuzz( uint256 loanOneCollateral_, @@ -2885,6 +3133,64 @@ contract LoanConsolidatorForkTest is Test { assertEq(additionalCollateral, additionalCollateralExpected, "additional collateral"); } + function test_collateralRequired_clearinghouseFromLowerLTC() public givenPolicyActive { + // Create a new Clearinghouse with a higher LTC + Clearinghouse newClearinghouse = _createClearinghouseWithHigherLTC(); + + // Calculate the collateral required for the existing loans + (uint256 existingPrincipal, ) = clearinghouse.getLoanForCollateral(_GOHM_AMOUNT); + uint256 newCollateralRequired = newClearinghouse.getCollateralForLoan(existingPrincipal); + + uint256[] memory loanIds = _idsA(); + + ( + uint256 consolidatedLoanCollateral, + uint256 existingLoanCollateral, + uint256 additionalCollateral + ) = utils.collateralRequired(address(newClearinghouse), address(coolerA), loanIds); + + // Assert values + // Consolidated loan collateral is the same as what the new Clearinghouse requires + assertEq(consolidatedLoanCollateral, newCollateralRequired, "consolidated loan collateral"); + + // Existing loan collateral is the same as what has been deposited already + assertEq(existingLoanCollateral, _GOHM_AMOUNT, "existing loan collateral"); + + // Additional collateral is 0, as less collateral is required + assertEq(additionalCollateral, 0, "additional collateral"); + } + + function test_collateralRequired_clearinghouseFromHigherLTC() public givenPolicyActive { + // Create a new Clearinghouse with a lower LTC + Clearinghouse newClearinghouse = _createClearinghouseWithLowerLTC(); + + // Calculate the collateral required for the existing loans + (uint256 existingPrincipal, ) = clearinghouse.getLoanForCollateral(_GOHM_AMOUNT); + uint256 newCollateralRequired = newClearinghouse.getCollateralForLoan(existingPrincipal); + + uint256[] memory loanIds = _idsA(); + + ( + uint256 consolidatedLoanCollateral, + uint256 existingLoanCollateral, + uint256 additionalCollateral + ) = utils.collateralRequired(address(newClearinghouse), address(coolerA), loanIds); + + // Assert values + // Consolidated loan collateral is the same as what the new Clearinghouse requires + assertEq(consolidatedLoanCollateral, newCollateralRequired, "consolidated loan collateral"); + + // Existing loan collateral is the same as what has been deposited already + assertEq(existingLoanCollateral, _GOHM_AMOUNT, "existing loan collateral"); + + // Additional collateral is the difference between the existing loan collateral and what the new Clearinghouse requires + assertEq( + additionalCollateral, + newCollateralRequired - _GOHM_AMOUNT, + "additional collateral" + ); + } + // fundsRequired // given there is no protocol fee // [X] it returns the correct values From f70896c5e87e0d1817e363ff97e443455c86ee05 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 31 Oct 2024 16:43:06 +0400 Subject: [PATCH 098/160] Fix: lenderFee should be exclusive of totalInterest --- src/policies/LoanConsolidator.sol | 4 +- src/test/policies/LoanConsolidatorFork.t.sol | 79 +++++++++++++++++++- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index 75b4cb2b1..e4ad446fa 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -813,7 +813,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent uint256 totalFees; { uint256 protocolFee = getProtocolFee(totalPrincipal + totalInterest); - uint256 lenderFee = FLASH.flashFee(address(DAI), totalPrincipal + totalInterest); + uint256 lenderFee = FLASH.flashFee(address(DAI), totalPrincipal); totalFees = totalInterest + lenderFee + protocolFee; } @@ -905,7 +905,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent reserveTo = _getClearinghouseReserveToken(clearinghouseTo_); protocolFee = getProtocolFee(totalPrincipal + totalInterest); interest = totalInterest; - lenderFee = FLASH.flashFee(address(DAI), totalPrincipal + totalInterest); + lenderFee = FLASH.flashFee(address(DAI), totalPrincipal); return (reserveTo, interest, lenderFee, protocolFee); } diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index 2a3418baf..5c4519b07 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -1329,7 +1329,7 @@ contract LoanConsolidatorForkTest is Test { givenActivated givenAdminHasRole givenMockFlashloanLender - givenMockFlashloanLenderFee(100) // 1% + givenMockFlashloanLenderFee(1000) // 10% givenMockFlashloanLenderHasBalance(20_000_000e18) { uint256[] memory idsA = _idsA(); @@ -2690,6 +2690,8 @@ contract LoanConsolidatorForkTest is Test { // [X] it returns the correct values // when the protocol fee is non-zero // [X] it returns the correct values + // when the lender fee is non-zero + // [X] it returns the correct values // when clearinghouseFrom is DAI and clearinghouseTo is USDS // [X] it provides the correct values // when clearinghouseFrom is USDS and clearinghouseTo is DAI @@ -2841,6 +2843,47 @@ contract LoanConsolidatorForkTest is Test { ); } + function test_requiredApprovals_lenderFee() + public + givenPolicyActive + givenAdminHasRole + givenMockFlashloanLender + givenMockFlashloanLenderFee(1000) // 10% + givenMockFlashloanLenderHasBalance(20_000_000e18) + { + uint256[] memory ids = _idsA(); + + ( + address owner_, + uint256 gohmApproval, + address reserveTo, + uint256 ownerReserveTo, + uint256 callerReserveTo + ) = utils.requiredApprovals(address(clearinghouse), address(coolerA), ids); + + uint256 expectedPrincipal; + uint256 expectedInterest; + for (uint256 i = 0; i < ids.length; i++) { + Cooler.Loan memory loan = coolerA.getLoan(ids[i]); + + expectedPrincipal += loan.principal; + expectedInterest += loan.interestDue; + } + + uint256 expectedProtocolFee = 0; + uint256 expectedLenderFee = (expectedPrincipal * 1000) / _ONE_HUNDRED_PERCENT; + + assertEq(owner_, walletA, "owner"); + assertEq(gohmApproval, _GOHM_AMOUNT, "gOHM approval"); + assertEq(reserveTo, address(dai), "reserveTo"); + assertEq(ownerReserveTo, expectedPrincipal, "ownerReserveTo"); + assertEq( + callerReserveTo, + expectedInterest + expectedProtocolFee + expectedLenderFee, + "callerReserveTo" + ); + } + function test_requiredApprovals_protocolFee_daiToUsds() public givenPolicyActive @@ -3194,6 +3237,8 @@ contract LoanConsolidatorForkTest is Test { // fundsRequired // given there is no protocol fee // [X] it returns the correct values + // given there is a lender fee + // [X] it returns the correct values // given the loan has interest due // [X] it returns the correct values // given clearinghouseTo is DAI @@ -3226,6 +3271,38 @@ contract LoanConsolidatorForkTest is Test { assertEq(protocolFee, expectedProtocolFee, "protocolFee"); } + function test_fundsRequired_lenderFee() + public + givenPolicyActive + givenAdminHasRole + givenMockFlashloanLender + givenMockFlashloanLenderFee(10000) // 10% + givenMockFlashloanLenderHasBalance(20_000_000e18) + { + uint256[] memory ids = _idsA(); + + // Calculate the interest due + uint256 expectedPrincipal; + uint256 expectedInterest; + for (uint256 i = 0; i < ids.length; i++) { + Cooler.Loan memory loan = coolerA.getLoan(ids[i]); + + expectedPrincipal += loan.principal; + expectedInterest += loan.interestDue; + } + + uint256 expectedProtocolFee = 0; + uint256 expectedLenderFee = (expectedPrincipal * 10000) / _ONE_HUNDRED_PERCENT; + + (address reserveTo, uint256 interest, uint256 lenderFee, uint256 protocolFee) = utils + .fundsRequired(address(clearinghouse), address(coolerA), ids); + + assertEq(reserveTo, address(dai), "reserveTo"); + assertEq(interest, expectedInterest, "interest"); + assertEq(lenderFee, expectedLenderFee, "lenderFee"); + assertEq(protocolFee, expectedProtocolFee, "protocolFee"); + } + function test_fundsRequired_interestDue() public givenPolicyActive From 31732c7a1ec622d631e428f4f955acc119891029 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 1 Nov 2024 10:59:48 +0400 Subject: [PATCH 099/160] Fix: Contract names can be visually identical but have different ascii representations --- src/modules/RGSTY/OlympusContractRegistry.sol | 35 ++++++++++++--- src/test/modules/RGSTY.t.sol | 44 +++++++++++++++++++ 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/modules/RGSTY/OlympusContractRegistry.sol b/src/modules/RGSTY/OlympusContractRegistry.sol index 857449528..6f38c64f1 100644 --- a/src/modules/RGSTY/OlympusContractRegistry.sol +++ b/src/modules/RGSTY/OlympusContractRegistry.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.15; import {Kernel, Module, Policy, Keycode, toKeycode} from "src/Kernel.sol"; import {RGSTYv1} from "./RGSTY.v1.sol"; +import {console2} from "forge-std/console2.sol"; + /// @title Olympus Contract Registry /// @notice This module is used to track the addresses of contracts. /// It supports both immutable and mutable addresses. @@ -216,23 +218,46 @@ contract OlympusContractRegistry is RGSTYv1 { /// @notice Validates the contract name /// @dev This function will revert if: /// - The name is empty + /// - Null characters are found in the start or middle of the name /// - The name contains punctuation or uppercase letters function _validateContractName(bytes5 name_) internal pure { + bool validCharacterFound = false; + // Check that the contract name is lowercase letters and numerals only for (uint256 i = 0; i < 5; i++) { bytes1 char = name_[i]; + // When a null character is found, it should only be followed by null characters + if (char == 0x00) { + for (uint256 j = i + 1; j < 5; j++) { + if (name_[j] != 0x00) revert Params_InvalidName(); + } + + // If reaching this far, then all of the subsequent characters are null characters + return; + } + + // Before finding valid characters, we should not find a null character + // This prevents names like "\x00\x00ohm" from being registered, which could be visually indistinguishable from "ohm" + if (char == 0x00 && validCharacterFound == false) revert Params_InvalidName(); + // 0-9 - if (char >= 0x30 && char <= 0x39) continue; + if (char >= 0x30 && char <= 0x39) { + validCharacterFound = true; + continue; + } // a-z - if (char >= 0x61 && char <= 0x7A) continue; - - // Skip if empty - if (char == 0x00) continue; + if (char >= 0x61 && char <= 0x7A) { + validCharacterFound = true; + continue; + } revert Params_InvalidName(); } + + // Catch-all + if (validCharacterFound == false) revert Params_InvalidName(); } /// @notice Updates the list of immutable contract names if the name is not already present. diff --git a/src/test/modules/RGSTY.t.sol b/src/test/modules/RGSTY.t.sol index 510d1b9d0..f4003bf47 100644 --- a/src/test/modules/RGSTY.t.sol +++ b/src/test/modules/RGSTY.t.sol @@ -131,6 +131,10 @@ contract ContractRegistryTest is Test { // [X] it reverts // when the name is empty // [X] it reverts + // when the start of the name contains null characters + // [X] it reverts + // when the end of the name contains null characters + // [X] it succeeds // when the contract address is zero // [X] it reverts // when the name is not lowercase @@ -165,6 +169,24 @@ contract ContractRegistryTest is Test { _registerImmutableContract(bytes5(""), addressOne); } + function test_registerImmutableContract_whenNameStartsWithNullCharacters_reverts() public { + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); + + _registerImmutableContract(bytes5("\x00\x00ohm"), addressOne); + } + + function test_registerImmutableContract_whenNameContainsNullCharacters_reverts() public { + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); + + _registerImmutableContract(bytes5("o\x00\x00hm"), addressOne); + } + + function test_registerImmutableContract_whenNameEndsWithNullCharacters_succeeds() public { + _registerImmutableContract(bytes5("ohm\x00"), addressOne); + + assertEq(RGSTY.getImmutableContract(bytes5("ohm")), addressOne); + } + function test_registerImmutableContract_whenNameIsZero_reverts() public { vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); @@ -325,6 +347,10 @@ contract ContractRegistryTest is Test { // [X] it reverts // when the name is empty // [X] it reverts + // when the start of the name contains null characters + // [X] it reverts + // when the end of the name contains null characters + // [X] it succeeds // when the contract address is zero // [X] it reverts // when the name is not lowercase @@ -359,6 +385,24 @@ contract ContractRegistryTest is Test { _registerContract(bytes5(""), addressOne); } + function test_registerContract_whenNameStartsWithNullCharacters_reverts() public { + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); + + _registerContract(bytes5("\x00\x00ohm"), addressOne); + } + + function test_registerContract_whenNameContainsNullCharacters_reverts() public { + vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); + + _registerContract(bytes5("o\x00\x00hm"), addressOne); + } + + function test_registerContract_whenNameEndsWithNullCharacters_succeeds() public { + _registerContract(bytes5("ohm\x00"), addressOne); + + assertEq(RGSTY.getContract(bytes5("ohm")), addressOne); + } + function test_registerContract_whenNameIsZero_reverts() public { vm.expectRevert(abi.encodeWithSelector(RGSTYv1.Params_InvalidName.selector)); From 4aa38c5e3711016664260cb2e52564171fcdcce2 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 1 Nov 2024 11:09:19 +0400 Subject: [PATCH 100/160] Fix: Comment / minor code improvements --- src/modules/CHREG/CHREG.v1.sol | 8 +++++--- .../CHREG/OlympusClearinghouseRegistry.sol | 2 +- src/policies/LoanConsolidator.sol | 18 ++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/modules/CHREG/CHREG.v1.sol b/src/modules/CHREG/CHREG.v1.sol index f94fc49af..212fbae0e 100644 --- a/src/modules/CHREG/CHREG.v1.sol +++ b/src/modules/CHREG/CHREG.v1.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.15; -import "src/Kernel.sol"; +import {Module} from "src/Kernel.sol"; /// @title Olympus Clearinghouse Registry /// @notice Olympus Clearinghouse Registry (Module) Contract /// @dev The Olympus Clearinghouse Registry Module tracks the lending facilities that the Olympus -/// protocol deploys to satisfy the Cooler Loan demand. This allows for a single-soure of truth +/// protocol deploys to satisfy the Cooler Loan demand. This allows for a single-source of truth /// for reporting purposes around the total Treasury holdings as well as its projected receivables. abstract contract CHREGv1 is Module { // ========= ERRORS ========= // @@ -42,11 +42,13 @@ abstract contract CHREGv1 is Module { /// @notice Adds a Clearinghouse to the registry. /// Only callable by permissioned policies. + /// /// @param clearinghouse_ The address of the clearinghouse. function activateClearinghouse(address clearinghouse_) external virtual; /// @notice Deactivates a clearinghouse from the registry. /// Only callable by permissioned policies. - /// @param clearinghouse_ The address of the clearginhouse. + /// + /// @param clearinghouse_ The address of the clearinghouse. function deactivateClearinghouse(address clearinghouse_) external virtual; } diff --git a/src/modules/CHREG/OlympusClearinghouseRegistry.sol b/src/modules/CHREG/OlympusClearinghouseRegistry.sol index 88e73b62e..857770fd5 100644 --- a/src/modules/CHREG/OlympusClearinghouseRegistry.sol +++ b/src/modules/CHREG/OlympusClearinghouseRegistry.sol @@ -7,7 +7,7 @@ import {CHREGv1} from "modules/CHREG/CHREG.v1.sol"; /// @title Olympus Clearinghouse Registry /// @notice Olympus Clearinghouse Registry (Module) Contract /// @dev The Olympus Clearinghouse Registry Module tracks the lending facilities that the Olympus -/// protocol deploys to satisfy the Cooler Loan demand. This allows for a single-soure of truth +/// protocol deploys to satisfy the Cooler Loan demand. This allows for a single-source of truth /// for reporting purposes around the total Treasury holdings as well as its projected receivables. contract OlympusClearinghouseRegistry is CHREGv1 { //============================================================================================// diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index e4ad446fa..07613a624 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -237,8 +237,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// /// @dev This function will revert if: /// - The caller is not the 'coolerFrom' and 'coolerTo' owner. - /// - The caller has not approved this contract to spend the fees in DAI. - /// - The caller has not approved this contract to spend the reserve token of `clearinghouseTo_` in order to repay the flashloan. + /// - The caller has not approved this contract to spend the reserve token of `clearinghouseTo_` in order to pay the interest, lender and protocol fees. /// - The caller has not approved this contract to spend the gOHM escrowed by `coolerFrom_`. /// - `clearinghouseFrom_` or `clearinghouseTo_` is not registered with the Clearinghouse registry. /// - `coolerFrom_` or `coolerTo_` is not a valid Cooler for the respective Clearinghouse. @@ -285,8 +284,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// - The caller is not the `coolerFrom_` owner. /// - `coolerFrom_` is the same as `coolerTo_` (in which case `consolidate()` should be used). /// - The owner of `coolerFrom_` is the same as `coolerTo_` (in which case `consolidate()` should be used). - /// - The caller has not approved this contract to spend the fees in DAI. - /// - The caller has not approved this contract to spend the reserve token of `clearinghouseTo_` in order to repay the flashloan. + /// - The caller has not approved this contract to spend the reserve token of `clearinghouseTo_` in order to pay the interest, lender and protocol fees. /// - The caller has not approved this contract to spend the gOHM escrowed by the target Cooler. /// - `clearinghouseFrom_` or `clearinghouseTo_` is not registered with the Clearinghouse registry. /// - `coolerFrom_` or `coolerTo_` is not a valid Cooler for the respective Clearinghouse. @@ -425,7 +423,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent if (initiator_ != address(this)) revert OnlyThis(); // Assumptions: - // - The flashloan provider has transferred amount_ in DAI, which includes the principal + // - The flashloan provider has transferred amount_ in DAI, which is equal to the principal // - This contract has transferred from the caller the interest, lender fee and protocol fee to this contract // If clearinghouseFrom is in USDS, then we need to convert the flashloan DAI to USDS in order to repay the principal @@ -438,7 +436,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent } // Ensure that the interest transferred from the caller is in terms of the reserveFrom token - // Fees are in terms of the reserveTo token + // Interest was collected in terms of the reserveTo token if (flashLoanData.migrationType == MigrationType.USDS_DAI) { DAI.approve(address(MIGRATOR), flashLoanData.interest); MIGRATOR.daiToUsds(address(this), flashLoanData.interest); @@ -457,7 +455,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent _repayDebtForLoans(address(flashLoanData.coolerFrom), flashLoanData.ids); // State: // - reserveFrom: reduced by principal and interest, should be 0 - // - reserveTo: no change, should be 0 + // - reserveTo: no change, balance is lender fee + protocol fee // - gOHM: no change, should be 0 // Calculate the amount of collateral that will be needed for the consolidated loan @@ -491,7 +489,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent // - gOHM: reduced by the collateral used for the consolidated loan. gOHM balance in this contract is now 0. // The coolerTo owner will receive `principal` quantity of `reserveTo` tokens for the consolidated loan - // Transfer the amount of `reserveTo` required to repay the flash loan (debt + interest), lender fee and protocol fee + // Transfer the principal amount in terms of `reserveTo`. The lender fee and protocol fee have already been transferred to this contract. // Approval must have already been granted by the Cooler owner flashLoanData.reserveTo.transferFrom( flashLoanData.coolerTo.owner(), @@ -500,7 +498,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent ); // State: // - reserveFrom: no change - // - reserveTo: increased by the flashloan amount, lender fee and protocol fee + // - reserveTo: increased by the loan principal, balance is principal + lender fee + protocol fee // - gOHM: no change, 0 // The flashloan needs to be repaid in DAI @@ -795,7 +793,7 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @param coolerFrom_ Cooler contract that issued the loans. /// @param ids_ Array of loan ids to be consolidated. /// @return owner Owner of the Cooler (address that should grant the approval). - /// @return ownerGOhm Amount of gOHM to be approved by the Cooler owner. + /// @return gOhmAmount Amount of gOHM to be approved by the Cooler owner. /// @return reserveTo Token that the approval is in terms of /// @return ownerReserveTo Amount of `reserveTo` to be approved by the Cooler owner. This will be the principal of the consolidated loan. /// @return callerReserveTo Amount of `reserveTo` that the caller will need to provide. From a641c10e11fc3d52e6e65fd2c095341ccdbd9be4 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 1 Nov 2024 11:11:54 +0400 Subject: [PATCH 101/160] Fix: Redundant sDAI token variable --- src/policies/LoanConsolidator.sol | 5 ----- src/test/policies/LoanConsolidatorFork.t.sol | 1 - 2 files changed, 6 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index 07613a624..c9454e684 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -119,10 +119,6 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent /// @dev The value is set when the policy is activated IERC20 internal DAI; - /// @notice The sDAI token - /// @dev The value is set when the policy is activated - IERC4626 internal SDAI; - /// @notice The USDS token /// @dev The value is set when the policy is activated IERC20 internal USDS; @@ -207,7 +203,6 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent // This function will be called whenever a contract is registered or deregistered, which enables caching of the values // Token contract addresses are immutable DAI = IERC20(RGSTY.getImmutableContract("dai")); - SDAI = IERC4626(RGSTY.getImmutableContract("sdai")); USDS = IERC20(RGSTY.getImmutableContract("usds")); GOHM = IERC20(RGSTY.getImmutableContract("gohm")); // Utility contract addresses are mutable diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index 5c4519b07..e16fda53f 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -128,7 +128,6 @@ contract LoanConsolidatorForkTest is Test { // Register the tokens with RGSTY vm.startPrank(address(this)); rgstyAdmin.registerImmutableContract("dai", address(dai)); - rgstyAdmin.registerImmutableContract("sdai", address(sdai)); rgstyAdmin.registerImmutableContract("gohm", address(gohm)); rgstyAdmin.registerImmutableContract("usds", address(usds)); rgstyAdmin.registerContract("flash", address(lender)); From 75bd4520635bc66085b3fd1b060a9162cd2aca1f Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 1 Nov 2024 11:19:32 +0400 Subject: [PATCH 102/160] Minor fix to build script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cbf15f3ad..afe96c24b 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "yarn": "use-pnpm" }, "scripts": { - "build": "chmod +x shell/* && ./shell/full_install.sh", + "build": "chmod +x shell/*.sh && ./shell/full_install.sh", "prettier": "prettier --no-error-on-unmatched-pattern --write 'src/**/*.sol' '**/*.html'", "prettier:list": "prettier --no-error-on-unmatched-pattern --list-different 'src/**/*.sol' '**/*.html'", "prettier:check": "prettier --no-error-on-unmatched-pattern --check 'src/**/*.sol' '**/*.html'", From 6fff1fc39bc1f5943556a755b0d08036df4316fb Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 1 Nov 2024 11:19:39 +0400 Subject: [PATCH 103/160] Fix: New cooler owner verification can be optimised --- src/policies/LoanConsolidator.sol | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index c9454e684..59c0d08b2 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -304,11 +304,9 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent // Ensure `msg.sender` is allowed to spend cooler funds on behalf of this contract if (Cooler(coolerFrom_).owner() != msg.sender) revert OnlyCoolerOwner(); - // Ensure that the caller is not trying to operate on the same Cooler - if (coolerFrom_ == coolerTo_) revert Params_InvalidCooler(); - // Ensure that the owner of the coolerFrom_ is not the same as coolerTo_ - if (Cooler(coolerFrom_).owner() == Cooler(coolerTo_).owner()) revert Params_InvalidCooler(); + // This also implicitly checks that the coolers must be different, ie. can't operate on the same Cooler + if (Cooler(coolerTo_).owner() == msg.sender) revert Params_InvalidCooler(); _consolidateWithFlashLoan( clearinghouseFrom_, From 57ab1f3cd6cd06619d43f2d900fbbfd830216ca4 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 1 Nov 2024 11:25:14 +0400 Subject: [PATCH 104/160] Fix: Iterating through contractNames is redundant --- src/modules/RGSTY/OlympusContractRegistry.sol | 36 ++++--------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/src/modules/RGSTY/OlympusContractRegistry.sol b/src/modules/RGSTY/OlympusContractRegistry.sol index 6f38c64f1..9bce53702 100644 --- a/src/modules/RGSTY/OlympusContractRegistry.sol +++ b/src/modules/RGSTY/OlympusContractRegistry.sol @@ -82,7 +82,9 @@ contract OlympusContractRegistry is RGSTYv1 { // Register the contract _immutableContracts[name_] = contractAddress_; - _updateImmutableContractNames(name_); + // Update the list of immutable contract names + // By this stage, it has been validated that an entry for the name does not already exist + _immutableContractNames.push(name_); _refreshDependents(); emit ContractRegistered(name_, contractAddress_, true); @@ -127,7 +129,9 @@ contract OlympusContractRegistry is RGSTYv1 { // Register the contract _contracts[name_] = contractAddress_; - _updateContractNames(name_); + // Update the list of contract names + // By this stage, it has been validated that an entry for the name does not already exist + _contractNames.push(name_); _refreshDependents(); emit ContractRegistered(name_, contractAddress_, false); @@ -260,34 +264,6 @@ contract OlympusContractRegistry is RGSTYv1 { if (validCharacterFound == false) revert Params_InvalidName(); } - /// @notice Updates the list of immutable contract names if the name is not already present. - /// - /// @param name_ The name of the contract - function _updateImmutableContractNames(bytes5 name_) internal { - bytes5[] memory contractNames = _immutableContractNames; - for (uint256 i; i < contractNames.length; ) { - if (contractNames[i] == name_) return; - unchecked { - ++i; - } - } - _immutableContractNames.push(name_); - } - - /// @notice Updates the list of contract names if the name is not already present. - /// - /// @param name_ The name of the contract - function _updateContractNames(bytes5 name_) internal { - bytes5[] memory contractNames = _contractNames; - for (uint256 i; i < contractNames.length; ) { - if (contractNames[i] == name_) return; - unchecked { - ++i; - } - } - _contractNames.push(name_); - } - /// @notice Removes the name of a contract from the list of contract names. /// /// @param name_ The name of the contract From 8748966efcf0b74c7e952b0ba72e5d4dd23332a4 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 1 Nov 2024 11:51:25 +0400 Subject: [PATCH 105/160] Add ROLES document based on PR #424 with additions --- ROLES.md | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 ROLES.md diff --git a/ROLES.md b/ROLES.md new file mode 100644 index 000000000..879b496b7 --- /dev/null +++ b/ROLES.md @@ -0,0 +1,86 @@ +# ROLES + +This document describes the roles that are used in the Olympus protocol. + +## Role Definitions + +| Role | Policy | Actions | +|------|----------|-------------| +| bondmanager_admin | BondManager | Create/close bond markets, set parameters | +| bridge_admin | CrossChainBridge | Allows configuring the CrossChainBridge | +| callback_admin | BondCallback | Administers the policy | +| callback_whitelist | BondCallback | Whitelists/blacklists tellers for callback | +| contract_registry_admin | ContractRegistryAdmin | Allows registering/deregistering contracts | +| cooler_overseer | Clearinghouse | Allows activating the Clearinghouse | +| custodian | TreasuryCustodian | Deposit/withdraw reserves and grant/revoke approvals | +| distributor_admin | Distributor | Set reward rate, bounty, and other parameters | +| emergency_restart | Emergency | Reactivates the TRSRY and/or MINTR modules | +| emergency_shutdown | Clearinghouse | Allows shutting down the protocol in an emergency | +| emergency_shutdown | Emergency | Deactivates the TRSRY and/or MINTR modules | +| heart | Operator | Call the operate() function | +| heart | ReserveMigrator | Allows migrating reserves from one reserve token to another | +| heart | YieldRepurchaseFacility | Creates a new YRF market | +| heart_admin | Heart | Allows configuring heart parameters and activation/deactivation | +| loan_consolidator_admin | LoanConsolidator | Allows configuring the LoanConsolidator | +| loop_daddy | YieldRepurchaseFacility | Activate/deactivate the functionality | +| operator_admin | Operator | Activate/deactivate the functionality | +| operator_policy | Operator | Set spreads, threshold factor, and cushion factor | +| operator_reporter | Operator | Report bond purchases | +| poly_admin | pOLY | Allows migrating pOLY terms to another contract | +| reserve_migrator_admin | ReserveMigrator | Activate/deactivate the functionality | + +## Role Allocations + +```json +{ + "0x0AE561226896dA978EaDA0Bec4a7d3CfAE04f506": [ // Current Operator contract + "callback_whitelist" + ], + "0x245cc372C84B3645Bf0Ffe6538620B04a217988B": [ // DAO MS + "operator_operate", + "operator_admin", + "callback_admin", + "price_admin", + "custodian", + "emergency_restart", + "bridge_admin", + "heart_admin", + "cooler_overseer", + "operator_policy", + "bondmanager_admin", + "loop_daddy" + ], + "0x73df08CE9dcC8d74d22F23282c4d49F13b4c795E": [ // Current BondCallback contract + "operator_reporter" + ], + "0x953EA3223d2dd3c1A91E9D6cca1bf7Af162C9c39": [ // OCG Timelock + "cooler_overseer", + "emergency_admin", + "emergency_shutdown", + "operator_admin", + "callback_admin", + "price_admin", + "custodian", + "emergency_restart", + "bridge_admin", + "heart_admin", + "operator_policy", + "loop_daddy", + "contract_registry_admin", + "loan_consolidator_admin" + ], + "0xda9fEDBcAF319Ecf8AB11fe874Fb1AbFc2181766": [ // pOly MS + "poly_admin" + ], + "0xa8A6ff2606b24F61AFA986381D8991DFcCCd2D55": [ // Emergency MS + "emergency_shutdown", + "emergency_admin" + ], + "0x39F6AA3d445e6Dd8eC232c6Bd589889A88E3034d": [ // Current Heart contract + "heart", + "operator_operate" + ] +} +``` + +The current role allocations can be determined by running the [role-viewer](https://github.com/OlympusDAO/role-viewer/) tool. From 49a26553557e08ac4afb688b6e7fb8ede231fd33 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 4 Nov 2024 10:21:32 +0400 Subject: [PATCH 106/160] Remove redundant import --- src/modules/RGSTY/OlympusContractRegistry.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/RGSTY/OlympusContractRegistry.sol b/src/modules/RGSTY/OlympusContractRegistry.sol index 9bce53702..d31ef9e59 100644 --- a/src/modules/RGSTY/OlympusContractRegistry.sol +++ b/src/modules/RGSTY/OlympusContractRegistry.sol @@ -4,8 +4,6 @@ pragma solidity 0.8.15; import {Kernel, Module, Policy, Keycode, toKeycode} from "src/Kernel.sol"; import {RGSTYv1} from "./RGSTY.v1.sol"; -import {console2} from "forge-std/console2.sol"; - /// @title Olympus Contract Registry /// @notice This module is used to track the addresses of contracts. /// It supports both immutable and mutable addresses. From fc92f015bb64977efc20829e56ea56e0e6993122 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 4 Nov 2024 10:21:46 +0400 Subject: [PATCH 107/160] Remove redundant code in valid character check --- src/modules/RGSTY/OlympusContractRegistry.sol | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/modules/RGSTY/OlympusContractRegistry.sol b/src/modules/RGSTY/OlympusContractRegistry.sol index d31ef9e59..3d9926690 100644 --- a/src/modules/RGSTY/OlympusContractRegistry.sol +++ b/src/modules/RGSTY/OlympusContractRegistry.sol @@ -223,8 +223,6 @@ contract OlympusContractRegistry is RGSTYv1 { /// - Null characters are found in the start or middle of the name /// - The name contains punctuation or uppercase letters function _validateContractName(bytes5 name_) internal pure { - bool validCharacterFound = false; - // Check that the contract name is lowercase letters and numerals only for (uint256 i = 0; i < 5; i++) { bytes1 char = name_[i]; @@ -239,27 +237,18 @@ contract OlympusContractRegistry is RGSTYv1 { return; } - // Before finding valid characters, we should not find a null character - // This prevents names like "\x00\x00ohm" from being registered, which could be visually indistinguishable from "ohm" - if (char == 0x00 && validCharacterFound == false) revert Params_InvalidName(); - // 0-9 if (char >= 0x30 && char <= 0x39) { - validCharacterFound = true; continue; } // a-z if (char >= 0x61 && char <= 0x7A) { - validCharacterFound = true; continue; } revert Params_InvalidName(); } - - // Catch-all - if (validCharacterFound == false) revert Params_InvalidName(); } /// @notice Removes the name of a contract from the list of contract names. From b4951b83144c4520924f7d9c61afd6cc39822641 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 4 Nov 2024 10:25:24 +0400 Subject: [PATCH 108/160] Remove redundant condition --- src/policies/LoanConsolidator.sol | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/policies/LoanConsolidator.sol b/src/policies/LoanConsolidator.sol index 59c0d08b2..885f9daf4 100644 --- a/src/policies/LoanConsolidator.sol +++ b/src/policies/LoanConsolidator.sol @@ -458,16 +458,11 @@ contract LoanConsolidator is IERC3156FlashBorrower, Policy, RolesConsumer, Reent ); // Transfer the collateral from the cooler owner to this contract - // consolidatedLoanCollateral > returned collateral if clearinghouseFrom has a higher LTC than clearinghouseTo - // consolidatedLoanCollateral > returned collateral due to rounding - // consolidatedLoanCollateral < returned collateral if clearinghouseFrom has a lower LTC than clearinghouseTo - if (consolidatedLoanCollateral > GOHM.balanceOf(address(this))) { - GOHM.transferFrom( - flashLoanData.coolerFrom.owner(), - address(this), - consolidatedLoanCollateral - GOHM.balanceOf(address(this)) - ); - } + GOHM.transferFrom( + flashLoanData.coolerFrom.owner(), + address(this), + consolidatedLoanCollateral + ); // State: // - reserveFrom: no change // - reserveTo: no change From c51e354e5a4ec52d1e0ff8463b3a5248cb1c497f Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 6 Nov 2024 14:19:11 +0400 Subject: [PATCH 109/160] Fixes for MS batch script and OCG proposal --- src/proposals/ContractRegistryProposal.sol | 16 ++++++++-------- src/proposals/addresses.json | 5 +++++ .../ops/batches/ContractRegistryInstall.sol | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/proposals/ContractRegistryProposal.sol b/src/proposals/ContractRegistryProposal.sol index f23f38f65..81a09c92d 100644 --- a/src/proposals/ContractRegistryProposal.sol +++ b/src/proposals/ContractRegistryProposal.sol @@ -95,7 +95,7 @@ contract ContractRegistryProposal is GovernorBravoProposal { contractRegistryAdmin, abi.encodeWithSelector( ContractRegistryAdmin.registerImmutableContract.selector, - "dai", + bytes5("dai"), DAI ), "Register immutable DAI address" @@ -104,7 +104,7 @@ contract ContractRegistryProposal is GovernorBravoProposal { contractRegistryAdmin, abi.encodeWithSelector( ContractRegistryAdmin.registerImmutableContract.selector, - "sdai", + bytes5("sdai"), SDAI ), "Register immutable SDAI address" @@ -113,7 +113,7 @@ contract ContractRegistryProposal is GovernorBravoProposal { contractRegistryAdmin, abi.encodeWithSelector( ContractRegistryAdmin.registerImmutableContract.selector, - "usds", + bytes5("usds"), USDS ), "Register immutable USDS address" @@ -122,7 +122,7 @@ contract ContractRegistryProposal is GovernorBravoProposal { contractRegistryAdmin, abi.encodeWithSelector( ContractRegistryAdmin.registerImmutableContract.selector, - "susds", + bytes5("susds"), SUSDS ), "Register immutable SUSDS address" @@ -131,7 +131,7 @@ contract ContractRegistryProposal is GovernorBravoProposal { contractRegistryAdmin, abi.encodeWithSelector( ContractRegistryAdmin.registerImmutableContract.selector, - "gohm", + bytes5("gohm"), GOHM ), "Register immutable GOHM address" @@ -140,7 +140,7 @@ contract ContractRegistryProposal is GovernorBravoProposal { contractRegistryAdmin, abi.encodeWithSelector( ContractRegistryAdmin.registerImmutableContract.selector, - "ohm", + bytes5("ohm"), OHM ), "Register immutable OHM address" @@ -151,7 +151,7 @@ contract ContractRegistryProposal is GovernorBravoProposal { contractRegistryAdmin, abi.encodeWithSelector( ContractRegistryAdmin.registerContract.selector, - "flash", + bytes5("flash"), FLASH_LENDER ), "Register mutable Flash Lender address" @@ -160,7 +160,7 @@ contract ContractRegistryProposal is GovernorBravoProposal { contractRegistryAdmin, abi.encodeWithSelector( ContractRegistryAdmin.registerContract.selector, - "dmgtr", + bytes5("dmgtr"), DAI_USDS_MIGRATOR ), "Register mutable DAI-USDS Migrator address" diff --git a/src/proposals/addresses.json b/src/proposals/addresses.json index 4c7a9ee53..59be53e51 100644 --- a/src/proposals/addresses.json +++ b/src/proposals/addresses.json @@ -86,6 +86,11 @@ }, { "addr": "0x0000000000000000000000000000000000000000", + "name": "olympus-module-rgsty", + "chainId": 1 + }, + { + "addr": "0x986b99579BEc7B990331474b66CcDB94Fa2419F5", "name": "olympus-policy-contract-registry-admin", "chainId": 1 } diff --git a/src/scripts/ops/batches/ContractRegistryInstall.sol b/src/scripts/ops/batches/ContractRegistryInstall.sol index b58a12c21..331e4f6c8 100644 --- a/src/scripts/ops/batches/ContractRegistryInstall.sol +++ b/src/scripts/ops/batches/ContractRegistryInstall.sol @@ -19,7 +19,7 @@ contract ContractRegistryInstall is OlyBatch { // Load contract addresses from the environment file kernel = envAddress("current", "olympus.Kernel"); rolesAdmin = envAddress("current", "olympus.policies.RolesAdmin"); - rgsty = envAddress("current", "olympus.modules.RGSTY"); + rgsty = envAddress("current", "olympus.modules.OlympusContractRegistry"); contractRegistryAdmin = envAddress("current", "olympus.policies.ContractRegistryAdmin"); } From 37a23ade2bb6a95b0410439dce565ffd818157f3 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 6 Nov 2024 14:19:32 +0400 Subject: [PATCH 110/160] Scripts for OCG proposal submission --- src/proposals/ContractRegistryProposal.sol | 17 +++- src/scripts/ops/batch.sh | 0 src/scripts/proposals/README.md | 23 ++++++ src/scripts/proposals/delegate.sh | 58 ++++++++++++++ src/scripts/proposals/submitProposal.sh | 90 ++++++++++++++++++++++ 5 files changed, 187 insertions(+), 1 deletion(-) mode change 100644 => 100755 src/scripts/ops/batch.sh create mode 100644 src/scripts/proposals/README.md create mode 100755 src/scripts/proposals/delegate.sh create mode 100755 src/scripts/proposals/submitProposal.sh diff --git a/src/proposals/ContractRegistryProposal.sol b/src/proposals/ContractRegistryProposal.sol index 81a09c92d..cc154e906 100644 --- a/src/proposals/ContractRegistryProposal.sol +++ b/src/proposals/ContractRegistryProposal.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.15; import {console2} from "forge-std/console2.sol"; import {ScriptSuite} from "proposal-sim/script/ScriptSuite.s.sol"; +import {Address} from "proposal-sim/utils/Address.sol"; // OCG Proposal Simulator import {Addresses} from "proposal-sim/addresses/Addresses.sol"; @@ -217,6 +218,8 @@ contract ContractRegistryProposal is GovernorBravoProposal { // Use this as a template to create your own script // `forge script script/GovernorBravo.s.sol:GovernorBravoScript -vvvv --rpc-url {rpc} --broadcast --verify --etherscan-api-key {key}` contract ContractRegistryProposalScript is ScriptSuite { + using Address for address; + string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; constructor() ScriptSuite(ADDRESSES_PATH, new ContractRegistryProposal()) {} @@ -229,6 +232,18 @@ contract ContractRegistryProposalScript is ScriptSuite { proposal.run(addresses, address(0)); // get the calldata for the proposal, doing so in debug mode prints it to the console - proposal.getCalldata(); + bytes memory proposalCalldata = proposal.getCalldata(); + + address governor = addresses.getAddress("olympus-governor"); + + // Register the proposal + console2.log("\n\n"); + console2.log("Submitting proposal..."); + vm.startBroadcast(); + console2.log("Proposer: ", msg.sender); + bytes memory proposalReturnData = address(payable(governor)).functionCall(proposalCalldata); + vm.stopBroadcast(); + uint256 proposalId = abi.decode(proposalReturnData, (uint256)); + console2.log("Proposal ID:", proposalId); } } diff --git a/src/scripts/ops/batch.sh b/src/scripts/ops/batch.sh old mode 100644 new mode 100755 diff --git a/src/scripts/proposals/README.md b/src/scripts/proposals/README.md new file mode 100644 index 000000000..8fc6ecb82 --- /dev/null +++ b/src/scripts/proposals/README.md @@ -0,0 +1,23 @@ +# OCG Proposals + +This directory contains scripts for submitting proposals to the Olympus Governor. + +## Fork Testing + +It is possible to test proposal submission (and execution) on a forked chain. To do so, follow these steps: + +1. Create a fork of the chain you wish to test on. Tenderly will be used in the examples. +2. Create an environment file (e.g., `.env.tenderly`) and set the `RPC_URL` environment variable to your fork's RPC URL. +3. Configure a wallet with `cast wallet` +4. Fund your chosen wallet with gOHM + - On Tenderly, this can be done using the "Fund Account" button in the dashboard. +5. Delegate your gOHM voting power to your wallet address. + - This can be done by running `./delegate.sh` with the appropriate arguments, or through the Tenderly dashboard. +6. Submit your proposal by running `./submitProposal.sh` with the appropriate arguments. + +## Mainnet + +1. Configure a wallet with `cast wallet` +2. Delegate your gOHM voting power to your wallet address. + - This can be done by running `./delegate.sh` with the appropriate arguments, or through the Tenderly dashboard. +3. Submit your proposal by running `./submitProposal.sh` with the appropriate arguments. diff --git a/src/scripts/proposals/delegate.sh b/src/scripts/proposals/delegate.sh new file mode 100755 index 000000000..dc5f96eee --- /dev/null +++ b/src/scripts/proposals/delegate.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# This script delegates voting power to a delegate. +# +# Usage: src/scripts/proposals/delegate.sh --account --delegate --env +# +# Environment variables: +# RPC_URL + +# Exit if any error occurs +set -e + +# Iterate through named arguments +# Source: https://unix.stackexchange.com/a/388038 +while [ $# -gt 0 ]; do + if [[ $1 == *"--"* ]]; then + v="${1/--/}" + declare $v="$2" + fi + + shift +done + +# Get the name of the .env file or use the default +ENV_FILE=${env:-".env"} +echo "Sourcing environment variables from $ENV_FILE" + +# Load environment file +set -a # Automatically export all variables +source $ENV_FILE +set +a # Disable automatic export + +# Check if the RPC_URL was specified +if [ -z "$RPC_URL" ]; then + echo "Error: RPC_URL was not specified" + exit 1 +fi + +# Check if the forge account was specified +if [ -z "$account" ]; then + echo "Error: Forge account was not specified. Set up using 'cast wallet'." + exit 1 +fi + +# Check if the delegate was specified +if [ -z "$delegate" ]; then + echo "Error: Delegate was not specified" + exit 1 +fi + +echo "Using RPC at URL: $RPC_URL" +echo "Using forge account: $account" +echo "Delegating to delegate: $delegate" + +# Run the delegate command +# TODO this doesn't seem to work due to an error with cast call: +# > Error: invalid type: found string "mainnet", expected u64 +cast call 0x0ab87046fbb341d058f17cbc4c1133f25a20a52f "delegate(address)()" $delegate --chain mainnet --rpc-url $RPC_URL --account $account diff --git a/src/scripts/proposals/submitProposal.sh b/src/scripts/proposals/submitProposal.sh new file mode 100755 index 000000000..67437336b --- /dev/null +++ b/src/scripts/proposals/submitProposal.sh @@ -0,0 +1,90 @@ +#!/bin/bash + +# This script submits a proposal to the governor. +# +# Usage: src/scripts/proposals/submitProposal.sh --file --contract --account --fork --broadcast --env +# +# Environment variables: +# RPC_URL + +# Exit if any error occurs +set -e + +# Iterate through named arguments +# Source: https://unix.stackexchange.com/a/388038 +while [ $# -gt 0 ]; do + if [[ $1 == *"--"* ]]; then + v="${1/--/}" + declare $v="$2" + fi + + shift +done + +# Get the name of the .env file or use the default +ENV_FILE=${env:-".env"} +echo "Sourcing environment variables from $ENV_FILE" + +# Load environment file +set -a # Automatically export all variables +source $ENV_FILE +set +a # Disable automatic export + +# Apply defaults to command-line arguments +FORK=${fork:-false} +BROADCAST=${broadcast:-false} + +# Check if the proposal file was specified +if [ -z "$file" ]; then + echo "Error: Proposal file was not specified" + exit 1 +fi + +# Check if the proposal file exists +if [ ! -f "$file" ]; then + echo "Error: Proposal file does not exist. Provide the correct relative path after the --file flag." + exit 1 +fi + +# Check if the contract name was specified +if [ -z "$contract" ]; then + echo "Error: Contract name was not specified" + exit 1 +fi + +# Check if the RPC_URL was specified +if [ -z "$RPC_URL" ]; then + echo "Error: RPC_URL was not specified" + exit 1 +fi + +# Check if the forge account was specified +if [ -z "$account" ]; then + echo "Error: Forge account was not specified. Set up using 'cast wallet'." + exit 1 +fi + +echo "Using proposal contract: $file:$contract" +echo "Using RPC at URL: $RPC_URL" +echo "Using forge account: $account" + +# Set the fork flag +FORK_FLAG="" +if [ "$FORK" = "true" ]; then + FORK_FLAG="--legacy" + echo "Fork: enabled" +else + echo "Fork: disabled" +fi + +# Set the broadcast flag +BROADCAST_FLAG="" +if [ "$BROADCAST" = "true" ]; then + BROADCAST_FLAG="--broadcast" + echo "Broadcast: enabled" +else + echo "Broadcast: disabled" +fi + +# Run the forge script +forge script $file:$contract -vvv --rpc-url $RPC_URL --account $account $FORK_FLAG $BROADCAST_FLAG From 54efe63cae25d42799590a9b385e8d242f5e6dbb Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 6 Nov 2024 14:34:16 +0400 Subject: [PATCH 111/160] Note on batch scripts on forks --- src/scripts/ops/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/scripts/ops/README.md diff --git a/src/scripts/ops/README.md b/src/scripts/ops/README.md new file mode 100644 index 000000000..c70698380 --- /dev/null +++ b/src/scripts/ops/README.md @@ -0,0 +1,9 @@ +# Batch Scripts + +This directory contains batch scripts for the Olympus DAO multisig. + +## Fork Testing + +There is no way to directly run the scripts (in their current state) on a forked chain. + +If you are using Tenderly, you can run each step of the script manually through the "Send Transaction" feature on the dashboard. From a1bf723e379bbfcf1a763ea01ae65007347eafe6 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 7 Nov 2024 13:47:38 +0400 Subject: [PATCH 112/160] WIP script to execute OCG proposals on a testnet --- src/proposals/ContractRegistryProposal.sol | 85 ++++++++++++++++++++ src/scripts/proposals/executeOnTestnet.sh | 92 ++++++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100755 src/scripts/proposals/executeOnTestnet.sh diff --git a/src/proposals/ContractRegistryProposal.sol b/src/proposals/ContractRegistryProposal.sol index cc154e906..470c215ae 100644 --- a/src/proposals/ContractRegistryProposal.sol +++ b/src/proposals/ContractRegistryProposal.sol @@ -246,4 +246,89 @@ contract ContractRegistryProposalScript is ScriptSuite { uint256 proposalId = abi.decode(proposalReturnData, (uint256)); console2.log("Proposal ID:", proposalId); } + + function executeOnTestnet() public { + console2.log("Building proposal..."); + // set debug mode to true and run it to build the actions list + proposal.setDebug(true); + + // run the proposal to build it + proposal.run(addresses, address(0)); + + console2.log("Preparing transactions"); + // Get the timelock address + address timelock = addresses.getAddress("olympus-timelock"); + + // Get the testnet RPC URL and access key + string memory TENDERLY_ACCOUNT_SLUG = vm.envString("TENDERLY_ACCOUNT_SLUG"); + string memory TENDERLY_PROJECT_SLUG = vm.envString("TENDERLY_PROJECT_SLUG"); + string memory TENDERLY_VNET_ID = vm.envString("TENDERLY_VNET_ID"); + string memory TENDERLY_ACCESS_KEY = vm.envString("TENDERLY_ACCESS_KEY"); + + // Iterate over the proposal actions and execute them + (address[] memory targets, , bytes[] memory arguments) = proposal.getProposalActions(); + for (uint256 i; i < targets.length; i++) { + console2.log("Preparing proposal action ", i + 1); + + // Construct the API call + string[] memory inputs = new string[](14); + inputs[0] = "curl"; + inputs[1] = "--request"; + inputs[2] = "POST"; + inputs[3] = "--url"; + inputs[4] = string.concat( + "https://api.tenderly.co/api/v1/account/", + TENDERLY_ACCOUNT_SLUG, + "/project/", + TENDERLY_PROJECT_SLUG, + "/vnets/", + TENDERLY_VNET_ID, + "/transactions" + ); + inputs[5] = "--header"; + inputs[6] = "'Accept: application/json'"; + inputs[7] = "--header"; + inputs[8] = "'Content-Type: application/json'"; + inputs[9] = "--header"; + inputs[10] = string.concat("'X-Access-Key: ", TENDERLY_ACCESS_KEY, "'"); + inputs[11] = "--data"; + inputs[12] = string.concat( + "'{", + '"callArgs": {', + '"from": "', + vm.toString(timelock), + '", "to": "', + vm.toString(targets[i]), + '", "gas": "0x7a1200", "gasPrice": "0x10", "value": "0x0", ', + '"data": "', + vm.toString(arguments[i]), + '"', + "}}'" + ); + inputs[13] = "--silent"; + + console2.log(inputs[3]); + console2.log(inputs[4]); + console2.log(inputs[9]); + console2.log(inputs[10]); + + // Print the command + string memory command = ""; + for (uint256 j; j < inputs.length; j++) { + command = string.concat(command, inputs[j], " "); + } + console2.log("Command: ", command); + + // Execute the API call + console2.log("Executing proposal action ", i + 1); + bytes memory response = vm.ffi(inputs); + string memory responseString = string(response); + console2.log("Response: ", responseString); + + // If the response contains "error", exit + if (vm.keyExists(responseString, ".error")) { + revert("Error executing proposal action"); + } + } + } } diff --git a/src/scripts/proposals/executeOnTestnet.sh b/src/scripts/proposals/executeOnTestnet.sh new file mode 100755 index 000000000..532038c6f --- /dev/null +++ b/src/scripts/proposals/executeOnTestnet.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +# This script executes a proposal's actions on a testnet. +# +# Usage: src/scripts/proposals/executeOnTestnet.sh --file --contract --env +# +# Environment variables: +# TENDERLY_ACCOUNT_SLUG +# TENDERLY_PROJECT_SLUG +# TENDERLY_VNET_ID +# TENDERLY_ACCESS_KEY +# RPC_URL + +# Exit if any error occurs +set -e + +# Iterate through named arguments +# Source: https://unix.stackexchange.com/a/388038 +while [ $# -gt 0 ]; do + if [[ $1 == *"--"* ]]; then + v="${1/--/}" + declare $v="$2" + fi + + shift +done + +# Get the name of the .env file or use the default +ENV_FILE=${env:-".env"} +echo "Sourcing environment variables from $ENV_FILE" + +# Load environment file +set -a # Automatically export all variables +source $ENV_FILE +set +a # Disable automatic export + +# Check if the proposal file was specified +if [ -z "$file" ]; then + echo "Error: Proposal file was not specified" + exit 1 +fi + +# Check if the proposal file exists +if [ ! -f "$file" ]; then + echo "Error: Proposal file does not exist. Provide the correct relative path after the --file flag." + exit 1 +fi + +# Check if the contract name was specified +if [ -z "$contract" ]; then + echo "Error: Contract name was not specified" + exit 1 +fi + +# Check if the TENDERLY_ACCOUNT_SLUG was specified +if [ -z "$TENDERLY_ACCOUNT_SLUG" ]; then + echo "Error: TENDERLY_ACCOUNT_SLUG was not specified" + exit 1 +fi + +# Check if the TENDERLY_PROJECT_SLUG was specified +if [ -z "$TENDERLY_PROJECT_SLUG" ]; then + echo "Error: TENDERLY_PROJECT_SLUG was not specified" + exit 1 +fi + +# Check if the TENDERLY_VNET_ID was specified +if [ -z "$TENDERLY_VNET_ID" ]; then + echo "Error: TENDERLY_VNET_ID was not specified" + exit 1 +fi + +# Check if the TENDERLY_ACCESS_KEY was specified +if [ -z "$TENDERLY_ACCESS_KEY" ]; then + echo "Error: TENDERLY_ACCESS_KEY was not specified" + exit 1 +fi + +# Check if the RPC_URL was specified +if [ -z "$RPC_URL" ]; then + echo "Error: RPC_URL was not specified" + exit 1 +fi + +echo "Using proposal contract: $file:$contract" +echo "Using TENDERLY_ACCOUNT_SLUG: $TENDERLY_ACCOUNT_SLUG" +echo "Using TENDERLY_PROJECT_SLUG: $TENDERLY_PROJECT_SLUG" +echo "Using TENDERLY_VNET_ID: $TENDERLY_VNET_ID" +echo "Using RPC_URL: $RPC_URL" + +# Run the forge script +TENDERLY_ACCOUNT_SLUG=$TENDERLY_ACCOUNT_SLUG TENDERLY_PROJECT_SLUG=$TENDERLY_PROJECT_SLUG TENDERLY_VNET_ID=$TENDERLY_VNET_ID TENDERLY_ACCESS_KEY=$TENDERLY_ACCESS_KEY forge script $file:$contract --sig "executeOnTestnet()" --rpc-url $RPC_URL From d7f2849f4be123a9bfbcd0adeecb8d1e34efe2d1 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 7 Nov 2024 13:55:19 +0400 Subject: [PATCH 113/160] Working proposal execution on Tenderly fork --- .gitignore | 2 + foundry.toml | 6 ++- remappings.txt | 3 +- soldeer.lock | 5 +++ src/proposals/ContractRegistryProposal.sol | 51 ++++++++-------------- 5 files changed, 31 insertions(+), 36 deletions(-) create mode 100644 soldeer.lock diff --git a/.gitignore b/.gitignore index 4295dcff0..ade1046bc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ cache/ node_modules/ .env +.env.* remappings.txt broadcast/ src/test/sim/in/*.json @@ -12,3 +13,4 @@ src/test/sim/sims/*.t.sol docs/ coverage/ /solidity-metrics.html +dependencies/ diff --git a/foundry.toml b/foundry.toml index 1d4739545..3b261ea93 100644 --- a/foundry.toml +++ b/foundry.toml @@ -26,6 +26,10 @@ solc_version = "0.8.15" optimizer = true optimizer_runs = 10 chain_id = 1 +libs = ["lib", "dependencies"] [fuzz] -runs = 256 \ No newline at end of file +runs = 256 + +[dependencies] +surl = { version = "1.0.0", git = "git@github.com:memester-xyz/surl.git", rev = "034c912ae9b5e707a5afd21f145b452ad8e800df" } diff --git a/remappings.txt b/remappings.txt index f59795f46..7798f10fa 100644 --- a/remappings.txt +++ b/remappings.txt @@ -12,4 +12,5 @@ clones/=lib/clones-with-immutable-args/src/ proposal-sim=lib/forge-proposal-simulator/ proposal-sim/proposals/=lib/forge-proposal-simulator/proposals/ proposals/=src/proposals/ -openzeppelin/=lib/openzeppelin-contracts/contracts \ No newline at end of file +openzeppelin/=lib/openzeppelin-contracts/contracts +surl-1.0.0/=dependencies/surl-1.0.0/src diff --git a/soldeer.lock b/soldeer.lock new file mode 100644 index 000000000..a4dc55398 --- /dev/null +++ b/soldeer.lock @@ -0,0 +1,5 @@ +[[dependencies]] +name = "surl" +version = "1.0.0" +git = "git@github.com:memester-xyz/surl.git" +rev = "034c912ae9b5e707a5afd21f145b452ad8e800df" diff --git a/src/proposals/ContractRegistryProposal.sol b/src/proposals/ContractRegistryProposal.sol index 470c215ae..744c22fbf 100644 --- a/src/proposals/ContractRegistryProposal.sol +++ b/src/proposals/ContractRegistryProposal.sol @@ -18,6 +18,8 @@ import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelega import {ContractRegistryAdmin} from "src/policies/ContractRegistryAdmin.sol"; import {RGSTYv1} from "src/modules/RGSTY/RGSTY.v1.sol"; +import {Surl} from "surl-1.0.0/Surl.sol"; + /// @notice Activates the contract registry module and associated configuration policy. contract ContractRegistryProposal is GovernorBravoProposal { Kernel internal _kernel; @@ -219,6 +221,7 @@ contract ContractRegistryProposal is GovernorBravoProposal { // `forge script script/GovernorBravo.s.sol:GovernorBravoScript -vvvv --rpc-url {rpc} --broadcast --verify --etherscan-api-key {key}` contract ContractRegistryProposalScript is ScriptSuite { using Address for address; + using Surl for *; string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; @@ -271,12 +274,12 @@ contract ContractRegistryProposalScript is ScriptSuite { console2.log("Preparing proposal action ", i + 1); // Construct the API call - string[] memory inputs = new string[](14); - inputs[0] = "curl"; - inputs[1] = "--request"; - inputs[2] = "POST"; - inputs[3] = "--url"; - inputs[4] = string.concat( + string[] memory headers = new string[](3); + headers[0] = "Accept: application/json"; + headers[1] = "Content-Type: application/json"; + headers[2] = string.concat("X-Access-Key: ", TENDERLY_ACCESS_KEY); + + string memory url = string.concat( "https://api.tenderly.co/api/v1/account/", TENDERLY_ACCOUNT_SLUG, "/project/", @@ -285,15 +288,11 @@ contract ContractRegistryProposalScript is ScriptSuite { TENDERLY_VNET_ID, "/transactions" ); - inputs[5] = "--header"; - inputs[6] = "'Accept: application/json'"; - inputs[7] = "--header"; - inputs[8] = "'Content-Type: application/json'"; - inputs[9] = "--header"; - inputs[10] = string.concat("'X-Access-Key: ", TENDERLY_ACCESS_KEY, "'"); - inputs[11] = "--data"; - inputs[12] = string.concat( - "'{", + + // Execute the API call + console2.log("Executing proposal action ", i + 1); + (uint256 status, bytes memory response) = url.post(headers, string.concat( + "{", '"callArgs": {', '"from": "', vm.toString(timelock), @@ -303,30 +302,14 @@ contract ContractRegistryProposalScript is ScriptSuite { '"data": "', vm.toString(arguments[i]), '"', - "}}'" - ); - inputs[13] = "--silent"; + "}}" + )); - console2.log(inputs[3]); - console2.log(inputs[4]); - console2.log(inputs[9]); - console2.log(inputs[10]); - - // Print the command - string memory command = ""; - for (uint256 j; j < inputs.length; j++) { - command = string.concat(command, inputs[j], " "); - } - console2.log("Command: ", command); - - // Execute the API call - console2.log("Executing proposal action ", i + 1); - bytes memory response = vm.ffi(inputs); string memory responseString = string(response); console2.log("Response: ", responseString); // If the response contains "error", exit - if (vm.keyExists(responseString, ".error")) { + if (status >= 300 || vm.keyExists(responseString, ".error")) { revert("Error executing proposal action"); } } From f93a185a66ba2f67275b1af35568b4a2688d670e Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 7 Nov 2024 14:04:16 +0400 Subject: [PATCH 114/160] Fix for linting --- .markdownlintignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.markdownlintignore b/.markdownlintignore index 18fc81f2a..80422dc14 100644 --- a/.markdownlintignore +++ b/.markdownlintignore @@ -4,3 +4,4 @@ docs/ cache/ coverage/ lib/ +dependencies/ From 81f452de0e26d45b3a3d9d5b90ee3d038ea88b04 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 7 Nov 2024 14:04:36 +0400 Subject: [PATCH 115/160] Extract proposal submission and execution code into ProposalScript abstract contract --- src/proposals/ContractRegistryProposal.sol | 110 +------------------- src/proposals/ProposalScript.sol | 114 +++++++++++++++++++++ 2 files changed, 118 insertions(+), 106 deletions(-) create mode 100644 src/proposals/ProposalScript.sol diff --git a/src/proposals/ContractRegistryProposal.sol b/src/proposals/ContractRegistryProposal.sol index 744c22fbf..5c764b93e 100644 --- a/src/proposals/ContractRegistryProposal.sol +++ b/src/proposals/ContractRegistryProposal.sol @@ -1,8 +1,5 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.15; -import {console2} from "forge-std/console2.sol"; -import {ScriptSuite} from "proposal-sim/script/ScriptSuite.s.sol"; -import {Address} from "proposal-sim/utils/Address.sol"; // OCG Proposal Simulator import {Addresses} from "proposal-sim/addresses/Addresses.sol"; @@ -18,7 +15,8 @@ import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelega import {ContractRegistryAdmin} from "src/policies/ContractRegistryAdmin.sol"; import {RGSTYv1} from "src/modules/RGSTY/RGSTY.v1.sol"; -import {Surl} from "surl-1.0.0/Surl.sol"; +// Script +import {ProposalScript} from "./ProposalScript.sol"; /// @notice Activates the contract registry module and associated configuration policy. contract ContractRegistryProposal is GovernorBravoProposal { @@ -212,106 +210,6 @@ contract ContractRegistryProposal is GovernorBravoProposal { } } -// @notice GovernorBravoScript is a script that runs BRAVO_01 proposal. -// BRAVO_01 proposal deploys a Vault contract and an ERC20 token contract -// Then the proposal transfers ownership of both Vault and ERC20 to the timelock address -// Finally the proposal whitelist the ERC20 token in the Vault contract -// @dev Use this script to simulates or run a single proposal -// Use this as a template to create your own script -// `forge script script/GovernorBravo.s.sol:GovernorBravoScript -vvvv --rpc-url {rpc} --broadcast --verify --etherscan-api-key {key}` -contract ContractRegistryProposalScript is ScriptSuite { - using Address for address; - using Surl for *; - - string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; - - constructor() ScriptSuite(ADDRESSES_PATH, new ContractRegistryProposal()) {} - - function run() public override { - // set debug mode to true and run it to build the actions list - proposal.setDebug(true); - - // run the proposal to build it - proposal.run(addresses, address(0)); - - // get the calldata for the proposal, doing so in debug mode prints it to the console - bytes memory proposalCalldata = proposal.getCalldata(); - - address governor = addresses.getAddress("olympus-governor"); - - // Register the proposal - console2.log("\n\n"); - console2.log("Submitting proposal..."); - vm.startBroadcast(); - console2.log("Proposer: ", msg.sender); - bytes memory proposalReturnData = address(payable(governor)).functionCall(proposalCalldata); - vm.stopBroadcast(); - uint256 proposalId = abi.decode(proposalReturnData, (uint256)); - console2.log("Proposal ID:", proposalId); - } - - function executeOnTestnet() public { - console2.log("Building proposal..."); - // set debug mode to true and run it to build the actions list - proposal.setDebug(true); - - // run the proposal to build it - proposal.run(addresses, address(0)); - - console2.log("Preparing transactions"); - // Get the timelock address - address timelock = addresses.getAddress("olympus-timelock"); - - // Get the testnet RPC URL and access key - string memory TENDERLY_ACCOUNT_SLUG = vm.envString("TENDERLY_ACCOUNT_SLUG"); - string memory TENDERLY_PROJECT_SLUG = vm.envString("TENDERLY_PROJECT_SLUG"); - string memory TENDERLY_VNET_ID = vm.envString("TENDERLY_VNET_ID"); - string memory TENDERLY_ACCESS_KEY = vm.envString("TENDERLY_ACCESS_KEY"); - - // Iterate over the proposal actions and execute them - (address[] memory targets, , bytes[] memory arguments) = proposal.getProposalActions(); - for (uint256 i; i < targets.length; i++) { - console2.log("Preparing proposal action ", i + 1); - - // Construct the API call - string[] memory headers = new string[](3); - headers[0] = "Accept: application/json"; - headers[1] = "Content-Type: application/json"; - headers[2] = string.concat("X-Access-Key: ", TENDERLY_ACCESS_KEY); - - string memory url = string.concat( - "https://api.tenderly.co/api/v1/account/", - TENDERLY_ACCOUNT_SLUG, - "/project/", - TENDERLY_PROJECT_SLUG, - "/vnets/", - TENDERLY_VNET_ID, - "/transactions" - ); - - // Execute the API call - console2.log("Executing proposal action ", i + 1); - (uint256 status, bytes memory response) = url.post(headers, string.concat( - "{", - '"callArgs": {', - '"from": "', - vm.toString(timelock), - '", "to": "', - vm.toString(targets[i]), - '", "gas": "0x7a1200", "gasPrice": "0x10", "value": "0x0", ', - '"data": "', - vm.toString(arguments[i]), - '"', - "}}" - )); - - string memory responseString = string(response); - console2.log("Response: ", responseString); - - // If the response contains "error", exit - if (status >= 300 || vm.keyExists(responseString, ".error")) { - revert("Error executing proposal action"); - } - } - } +contract ContractRegistryProposalScript is ProposalScript { + constructor() ProposalScript(new ContractRegistryProposal()) {} } diff --git a/src/proposals/ProposalScript.sol b/src/proposals/ProposalScript.sol new file mode 100644 index 000000000..e04c5b68d --- /dev/null +++ b/src/proposals/ProposalScript.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +import {ScriptSuite} from "proposal-sim/script/ScriptSuite.s.sol"; +import {Address} from "proposal-sim/utils/Address.sol"; +import {Surl} from "surl-1.0.0/Surl.sol"; +import {console2} from "forge-std/console2.sol"; +import {IProposal} from "proposal-sim/proposals/IProposal.sol"; + +/// @notice Allows submission and testing of OCG proposals +/// @dev Inheriting contracts must implement the constructor +/// +/// See the scripts in `src/scripts/proposals/` +abstract contract ProposalScript is ScriptSuite { + using Address for address; + using Surl for *; + + string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; + + constructor(IProposal _proposal) ScriptSuite(ADDRESSES_PATH, _proposal) {} + + function run() public override { + // set debug mode to true and run it to build the actions list + proposal.setDebug(true); + + // run the proposal to build it + proposal.run(addresses, address(0)); + + // get the calldata for the proposal, doing so in debug mode prints it to the console + bytes memory proposalCalldata = proposal.getCalldata(); + + address governor = addresses.getAddress("olympus-governor"); + + // Register the proposal + console2.log("\n\n"); + console2.log("Submitting proposal..."); + vm.startBroadcast(); + console2.log("Proposer: ", msg.sender); + bytes memory proposalReturnData = address(payable(governor)).functionCall(proposalCalldata); + vm.stopBroadcast(); + uint256 proposalId = abi.decode(proposalReturnData, (uint256)); + console2.log("Proposal ID:", proposalId); + } + + function executeOnTestnet() public { + console2.log("Building proposal..."); + // set debug mode to true and run it to build the actions list + proposal.setDebug(true); + + // run the proposal to build it + proposal.run(addresses, address(0)); + + console2.log("Preparing transactions"); + // Get the timelock address + address timelock = addresses.getAddress("olympus-timelock"); + + // Get the testnet RPC URL and access key + string memory TENDERLY_ACCOUNT_SLUG = vm.envString("TENDERLY_ACCOUNT_SLUG"); + string memory TENDERLY_PROJECT_SLUG = vm.envString("TENDERLY_PROJECT_SLUG"); + string memory TENDERLY_VNET_ID = vm.envString("TENDERLY_VNET_ID"); + string memory TENDERLY_ACCESS_KEY = vm.envString("TENDERLY_ACCESS_KEY"); + + // Iterate over the proposal actions and execute them + (address[] memory targets, , bytes[] memory arguments) = proposal.getProposalActions(); + for (uint256 i; i < targets.length; i++) { + console2.log("Preparing proposal action ", i + 1); + + // Construct the API call + string[] memory headers = new string[](3); + headers[0] = "Accept: application/json"; + headers[1] = "Content-Type: application/json"; + headers[2] = string.concat("X-Access-Key: ", TENDERLY_ACCESS_KEY); + + string memory url = string.concat( + "https://api.tenderly.co/api/v1/account/", + TENDERLY_ACCOUNT_SLUG, + "/project/", + TENDERLY_PROJECT_SLUG, + "/vnets/", + TENDERLY_VNET_ID, + "/transactions" + ); + + // Execute the API call + // solhint-disable quotes + console2.log("Executing proposal action ", i + 1); + (uint256 status, bytes memory response) = url.post( + headers, + string.concat( + "{", + '"callArgs": {', + '"from": "', + vm.toString(timelock), + '", "to": "', + vm.toString(targets[i]), + '", "gas": "0x7a1200", "gasPrice": "0x10", "value": "0x0", ', + '"data": "', + vm.toString(arguments[i]), + '"', + "}}" + ) + ); + // solhint-enable quotes + + string memory responseString = string(response); + console2.log("Response: ", responseString); + + // If the response contains "error", exit + if (status >= 300 || vm.keyExists(responseString, ".error")) { + revert("Error executing proposal action"); + } + } + } +} From 093c8b41968c355c16f5628c5e460f5d653451fa Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 7 Nov 2024 14:10:42 +0400 Subject: [PATCH 116/160] Documentation --- src/scripts/proposals/README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/scripts/proposals/README.md b/src/scripts/proposals/README.md index 8fc6ecb82..9256843a5 100644 --- a/src/scripts/proposals/README.md +++ b/src/scripts/proposals/README.md @@ -2,18 +2,28 @@ This directory contains scripts for submitting proposals to the Olympus Governor. +## Setup + +The OCG proposal must have a separate contract that inherits from `ProposalScript`. See the `ContractRegistryProposal` for an example. + ## Fork Testing It is possible to test proposal submission (and execution) on a forked chain. To do so, follow these steps: 1. Create a fork of the chain you wish to test on. Tenderly will be used in the examples. -2. Create an environment file (e.g., `.env.tenderly`) and set the `RPC_URL` environment variable to your fork's RPC URL. +2. Create an environment file (e.g., `.env.tenderly`) and set the environment variables. + - `RPC_URL`: Your fork's RPC URL. + - `TENDERLY_ACCOUNT_SLUG`: Your Tenderly account slug. + - `TENDERLY_PROJECT_SLUG`: Your Tenderly project slug. + - `TENDERLY_VNET_ID`: Your Tenderly vNet ID. This is the random string in the URL of the testnet in the Tenderly dashboard. It is NOT the same as the random string in the `RPC_URL`. e.g. `https://dashboard.tenderly.co/{TENDERLY_ACCOUNT_SLUG}/{TENDERLY_PROJECT_SLUG}/testnet/{TENDERLY_VNET_ID}` + - `TENDERLY_ACCESS_KEY`: Your Tenderly access key. 3. Configure a wallet with `cast wallet` 4. Fund your chosen wallet with gOHM - On Tenderly, this can be done using the "Fund Account" button in the dashboard. 5. Delegate your gOHM voting power to your wallet address. - This can be done by running `./delegate.sh` with the appropriate arguments, or through the Tenderly dashboard. 6. Submit your proposal by running `./submitProposal.sh` with the appropriate arguments. +7. Alternatively, you can execute the proposal (as if the proposal has passed) by running `./executeOnTestnet.sh` with the appropriate arguments. ## Mainnet From f9b9d5d3df9bb6a9d9461e3ff55fd3276bcaf4f3 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 7 Nov 2024 14:14:16 +0400 Subject: [PATCH 117/160] Zero out address --- src/proposals/addresses.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proposals/addresses.json b/src/proposals/addresses.json index 59be53e51..0476d4433 100644 --- a/src/proposals/addresses.json +++ b/src/proposals/addresses.json @@ -90,7 +90,7 @@ "chainId": 1 }, { - "addr": "0x986b99579BEc7B990331474b66CcDB94Fa2419F5", + "addr": "0x0000000000000000000000000000000000000000", "name": "olympus-policy-contract-registry-admin", "chainId": 1 } From 6074d41987f292a5720dd4f4a38430ed463edc5a Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 7 Nov 2024 14:27:34 +0400 Subject: [PATCH 118/160] Add named parameters, validation and a custom env file to the deployment script --- shell/deploy.sh | 72 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/shell/deploy.sh b/shell/deploy.sh index 66c43a811..e0c267856 100755 --- a/shell/deploy.sh +++ b/shell/deploy.sh @@ -1,38 +1,68 @@ #!/bin/bash +# Deploys a sequence of contracts. +# # Usage: -# ./deploy.sh +# ./deploy.sh --sequence --broadcast --verify --resume --env +# +# Environment variables: +# RPC_URL +# PRIVATE_KEY +# GAS_PRICE +# ETHERSCAN_KEY (only needed if verify is true) +# VERIFIER_URL (only needed for a custom verifier or on a fork) -# Load environment variables, but respect overrides -curenv=$(declare -p -x) -source .env -eval "$curenv" +# Exit if any error occurs +set -e + +# Iterate through named arguments +# Source: https://unix.stackexchange.com/a/388038 +while [ $# -gt 0 ]; do + if [[ $1 == *"--"* ]]; then + v="${1/--/}" + declare $v="$2" + fi + + shift +done + +# Get the name of the .env file or use the default +ENV_FILE=${env:-".env"} +echo "Sourcing environment variables from $ENV_FILE" + +# Load environment file +set -a # Automatically export all variables +source $ENV_FILE +set +a # Disable automatic export # Get command-line arguments -DEPLOY_FILE=$1 -BROADCAST=${2:-false} -VERIFY=${3:-false} -RESUME=${4:-false} +BROADCAST=${broadcast:-false} +VERIFY=${verify:-false} +RESUME=${resume:-false} + +# Check if sequence is set +if [ -z "$sequence" ] +then + echo "No deployment sequence specified. Provide the relative path after the --sequence flag." + exit 1 +fi -# Check if DEPLOY_FILE is set -if [ -z "$DEPLOY_FILE" ] +# Check if the sequence file exists +if [ ! -f "$sequence" ] then - echo "No deploy file specified. Provide the relative path after the command." + echo "Deployment sequence ($sequence) not found. Provide the correct relative path after the --sequence flag." exit 1 fi -# Check if DEPLOY_FILE exists -if [ ! -f "$DEPLOY_FILE" ] +# Check if CHAIN is set +if [ -z "$CHAIN" ] then - echo "Deploy file ($DEPLOY_FILE) not found. Provide the correct relative path after the command." + echo "No chain specified. Specify the CHAIN in the $ENV_FILE file." exit 1 fi -echo "Deploying $DEPLOY_FILE" +echo "Deployment sequence: $sequence" echo "Chain: $CHAIN" -echo "Guardian: $GUARDIAN_ADDRESS" -echo "Policy: $POLICY_ADDRESS" -echo "Emergency: $EMERGENCY_ADDRESS" echo "Using RPC at URL: $RPC_URL" # Set BROADCAST_FLAG based on BROADCAST @@ -51,7 +81,7 @@ if [ "$VERIFY" = "true" ] || [ "$VERIFY" = "TRUE" ]; then # Check if ETHERSCAN_KEY is set if [ -z "$ETHERSCAN_KEY" ] then - echo "No Etherscan API key found. Provide the key in .env or disable verification." + echo "No Etherscan API key found. Provide the key in $ENV_FILE or disable verification." exit 1 fi @@ -79,7 +109,7 @@ fi # Deploy using script forge script ./src/scripts/deploy/DeployV2.sol:OlympusDeploy \ ---sig "deploy(string,string)()" $CHAIN $DEPLOY_FILE \ +--sig "deploy(string,string)()" $CHAIN $sequence \ --rpc-url $RPC_URL --private-key $PRIVATE_KEY --slow -vvv \ --with-gas-price $GAS_PRICE \ $BROADCAST_FLAG \ From 2409a43c8912324f8325238cd688d9c678e93e75 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 7 Nov 2024 15:04:06 +0400 Subject: [PATCH 119/160] Correct HTTP status code --- src/proposals/ProposalScript.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proposals/ProposalScript.sol b/src/proposals/ProposalScript.sol index e04c5b68d..41ba5c1fc 100644 --- a/src/proposals/ProposalScript.sol +++ b/src/proposals/ProposalScript.sol @@ -106,7 +106,7 @@ abstract contract ProposalScript is ScriptSuite { console2.log("Response: ", responseString); // If the response contains "error", exit - if (status >= 300 || vm.keyExists(responseString, ".error")) { + if (status >= 400 || vm.keyExists(responseString, ".error")) { revert("Error executing proposal action"); } } From ef4c0eeabcd6c839fd428fe26a8895907cead9aa Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 7 Nov 2024 15:15:17 +0400 Subject: [PATCH 120/160] Correct comment --- shell/deploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/deploy.sh b/shell/deploy.sh index e0c267856..1fb2e84d9 100755 --- a/shell/deploy.sh +++ b/shell/deploy.sh @@ -35,7 +35,7 @@ set -a # Automatically export all variables source $ENV_FILE set +a # Disable automatic export -# Get command-line arguments +# Set sane defaults BROADCAST=${broadcast:-false} VERIFY=${verify:-false} RESUME=${resume:-false} From 30d6360576fe11e99c10b73d9982afd4b8388665 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 7 Nov 2024 15:25:45 +0400 Subject: [PATCH 121/160] Modify scripts to support MS batch execution on testnets --- src/scripts/ops/OlyBatch.sol | 73 +++++++++++++++++++++++++++ src/scripts/ops/README.md | 8 ++- src/scripts/ops/batch.sh | 76 +++++++++++++++++++++++------ src/scripts/ops/lib/BatchScript.sol | 8 ++- 4 files changed, 147 insertions(+), 18 deletions(-) diff --git a/src/scripts/ops/OlyBatch.sol b/src/scripts/ops/OlyBatch.sol index 03a4dac19..6eee13ba0 100644 --- a/src/scripts/ops/OlyBatch.sol +++ b/src/scripts/ops/OlyBatch.sol @@ -3,9 +3,12 @@ pragma solidity 0.8.15; import {BatchScript} from "./lib/BatchScript.sol"; import {stdJson} from "forge-std/StdJson.sol"; +import {console2} from "forge-std/console2.sol"; +import {Surl} from "surl-1.0.0/Surl.sol"; abstract contract OlyBatch is BatchScript { using stdJson for string; + using Surl for *; string internal env; string internal chain; @@ -81,4 +84,74 @@ abstract contract OlyBatch is BatchScript { } function loadEnv() internal virtual; + + function executeBatch(bool send_) internal override { + bool isTestnet = vm.envOr("TESTNET", false); + + if (isTestnet && send_) { + console2.log("Sending batch on testnet"); + _sendTestnetBatch(); + return; + } + + super.executeBatch(send_); + } + + function _sendTestnetBatch() private { + // Get the testnet RPC URL and access key + string memory TENDERLY_ACCOUNT_SLUG = vm.envString("TENDERLY_ACCOUNT_SLUG"); + string memory TENDERLY_PROJECT_SLUG = vm.envString("TENDERLY_PROJECT_SLUG"); + string memory TENDERLY_VNET_ID = vm.envString("TENDERLY_VNET_ID"); + string memory TENDERLY_ACCESS_KEY = vm.envString("TENDERLY_ACCESS_KEY"); + + // Iterate over the proposal actions and execute them + for (uint256 i; i < actionsTo.length; i++) { + console2.log("Preparing proposal action ", i + 1); + + // Construct the API call + string[] memory headers = new string[](3); + headers[0] = "Accept: application/json"; + headers[1] = "Content-Type: application/json"; + headers[2] = string.concat("X-Access-Key: ", TENDERLY_ACCESS_KEY); + + string memory url = string.concat( + "https://api.tenderly.co/api/v1/account/", + TENDERLY_ACCOUNT_SLUG, + "/project/", + TENDERLY_PROJECT_SLUG, + "/vnets/", + TENDERLY_VNET_ID, + "/transactions" + ); + + // Execute the API call + // solhint-disable quotes + console2.log("Executing proposal action ", i + 1); + (uint256 status, bytes memory response) = url.post( + headers, + string.concat( + "{", + '"callArgs": {', + '"from": "', + vm.toString(daoMS), + '", "to": "', + vm.toString(actionsTo[i]), + '", "gas": "0x7a1200", "gasPrice": "0x10", "value": "0x0", ', + '"data": "', + vm.toString(actionsData[i]), + '"', + "}}" + ) + ); + // solhint-enable quotes + + string memory responseString = string(response); + console2.log("Response: ", responseString); + + // If the response contains "error", exit + if (status >= 400 || vm.keyExists(responseString, ".error")) { + revert("Error executing proposal action"); + } + } + } } diff --git a/src/scripts/ops/README.md b/src/scripts/ops/README.md index c70698380..5ff88abc3 100644 --- a/src/scripts/ops/README.md +++ b/src/scripts/ops/README.md @@ -4,6 +4,10 @@ This directory contains batch scripts for the Olympus DAO multisig. ## Fork Testing -There is no way to directly run the scripts (in their current state) on a forked chain. +To run the scripts on a testnet/forked chain, provide the `--testnet` flag to the `batch.sh` script. This requires certain environment variables to be set, which are documented in the `batch.sh` file. -If you are using Tenderly, you can run each step of the script manually through the "Send Transaction" feature on the dashboard. +For example: + +```bash +./batch.sh --contract ContractRegistryInstall --batch script1_install --broadcast true --testnet true --env .env.testnet +``` diff --git a/src/scripts/ops/batch.sh b/src/scripts/ops/batch.sh index 575a4cb7d..10da14903 100755 --- a/src/scripts/ops/batch.sh +++ b/src/scripts/ops/batch.sh @@ -1,26 +1,72 @@ #!/bin/bash -# Arguments required for this script -# 1. String: File/Contract Name (should be the same) -# 2. String: Batch Name -# 3. Bool: Whether to send to STS or not (if false, just simulates the batch) +# Run a multisig batch +# +# Usage: +# ./batch.sh --contract --batch --broadcast --testnet --env +# +# Environment variables: +# RPC_URL +# SIGNER_ADDRESS +# TESTNET -# Load environment variables, but respect overrides -curenv=$(declare -p -x) -source .env -eval "$curenv" +# Exit if any error occurs +set -e -CONTRACT_NAME=$1 -BATCH_NAME=$2 -BROADCAST=$3 +# Iterate through named arguments +# Source: https://unix.stackexchange.com/a/388038 +while [ $# -gt 0 ]; do + if [[ $1 == *"--"* ]]; then + v="${1/--/}" + declare $v="$2" + fi -echo "Contract name: $CONTRACT_NAME" + shift +done -echo "Batch name: $BATCH_NAME" +# Get the name of the .env file or use the default +ENV_FILE=${env:-".env"} +echo "Sourcing environment variables from $ENV_FILE" -echo "Broadcasting: $BROADCAST" +# Load environment file +set -a # Automatically export all variables +source $ENV_FILE +set +a # Disable automatic export + +# Set sane defaults +BROADCAST=${broadcast:-false} +TESTNET=${testnet:-false} + +# Check if contract is set +if [ -z "$contract" ]; then + echo "No contract name provided. Provide the contract name after the --contract flag." + exit 1 +fi + +# Check if batch is set +if [ -z "$batch" ]; then + echo "No batch name provided. Provide the batch name after the --batch flag." + exit 1 +fi +# Check if RPC_URL is set +if [ -z "$RPC_URL" ]; then + echo "No RPC URL provided. Specify the RPC_URL in the $ENV_FILE file." + exit 1 +fi + +# Check if SIGNER_ADDRESS is set +if [ -z "$SIGNER_ADDRESS" ]; then + echo "No signer address provided. Specify the SIGNER_ADDRESS in the $ENV_FILE file." + exit 1 +fi + +echo "Contract name: $contract" +echo "Batch name: $batch" echo "Using RPC at URL: $RPC_URL" +echo "Using signer address: $SIGNER_ADDRESS" +echo "Broadcasting: $BROADCAST" +echo "Using testnet: $TESTNET" # Execute the batch -forge script ./src/scripts/ops/batches/$CONTRACT_NAME.sol:$CONTRACT_NAME --sig "$BATCH_NAME(bool)()" $BROADCAST --slow -vvv --sender $SIGNER_ADDRESS --rpc-url $RPC_URL \ No newline at end of file +TESTNET=$TESTNET forge script ./src/scripts/ops/batches/$contract.sol:$contract --sig "$batch(bool)()" $BROADCAST --slow -vvv --sender $SIGNER_ADDRESS --rpc-url $RPC_URL diff --git a/src/scripts/ops/lib/BatchScript.sol b/src/scripts/ops/lib/BatchScript.sol index 8e2c08571..baa7db2f8 100644 --- a/src/scripts/ops/lib/BatchScript.sol +++ b/src/scripts/ops/lib/BatchScript.sol @@ -69,6 +69,9 @@ abstract contract BatchScript is Script { // Address to send transaction from address internal safe; + address[] internal actionsTo; + bytes[] internal actionsData; + enum Operation { CALL, DELEGATECALL @@ -128,6 +131,9 @@ abstract contract BatchScript is Script { vm.prank(safe); (bool success, bytes memory data) = to_.call(data_); if (success) { + actionsTo.push(to_); + actionsData.push(data_); + return data; } else { revert(string(data)); @@ -136,7 +142,7 @@ abstract contract BatchScript is Script { // Simulate then send the batch to the Safe API. If `send_` is `false`, the // batch will only be simulated. - function executeBatch(bool send_) internal { + function executeBatch(bool send_) internal virtual { _initialize(); Batch memory batch = _createBatch(safe); // _simulateBatch(safe, batch); From 731cba80707275fc173a452dc97080fe3c33139f Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 7 Nov 2024 15:34:58 +0400 Subject: [PATCH 122/160] Fix issues with soldeer --- foundry.toml | 4 +++- shell/full_install.sh | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/foundry.toml b/foundry.toml index 3b261ea93..894c70a89 100644 --- a/foundry.toml +++ b/foundry.toml @@ -26,10 +26,12 @@ solc_version = "0.8.15" optimizer = true optimizer_runs = 10 chain_id = 1 -libs = ["lib", "dependencies"] [fuzz] runs = 256 +[soldeer] +remappings_generate = false + [dependencies] surl = { version = "1.0.0", git = "git@github.com:memester-xyz/surl.git", rev = "034c912ae9b5e707a5afd21f145b452ad8e800df" } diff --git a/shell/full_install.sh b/shell/full_install.sh index 14157f330..5661e53fa 100755 --- a/shell/full_install.sh +++ b/shell/full_install.sh @@ -24,5 +24,8 @@ cd lib/solidity-examples/ && git checkout a4954e5747baca5e7fd2b62c639e7600ad388a cd lib/solmate/ && git checkout fadb2e2778adbf01c80275bfb99e5c14969d964b && cd ../.. cd lib/forge-proposal-simulator && git checkout 864b357b650f9dc7b2fb1ae23562454815d51def && cd ../.. +echo "*** Running forge soldeer update" +forge soldeer update + echo "*** Running forge build" forge build From 99e8c7b1bd5d55cbcd948163cb1a2e4530ac0a6d Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 7 Nov 2024 15:39:25 +0400 Subject: [PATCH 123/160] Correct build script --- .github/workflows/CI.yml | 2 +- .github/workflows/OCG.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 4d3579f4e..8c367466d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -29,7 +29,7 @@ jobs: uses: foundry-rs/foundry-toolchain@v1 - name: Install Foundry dependencies - run: forge install + run: pnpm run build - name: Run lint check run: pnpm run lint:check diff --git a/.github/workflows/OCG.yml b/.github/workflows/OCG.yml index ee219d15b..090021ebe 100644 --- a/.github/workflows/OCG.yml +++ b/.github/workflows/OCG.yml @@ -29,7 +29,7 @@ jobs: uses: foundry-rs/foundry-toolchain@v1 - name: Install Foundry dependencies - run: forge install + run: pnpm run build - name: Run proposal simulation tests run: pnpm run test:proposal From 3e0d1e5ed997d65e127fbc4197579bf5ebbca6a0 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 7 Nov 2024 15:43:09 +0400 Subject: [PATCH 124/160] Use https for soldeer dependencies --- foundry.toml | 2 +- soldeer.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/foundry.toml b/foundry.toml index 894c70a89..df3eb2bef 100644 --- a/foundry.toml +++ b/foundry.toml @@ -34,4 +34,4 @@ runs = 256 remappings_generate = false [dependencies] -surl = { version = "1.0.0", git = "git@github.com:memester-xyz/surl.git", rev = "034c912ae9b5e707a5afd21f145b452ad8e800df" } +surl = { version = "1.0.0", git = "https://github.com/memester-xyz/surl.git", rev = "034c912ae9b5e707a5afd21f145b452ad8e800df" } diff --git a/soldeer.lock b/soldeer.lock index a4dc55398..a25fbe0e0 100644 --- a/soldeer.lock +++ b/soldeer.lock @@ -1,5 +1,5 @@ [[dependencies]] name = "surl" version = "1.0.0" -git = "git@github.com:memester-xyz/surl.git" +git = "https://github.com/memester-xyz/surl.git" rev = "034c912ae9b5e707a5afd21f145b452ad8e800df" From 99338ebd0c7f1856b0f0d8080594e38afadd6f4a Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 8 Nov 2024 11:53:28 +0400 Subject: [PATCH 125/160] Update logs --- src/scripts/ops/OlyBatch.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/scripts/ops/OlyBatch.sol b/src/scripts/ops/OlyBatch.sol index 6eee13ba0..9f6cf1859 100644 --- a/src/scripts/ops/OlyBatch.sol +++ b/src/scripts/ops/OlyBatch.sol @@ -106,7 +106,7 @@ abstract contract OlyBatch is BatchScript { // Iterate over the proposal actions and execute them for (uint256 i; i < actionsTo.length; i++) { - console2.log("Preparing proposal action ", i + 1); + console2.log("Preparing batch action ", i + 1); // Construct the API call string[] memory headers = new string[](3); @@ -126,7 +126,7 @@ abstract contract OlyBatch is BatchScript { // Execute the API call // solhint-disable quotes - console2.log("Executing proposal action ", i + 1); + console2.log("Executing batch action ", i + 1); (uint256 status, bytes memory response) = url.post( headers, string.concat( @@ -150,7 +150,7 @@ abstract contract OlyBatch is BatchScript { // If the response contains "error", exit if (status >= 400 || vm.keyExists(responseString, ".error")) { - revert("Error executing proposal action"); + revert("Error executing batch action"); } } } From 925a06fd126d1c710bed7b6f5c3ac20e15c7ef99 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 8 Nov 2024 11:53:41 +0400 Subject: [PATCH 126/160] Update LoanConsolidatorProposal to use ProposalScript --- src/proposals/LoanConsolidatorProposal.sol | 29 ++++------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/src/proposals/LoanConsolidatorProposal.sol b/src/proposals/LoanConsolidatorProposal.sol index b50b7d221..802949894 100644 --- a/src/proposals/LoanConsolidatorProposal.sol +++ b/src/proposals/LoanConsolidatorProposal.sol @@ -1,7 +1,5 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.15; -import {console2} from "forge-std/console2.sol"; -import {ScriptSuite} from "proposal-sim/script/ScriptSuite.s.sol"; // OCG Proposal Simulator import {Addresses} from "proposal-sim/addresses/Addresses.sol"; @@ -16,6 +14,9 @@ import {RolesAdmin} from "src/policies/RolesAdmin.sol"; import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; +// Script +import {ProposalScript} from "./ProposalScript.sol"; + /// @notice Activates an updated LoanConsolidator policy. contract LoanConsolidatorProposal is GovernorBravoProposal { Kernel internal _kernel; @@ -137,26 +138,6 @@ contract LoanConsolidatorProposal is GovernorBravoProposal { } } -// @notice GovernorBravoScript is a script that runs BRAVO_01 proposal. -// BRAVO_01 proposal deploys a Vault contract and an ERC20 token contract -// Then the proposal transfers ownership of both Vault and ERC20 to the timelock address -// Finally the proposal whitelist the ERC20 token in the Vault contract -// @dev Use this script to simulates or run a single proposal -// Use this as a template to create your own script -// `forge script script/GovernorBravo.s.sol:GovernorBravoScript -vvvv --rpc-url {rpc} --broadcast --verify --etherscan-api-key {key}` -contract LoanConsolidatorProposalScript is ScriptSuite { - string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; - - constructor() ScriptSuite(ADDRESSES_PATH, new LoanConsolidatorProposal()) {} - - function run() public override { - // set debug mode to true and run it to build the actions list - proposal.setDebug(true); - - // run the proposal to build it - proposal.run(addresses, address(0)); - - // get the calldata for the proposal, doing so in debug mode prints it to the console - proposal.getCalldata(); - } +contract LoanConsolidatorProposalScript is ProposalScript { + constructor() ProposalScript(new LoanConsolidatorProposal()) {} } From 7599f27adf8637687b8f8b1542f0a44bafcb808b Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 12 Nov 2024 16:24:26 +0400 Subject: [PATCH 127/160] Fix issues with remappings --- foundry.toml | 2 +- remappings.txt | 22 +++++++++++++---- src/scripts/deploy/DeployV2.sol | 8 +++---- src/scripts/deploy/LegacyBurnerDeps.s.sol | 2 +- src/test/Kernel.t.sol | 2 +- src/test/external/ClaimTransfer.t.sol | 2 +- src/test/external/cooler/Cooler.t.sol | 6 ++--- src/test/external/cooler/CoolerFactory.t.sol | 4 ++-- .../governance/GovernorBravoDelegate.t.sol | 4 ++-- src/test/modules/BLREG.t.sol | 2 +- src/test/modules/CHREG.t.sol | 2 +- src/test/modules/INSTR.t.sol | 12 +++++----- src/test/modules/MINTR.t.sol | 10 ++++---- src/test/modules/PRICE.t.sol | 6 ++--- src/test/modules/RANGE.t.sol | 4 ++-- src/test/modules/RGSTY.t.sol | 4 ++-- src/test/modules/ROLES.t.sol | 4 ++-- src/test/modules/TRSRY.t.sol | 6 ++--- src/test/modules/VOTES.t.sol | 4 ++-- src/test/policies/BondCallback.t.sol | 12 +++++----- src/test/policies/BondManager.t.sol | 12 +++++----- .../BoostedLiquidity/BLVaultLidoFork.t.sol | 6 ++--- .../BoostedLiquidity/BLVaultLidoMocks.t.sol | 12 +++++----- .../BoostedLiquidity/BLVaultLusdFork.t.sol | 6 ++--- .../BoostedLiquidity/BLVaultLusdMocks.t.sol | 12 +++++----- .../BLVaultManagerLidoFork.t.sol | 6 ++--- .../BLVaultManagerLidoMocks.t.sol | 12 +++++----- .../BLVaultManagerLusdFork.t.sol | 6 ++--- .../BLVaultManagerLusdMocks.t.sol | 12 +++++----- src/test/policies/Burner.t.sol | 4 ++-- src/test/policies/Clearinghouse.t.sol | 6 ++--- src/test/policies/CrossChainBridge.t.sol | 4 ++-- src/test/policies/CrossChainBridgeFork.t.sol | 6 ++--- src/test/policies/Distributor.t.sol | 2 +- src/test/policies/Emergency.t.sol | 8 +++---- src/test/policies/Heart.t.sol | 8 +++---- src/test/policies/LegacyBurner.t.sol | 6 ++--- src/test/policies/Minter.t.sol | 4 ++-- src/test/policies/Operator.t.sol | 12 +++++----- src/test/policies/Parthenon.t.sol | 4 ++-- src/test/policies/PriceConfig.t.sol | 4 ++-- src/test/policies/RolesAdmin.t.sol | 2 +- src/test/policies/TreasuryCustodian.t.sol | 2 +- src/test/policies/VohmVault.t.sol | 4 ++-- .../policies/YieldRepurchaseFacility.t.sol | 14 +++++------ src/test/policies/pOLY.t.sol | 4 ++-- src/test/proposals/OIP_166.t.sol | 2 +- src/test/proposals/OIP_XXX.t.sol | 2 +- src/test/sim/RangeSim.sol | 24 +++++++++---------- src/test/sim/SimTemplate.sol.x | 2 +- 50 files changed, 170 insertions(+), 156 deletions(-) diff --git a/foundry.toml b/foundry.toml index df3eb2bef..dd8f3949a 100644 --- a/foundry.toml +++ b/foundry.toml @@ -13,8 +13,8 @@ remappings = [ 'layer-zero/=lib/solidity-examples/contracts/', '@openzeppelin/=lib/openzeppelin-contracts/', 'bonds/=lib/bonds/src/', - 'test/=src/test/', 'clones/=lib/clones-with-immutable-args/src/', + 'proposal-sim/=lib/forge-proposal-simulator/', ] fs_permissions = [ {access = "write", path = "./src/test/sim/out/"}, diff --git a/remappings.txt b/remappings.txt index 7798f10fa..5ed3e07ef 100644 --- a/remappings.txt +++ b/remappings.txt @@ -5,12 +5,26 @@ modules/=src/modules/ policies/=src/policies/ libraries/=src/libraries/ solmate/=lib/solmate/src/ +balancer-v2/=lib/balancer-v2/ layer-zero/=lib/solidity-examples/contracts/ +@openzeppelin/=lib/openzeppelin-contracts/ bonds/=lib/bonds/src/ test/=src/test/ clones/=lib/clones-with-immutable-args/src/ -proposal-sim=lib/forge-proposal-simulator/ -proposal-sim/proposals/=lib/forge-proposal-simulator/proposals/ -proposals/=src/proposals/ -openzeppelin/=lib/openzeppelin-contracts/contracts +proposal-sim/=lib/forge-proposal-simulator/ +@addresses/=lib/forge-proposal-simulator/addresses/ +@examples/=lib/forge-proposal-simulator/examples/ +@proposals/=lib/forge-proposal-simulator/proposals/ +@script/=lib/forge-proposal-simulator/script/ +@test/=lib/forge-proposal-simulator/test/ +@utils/=lib/forge-proposal-simulator/utils/ +clones-with-immutable-args/=lib/clones-with-immutable-args/src/ +comp-governance/=lib/forge-proposal-simulator/lib/compound-governance/contracts/ +compound-governance/=lib/forge-proposal-simulator/lib/compound-governance/contracts/ +erc4626-tests/=lib/forge-proposal-simulator/lib/openzeppelin-contracts/lib/erc4626-tests/ +forge-proposal-simulator/=lib/forge-proposal-simulator/ +openzeppelin-contracts/=lib/openzeppelin-contracts/ +openzeppelin/=lib/forge-proposal-simulator/lib/openzeppelin-contracts/contracts/ +solidity-code-metrics/=node_modules/solidity-code-metrics/ +solidity-examples/=lib/solidity-examples/contracts/ surl-1.0.0/=dependencies/surl-1.0.0/src diff --git a/src/scripts/deploy/DeployV2.sol b/src/scripts/deploy/DeployV2.sol index d998e4202..217c5e261 100644 --- a/src/scripts/deploy/DeployV2.sol +++ b/src/scripts/deploy/DeployV2.sol @@ -64,11 +64,11 @@ import {YieldRepurchaseFacility} from "policies/YieldRepurchaseFacility.sol"; import {OlympusContractRegistry} from "modules/RGSTY/OlympusContractRegistry.sol"; import {ContractRegistryAdmin} from "policies/ContractRegistryAdmin.sol"; -import {MockPriceFeed} from "test/mocks/MockPriceFeed.sol"; -import {MockAuraBooster, MockAuraRewardPool, MockAuraMiningLib, MockAuraVirtualRewardPool, MockAuraStashToken} from "test/mocks/AuraMocks.sol"; -import {MockBalancerPool, MockVault} from "test/mocks/BalancerMocks.sol"; +import {MockPriceFeed} from "src/test/mocks/MockPriceFeed.sol"; +import {MockAuraBooster, MockAuraRewardPool, MockAuraMiningLib, MockAuraVirtualRewardPool, MockAuraStashToken} from "src/test/mocks/AuraMocks.sol"; +import {MockBalancerPool, MockVault} from "src/test/mocks/BalancerMocks.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {Faucet} from "test/mocks/Faucet.sol"; +import {Faucet} from "src/test/mocks/Faucet.sol"; import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; import {TransferHelper} from "libraries/TransferHelper.sol"; diff --git a/src/scripts/deploy/LegacyBurnerDeps.s.sol b/src/scripts/deploy/LegacyBurnerDeps.s.sol index 4803c5811..b17be5449 100644 --- a/src/scripts/deploy/LegacyBurnerDeps.s.sol +++ b/src/scripts/deploy/LegacyBurnerDeps.s.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; import {Script, console2} from "forge-std/Script.sol"; -import {MockLegacyInverseBondDepo} from "test/mocks/MockLegacyInverseBondDepo.sol"; +import {MockLegacyInverseBondDepo} from "src/test/mocks/MockLegacyInverseBondDepo.sol"; contract LegacyBurnerDepsDeploy is Script { address public authority = 0x4A8c9502A34962a2C6d73c5D181dAaeF3dcDc88D; diff --git a/src/test/Kernel.t.sol b/src/test/Kernel.t.sol index b5bf84548..a05397e3c 100644 --- a/src/test/Kernel.t.sol +++ b/src/test/Kernel.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import "./mocks/KernelTestMocks.sol"; diff --git a/src/test/external/ClaimTransfer.t.sol b/src/test/external/ClaimTransfer.t.sol index add4e6057..4cc5183f0 100644 --- a/src/test/external/ClaimTransfer.t.sol +++ b/src/test/external/ClaimTransfer.t.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; import {stdError} from "forge-std/StdError.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; diff --git a/src/test/external/cooler/Cooler.t.sol b/src/test/external/cooler/Cooler.t.sol index 726b1417a..2dba7d846 100644 --- a/src/test/external/cooler/Cooler.t.sol +++ b/src/test/external/cooler/Cooler.t.sol @@ -3,11 +3,11 @@ pragma solidity ^0.8.15; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; -import {MockGohm} from "test/mocks/MockGohm.sol"; +import {MockGohm} from "src/test/mocks/MockGohm.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {MockLender} from "test/mocks/MockCallbackLender.sol"; +import {MockLender} from "src/test/mocks/MockCallbackLender.sol"; import {Cooler} from "src/external/cooler/Cooler.sol"; import {CoolerFactory} from "src/external/cooler/CoolerFactory.sol"; diff --git a/src/test/external/cooler/CoolerFactory.t.sol b/src/test/external/cooler/CoolerFactory.t.sol index 2e01dcb53..195cfd2b0 100644 --- a/src/test/external/cooler/CoolerFactory.t.sol +++ b/src/test/external/cooler/CoolerFactory.t.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.15; import {Test} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; -import {MockERC20} from "test/mocks/OlympusMocks.sol"; +import {MockERC20} from "src/test/mocks/OlympusMocks.sol"; import {CoolerFactory} from "src/external/cooler/CoolerFactory.sol"; diff --git a/src/test/external/governance/GovernorBravoDelegate.t.sol b/src/test/external/governance/GovernorBravoDelegate.t.sol index 9890a6db0..a2b0718e2 100644 --- a/src/test/external/governance/GovernorBravoDelegate.t.sol +++ b/src/test/external/governance/GovernorBravoDelegate.t.sol @@ -2,11 +2,11 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {console2} from "forge-std/console2.sol"; -import {MockGohm} from "test/mocks/OlympusMocks.sol"; +import {MockGohm} from "src/test/mocks/OlympusMocks.sol"; import {OlympusTreasury} from "modules/TRSRY/OlympusTreasury.sol"; import {OlympusRoles} from "modules/ROLES/OlympusRoles.sol"; diff --git a/src/test/modules/BLREG.t.sol b/src/test/modules/BLREG.t.sol index 960515864..53e1b318b 100644 --- a/src/test/modules/BLREG.t.sol +++ b/src/test/modules/BLREG.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; -import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; +import {ModuleTestFixtureGenerator} from "src/test/lib/ModuleTestFixtureGenerator.sol"; import "modules/BLREG/OlympusBoostedLiquidityRegistry.sol"; import "src/Kernel.sol"; diff --git a/src/test/modules/CHREG.t.sol b/src/test/modules/CHREG.t.sol index 106da267e..3e3c83a5d 100644 --- a/src/test/modules/CHREG.t.sol +++ b/src/test/modules/CHREG.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; +import {ModuleTestFixtureGenerator} from "src/test/lib/ModuleTestFixtureGenerator.sol"; import "modules/CHREG/OlympusClearinghouseRegistry.sol"; import "src/Kernel.sol"; diff --git a/src/test/modules/INSTR.t.sol b/src/test/modules/INSTR.t.sol index d65d6934b..e241d1fb2 100644 --- a/src/test/modules/INSTR.t.sol +++ b/src/test/modules/INSTR.t.sol @@ -3,14 +3,14 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {ModuleTestFixtureGenerator} from "src/test/lib/ModuleTestFixtureGenerator.sol"; import {Parthenon} from "policies/Parthenon.sol"; -import {MockModuleWriter} from "test/mocks/MockModuleWriter.sol"; -import {MockInvalidModule} from "test/mocks/MockInvalidModule.sol"; -import {MockValidModule} from "test/mocks/MockValidModule.sol"; -import {MockValidUpgradedModule} from "test/mocks/MockValidUpgradedModule.sol"; +import {MockModuleWriter} from "src/test/mocks/MockModuleWriter.sol"; +import {MockInvalidModule} from "src/test/mocks/MockInvalidModule.sol"; +import {MockValidModule} from "src/test/mocks/MockValidModule.sol"; +import {MockValidUpgradedModule} from "src/test/mocks/MockValidUpgradedModule.sol"; import "modules/INSTR/OlympusInstructions.sol"; import "src/Kernel.sol"; diff --git a/src/test/modules/MINTR.t.sol b/src/test/modules/MINTR.t.sol index 198188e61..146e850c0 100644 --- a/src/test/modules/MINTR.t.sol +++ b/src/test/modules/MINTR.t.sol @@ -3,12 +3,12 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {larping} from "test/lib/larping.sol"; -import {Quabi} from "test/lib/quabi/Quabi.sol"; -import {MockLegacyAuthority} from "test/mocks/MockLegacyAuthority.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {larping} from "src/test/lib/larping.sol"; +import {Quabi} from "src/test/lib/quabi/Quabi.sol"; +import {MockLegacyAuthority} from "src/test/mocks/MockLegacyAuthority.sol"; -import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; +import {ModuleTestFixtureGenerator} from "src/test/lib/ModuleTestFixtureGenerator.sol"; import {OlympusERC20Token, IOlympusAuthority} from "src/external/OlympusERC20.sol"; import "modules/MINTR/OlympusMinter.sol"; diff --git a/src/test/modules/PRICE.t.sol b/src/test/modules/PRICE.t.sol index c21e309c1..4f4169265 100644 --- a/src/test/modules/PRICE.t.sol +++ b/src/test/modules/PRICE.t.sol @@ -3,13 +3,13 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {ModuleTestFixtureGenerator} from "src/test/lib/ModuleTestFixtureGenerator.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {FullMath} from "libraries/FullMath.sol"; -import {MockPriceFeed} from "test/mocks/MockPriceFeed.sol"; +import {MockPriceFeed} from "src/test/mocks/MockPriceFeed.sol"; import {OlympusPrice} from "modules/PRICE/OlympusPrice.sol"; import "src/Kernel.sol"; diff --git a/src/test/modules/RANGE.t.sol b/src/test/modules/RANGE.t.sol index b9f60f55a..5be382530 100644 --- a/src/test/modules/RANGE.t.sol +++ b/src/test/modules/RANGE.t.sol @@ -3,8 +3,8 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {ModuleTestFixtureGenerator} from "src/test/lib/ModuleTestFixtureGenerator.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; diff --git a/src/test/modules/RGSTY.t.sol b/src/test/modules/RGSTY.t.sol index f4003bf47..a7308fdfe 100644 --- a/src/test/modules/RGSTY.t.sol +++ b/src/test/modules/RGSTY.t.sol @@ -2,8 +2,8 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; -import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; -import {MockContractRegistryPolicy, MockImmutableContractRegistryPolicy} from "test/mocks/MockContractRegistryPolicy.sol"; +import {ModuleTestFixtureGenerator} from "src/test/lib/ModuleTestFixtureGenerator.sol"; +import {MockContractRegistryPolicy, MockImmutableContractRegistryPolicy} from "src/test/mocks/MockContractRegistryPolicy.sol"; import {Kernel, Actions, Module, fromKeycode} from "src/Kernel.sol"; import {RGSTYv1} from "src/modules/RGSTY/RGSTY.v1.sol"; diff --git a/src/test/modules/ROLES.t.sol b/src/test/modules/ROLES.t.sol index e6c088dcc..dcab7054c 100644 --- a/src/test/modules/ROLES.t.sol +++ b/src/test/modules/ROLES.t.sol @@ -2,9 +2,9 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {console2 as console} from "forge-std/console2.sol"; -import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; +import {ModuleTestFixtureGenerator} from "src/test/lib/ModuleTestFixtureGenerator.sol"; import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; import {OlympusRoles} from "src/modules/ROLES/OlympusRoles.sol"; diff --git a/src/test/modules/TRSRY.t.sol b/src/test/modules/TRSRY.t.sol index ed101d8de..1f904d6af 100644 --- a/src/test/modules/TRSRY.t.sol +++ b/src/test/modules/TRSRY.t.sol @@ -2,13 +2,13 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {console2 as console} from "forge-std/console2.sol"; -import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; +import {ModuleTestFixtureGenerator} from "src/test/lib/ModuleTestFixtureGenerator.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {OlympusERC20Token} from "src/external/OlympusERC20.sol"; -//import {MockPolicy} from "test/mocks/KernelTestMocks.sol"; +//import {MockPolicy} from "src/test/mocks/KernelTestMocks.sol"; import {OlympusTreasury} from "src/modules/TRSRY/OlympusTreasury.sol"; import {TRSRYv1} from "src/modules/TRSRY/TRSRY.v1.sol"; diff --git a/src/test/modules/VOTES.t.sol b/src/test/modules/VOTES.t.sol index 23f29a8af..be993720a 100644 --- a/src/test/modules/VOTES.t.sol +++ b/src/test/modules/VOTES.t.sol @@ -6,12 +6,12 @@ import {ERC20} from "solmate/tokens/ERC20.sol"; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {Kernel, Actions} from "src/Kernel.sol"; import {OlympusVotes} from "src/modules/VOTES/OlympusVotes.sol"; -import "test/lib/ModuleTestFixtureGenerator.sol"; +import {ModuleTestFixtureGenerator} from "src/test/lib/ModuleTestFixtureGenerator.sol"; contract VOTESTest is Test { using ModuleTestFixtureGenerator for OlympusVotes; diff --git a/src/test/policies/BondCallback.t.sol b/src/test/policies/BondCallback.t.sol index 237124858..8a7891cbe 100644 --- a/src/test/policies/BondCallback.t.sol +++ b/src/test/policies/BondCallback.t.sol @@ -3,17 +3,17 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; -import {BondFixedTermSDA} from "test/lib/bonds/BondFixedTermSDA.sol"; -import {BondAggregator} from "test/lib/bonds/BondAggregator.sol"; -import {BondFixedTermTeller} from "test/lib/bonds/BondFixedTermTeller.sol"; -import {IBondSDA as LibIBondSDA} from "test/lib/bonds/interfaces/IBondSDA.sol"; +import {BondFixedTermSDA} from "src/test/lib/bonds/BondFixedTermSDA.sol"; +import {BondAggregator} from "src/test/lib/bonds/BondAggregator.sol"; +import {BondFixedTermTeller} from "src/test/lib/bonds/BondFixedTermTeller.sol"; +import {IBondSDA as LibIBondSDA} from "src/test/lib/bonds/interfaces/IBondSDA.sol"; import {RolesAuthority, Authority as SolmateAuthority} from "solmate/auth/authorities/RolesAuthority.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {MockERC4626, ERC4626} from "solmate/test/utils/mocks/MockERC4626.sol"; -import {MockPrice} from "test/mocks/MockPrice.sol"; +import {MockPrice} from "src/test/mocks/MockPrice.sol"; import {IBondSDA} from "interfaces/IBondSDA.sol"; import {IBondAggregator} from "interfaces/IBondAggregator.sol"; diff --git a/src/test/policies/BondManager.t.sol b/src/test/policies/BondManager.t.sol index 12cf7c878..ee71ecd54 100644 --- a/src/test/policies/BondManager.t.sol +++ b/src/test/policies/BondManager.t.sol @@ -2,8 +2,8 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {larping} from "test/lib/larping.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {larping} from "src/test/lib/larping.sol"; import {console2} from "forge-std/console2.sol"; import {OlympusERC20Token, IOlympusAuthority} from "src/external/OlympusERC20.sol"; @@ -23,10 +23,10 @@ import {BondManager} from "policies/BondManager.sol"; import {RolesAdmin} from "policies/RolesAdmin.sol"; import {RolesAuthority, Authority as SolmateAuthority} from "solmate/auth/authorities/RolesAuthority.sol"; -import {MockAggregator} from "test/mocks/MockAggregator.sol"; -import {MockFixedExpirySDA} from "test/mocks/MockFixedExpirySDA.sol"; -import {MockFixedExpiryTeller} from "test/mocks/MockFixedExpiryTeller.sol"; -import {MockEasyAuction} from "test/mocks/MockEasyAuction.sol"; +import {MockAggregator} from "src/test/mocks/MockAggregator.sol"; +import {MockFixedExpirySDA} from "src/test/mocks/MockFixedExpirySDA.sol"; +import {MockFixedExpiryTeller} from "src/test/mocks/MockFixedExpiryTeller.sol"; +import {MockEasyAuction} from "src/test/mocks/MockEasyAuction.sol"; import {IBondAggregator} from "interfaces/IBondAggregator.sol"; diff --git a/src/test/policies/BoostedLiquidity/BLVaultLidoFork.t.sol b/src/test/policies/BoostedLiquidity/BLVaultLidoFork.t.sol index 27896ede6..38462b287 100644 --- a/src/test/policies/BoostedLiquidity/BLVaultLidoFork.t.sol +++ b/src/test/policies/BoostedLiquidity/BLVaultLidoFork.t.sol @@ -2,12 +2,12 @@ pragma solidity 0.8.15; import {Test, stdError} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {larping} from "test/lib/larping.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {larping} from "src/test/lib/larping.sol"; import {FullMath} from "libraries/FullMath.sol"; -import {MockLegacyAuthority} from "test/mocks/MockLegacyAuthority.sol"; +import {MockLegacyAuthority} from "src/test/mocks/MockLegacyAuthority.sol"; import {ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {OlympusERC20Token, IOlympusAuthority} from "src/external/OlympusERC20.sol"; diff --git a/src/test/policies/BoostedLiquidity/BLVaultLidoMocks.t.sol b/src/test/policies/BoostedLiquidity/BLVaultLidoMocks.t.sol index 72eff6620..41c2829e6 100644 --- a/src/test/policies/BoostedLiquidity/BLVaultLidoMocks.t.sol +++ b/src/test/policies/BoostedLiquidity/BLVaultLidoMocks.t.sol @@ -2,16 +2,16 @@ pragma solidity 0.8.15; import {Test, stdError} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {larping} from "test/lib/larping.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {larping} from "src/test/lib/larping.sol"; import {FullMath} from "libraries/FullMath.sol"; -import {MockLegacyAuthority} from "test/mocks/MockLegacyAuthority.sol"; +import {MockLegacyAuthority} from "src/test/mocks/MockLegacyAuthority.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {MockPriceFeed} from "test/mocks/MockPriceFeed.sol"; -import {MockVault, MockBalancerPool} from "test/mocks/BalancerMocks.sol"; -import {MockAuraBooster, MockAuraRewardPool} from "test/mocks/AuraMocks.sol"; +import {MockPriceFeed} from "src/test/mocks/MockPriceFeed.sol"; +import {MockVault, MockBalancerPool} from "src/test/mocks/BalancerMocks.sol"; +import {MockAuraBooster, MockAuraRewardPool} from "src/test/mocks/AuraMocks.sol"; import {OlympusERC20Token, IOlympusAuthority} from "src/external/OlympusERC20.sol"; import {IAuraBooster, IAuraRewardPool} from "policies/BoostedLiquidity/interfaces/IAura.sol"; diff --git a/src/test/policies/BoostedLiquidity/BLVaultLusdFork.t.sol b/src/test/policies/BoostedLiquidity/BLVaultLusdFork.t.sol index b198c9dd2..d1f31c483 100644 --- a/src/test/policies/BoostedLiquidity/BLVaultLusdFork.t.sol +++ b/src/test/policies/BoostedLiquidity/BLVaultLusdFork.t.sol @@ -2,12 +2,12 @@ pragma solidity 0.8.15; import {Test, stdError} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {larping} from "test/lib/larping.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {larping} from "src/test/lib/larping.sol"; import {FullMath} from "libraries/FullMath.sol"; -import {MockLegacyAuthority} from "test/mocks/MockLegacyAuthority.sol"; +import {MockLegacyAuthority} from "src/test/mocks/MockLegacyAuthority.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {OlympusERC20Token, IOlympusAuthority} from "src/external/OlympusERC20.sol"; diff --git a/src/test/policies/BoostedLiquidity/BLVaultLusdMocks.t.sol b/src/test/policies/BoostedLiquidity/BLVaultLusdMocks.t.sol index 7b8ba5f32..b38c423ba 100644 --- a/src/test/policies/BoostedLiquidity/BLVaultLusdMocks.t.sol +++ b/src/test/policies/BoostedLiquidity/BLVaultLusdMocks.t.sol @@ -2,16 +2,16 @@ pragma solidity 0.8.15; import {Test, stdError} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {larping} from "test/lib/larping.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {larping} from "src/test/lib/larping.sol"; import {FullMath} from "libraries/FullMath.sol"; -import {MockLegacyAuthority} from "test/mocks/MockLegacyAuthority.sol"; +import {MockLegacyAuthority} from "src/test/mocks/MockLegacyAuthority.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {MockPriceFeed} from "test/mocks/MockPriceFeed.sol"; -import {MockBalancerVault, MockBalancerPool} from "test/mocks/BalancerMocks.sol"; -import {MockAuraBooster, MockAuraRewardPool, MockAuraStashToken, MockAuraVirtualRewardPool} from "test/mocks/AuraMocks.sol"; +import {MockPriceFeed} from "src/test/mocks/MockPriceFeed.sol"; +import {MockBalancerVault, MockBalancerPool} from "src/test/mocks/BalancerMocks.sol"; +import {MockAuraBooster, MockAuraRewardPool, MockAuraStashToken, MockAuraVirtualRewardPool} from "src/test/mocks/AuraMocks.sol"; import {OlympusERC20Token, IOlympusAuthority} from "src/external/OlympusERC20.sol"; import {IAuraBooster, IAuraRewardPool} from "policies/BoostedLiquidity/interfaces/IAura.sol"; diff --git a/src/test/policies/BoostedLiquidity/BLVaultManagerLidoFork.t.sol b/src/test/policies/BoostedLiquidity/BLVaultManagerLidoFork.t.sol index d4f5404bd..33cfdcca6 100644 --- a/src/test/policies/BoostedLiquidity/BLVaultManagerLidoFork.t.sol +++ b/src/test/policies/BoostedLiquidity/BLVaultManagerLidoFork.t.sol @@ -2,12 +2,12 @@ pragma solidity 0.8.15; import {Test, stdError} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {larping} from "test/lib/larping.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {larping} from "src/test/lib/larping.sol"; import {FullMath} from "libraries/FullMath.sol"; -import {MockLegacyAuthority} from "test/mocks/MockLegacyAuthority.sol"; +import {MockLegacyAuthority} from "src/test/mocks/MockLegacyAuthority.sol"; import {ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {OlympusERC20Token, IOlympusAuthority} from "src/external/OlympusERC20.sol"; diff --git a/src/test/policies/BoostedLiquidity/BLVaultManagerLidoMocks.t.sol b/src/test/policies/BoostedLiquidity/BLVaultManagerLidoMocks.t.sol index 356cde58d..5f950ba07 100644 --- a/src/test/policies/BoostedLiquidity/BLVaultManagerLidoMocks.t.sol +++ b/src/test/policies/BoostedLiquidity/BLVaultManagerLidoMocks.t.sol @@ -2,16 +2,16 @@ pragma solidity 0.8.15; import {Test, stdError} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {larping} from "test/lib/larping.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {larping} from "src/test/lib/larping.sol"; import {FullMath} from "libraries/FullMath.sol"; -import {MockLegacyAuthority} from "test/mocks/MockLegacyAuthority.sol"; +import {MockLegacyAuthority} from "src/test/mocks/MockLegacyAuthority.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {MockPriceFeed} from "test/mocks/MockPriceFeed.sol"; -import {MockVault, MockBalancerPool} from "test/mocks/BalancerMocks.sol"; -import {MockAuraBooster, MockAuraRewardPool, MockAuraMiningLib} from "test/mocks/AuraMocks.sol"; +import {MockPriceFeed} from "src/test/mocks/MockPriceFeed.sol"; +import {MockVault, MockBalancerPool} from "src/test/mocks/BalancerMocks.sol"; +import {MockAuraBooster, MockAuraRewardPool, MockAuraMiningLib} from "src/test/mocks/AuraMocks.sol"; import {OlympusERC20Token, IOlympusAuthority} from "src/external/OlympusERC20.sol"; import {IAuraBooster, IAuraRewardPool} from "policies/BoostedLiquidity/interfaces/IAura.sol"; diff --git a/src/test/policies/BoostedLiquidity/BLVaultManagerLusdFork.t.sol b/src/test/policies/BoostedLiquidity/BLVaultManagerLusdFork.t.sol index 51407682b..0f8c99b40 100644 --- a/src/test/policies/BoostedLiquidity/BLVaultManagerLusdFork.t.sol +++ b/src/test/policies/BoostedLiquidity/BLVaultManagerLusdFork.t.sol @@ -2,12 +2,12 @@ pragma solidity 0.8.15; import {Test, stdError} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {larping} from "test/lib/larping.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {larping} from "src/test/lib/larping.sol"; import {FullMath} from "libraries/FullMath.sol"; -import {MockLegacyAuthority} from "test/mocks/MockLegacyAuthority.sol"; +import {MockLegacyAuthority} from "src/test/mocks/MockLegacyAuthority.sol"; import {ERC20, MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {OlympusERC20Token, IOlympusAuthority} from "src/external/OlympusERC20.sol"; diff --git a/src/test/policies/BoostedLiquidity/BLVaultManagerLusdMocks.t.sol b/src/test/policies/BoostedLiquidity/BLVaultManagerLusdMocks.t.sol index d447d005b..d1180bb0d 100644 --- a/src/test/policies/BoostedLiquidity/BLVaultManagerLusdMocks.t.sol +++ b/src/test/policies/BoostedLiquidity/BLVaultManagerLusdMocks.t.sol @@ -2,16 +2,16 @@ pragma solidity 0.8.15; import {Test, stdError} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {larping} from "test/lib/larping.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {larping} from "src/test/lib/larping.sol"; import {FullMath} from "libraries/FullMath.sol"; -import {MockLegacyAuthority} from "test/mocks/MockLegacyAuthority.sol"; +import {MockLegacyAuthority} from "src/test/mocks/MockLegacyAuthority.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {MockPriceFeed} from "test/mocks/MockPriceFeed.sol"; -import {MockVault, MockBalancerPool} from "test/mocks/BalancerMocks.sol"; -import {MockAuraBooster, MockAuraRewardPool, MockAuraMiningLib, MockAuraStashToken} from "test/mocks/AuraMocks.sol"; +import {MockPriceFeed} from "src/test/mocks/MockPriceFeed.sol"; +import {MockVault, MockBalancerPool} from "src/test/mocks/BalancerMocks.sol"; +import {MockAuraBooster, MockAuraRewardPool, MockAuraMiningLib, MockAuraStashToken} from "src/test/mocks/AuraMocks.sol"; import {OlympusERC20Token, IOlympusAuthority} from "src/external/OlympusERC20.sol"; import {IAuraBooster, IAuraRewardPool} from "policies/BoostedLiquidity/interfaces/IAura.sol"; diff --git a/src/test/policies/Burner.t.sol b/src/test/policies/Burner.t.sol index 76d1ba421..c30b919f9 100644 --- a/src/test/policies/Burner.t.sol +++ b/src/test/policies/Burner.t.sol @@ -3,10 +3,10 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {MockOhm} from "test/mocks/MockOhm.sol"; +import {MockOhm} from "src/test/mocks/MockOhm.sol"; import "src/Kernel.sol"; import {OlympusTreasury} from "modules/TRSRY/OlympusTreasury.sol"; diff --git a/src/test/policies/Clearinghouse.t.sol b/src/test/policies/Clearinghouse.t.sol index 4b8ec93a8..def0ec9bc 100644 --- a/src/test/policies/Clearinghouse.t.sol +++ b/src/test/policies/Clearinghouse.t.sol @@ -3,10 +3,10 @@ pragma solidity ^0.8.15; import {Test} from "forge-std/Test.sol"; import {console2 as console} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; -import {MockOhm} from "test/mocks/MockOhm.sol"; -import {MockStaking} from "test/mocks/MockStaking.sol"; +import {MockOhm} from "src/test/mocks/MockOhm.sol"; +import {MockStaking} from "src/test/mocks/MockStaking.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {MockERC4626} from "solmate/test/utils/mocks/MockERC4626.sol"; diff --git a/src/test/policies/CrossChainBridge.t.sol b/src/test/policies/CrossChainBridge.t.sol index c7d341687..ccf409e48 100644 --- a/src/test/policies/CrossChainBridge.t.sol +++ b/src/test/policies/CrossChainBridge.t.sol @@ -2,12 +2,12 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {console2} from "forge-std/console2.sol"; import {Bytes32AddressLib} from "solmate/utils/Bytes32AddressLib.sol"; //import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {MockOhm} from "test/mocks/MockOhm.sol"; +import {MockOhm} from "src/test/mocks/MockOhm.sol"; import {OlympusRoles} from "modules/ROLES/OlympusRoles.sol"; import {ROLESv1} from "modules/ROLES/ROLES.v1.sol"; import {OlympusMinter} from "modules/MINTR/OlympusMinter.sol"; diff --git a/src/test/policies/CrossChainBridgeFork.t.sol b/src/test/policies/CrossChainBridgeFork.t.sol index 82bf1b690..93bba9a93 100644 --- a/src/test/policies/CrossChainBridgeFork.t.sol +++ b/src/test/policies/CrossChainBridgeFork.t.sol @@ -5,11 +5,11 @@ import {Test, Vm} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; import {Bytes32AddressLib} from "solmate/utils/Bytes32AddressLib.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {FullMath} from "libraries/FullMath.sol"; //import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {MockOhm} from "test/mocks/MockOhm.sol"; +import {MockOhm} from "src/test/mocks/MockOhm.sol"; import {OlympusRoles} from "modules/ROLES/OlympusRoles.sol"; import {ROLESv1} from "modules/ROLES/ROLES.v1.sol"; import {OlympusMinter} from "modules/MINTR/OlympusMinter.sol"; @@ -18,7 +18,7 @@ import {MINTRv1} from "modules/MINTR/MINTR.v1.sol"; import {CrossChainBridge, ILayerZeroEndpoint} from "policies/CrossChainBridge.sol"; import {RolesAdmin} from "policies/RolesAdmin.sol"; -import {LayerZeroHelper} from "test/lib/pigeon/layerzero/LayerZeroHelper.sol"; +import {LayerZeroHelper} from "src/test/lib/pigeon/layerzero/LayerZeroHelper.sol"; import "src/Kernel.sol"; diff --git a/src/test/policies/Distributor.t.sol b/src/test/policies/Distributor.t.sol index 87bf9c7c8..e92960658 100644 --- a/src/test/policies/Distributor.t.sol +++ b/src/test/policies/Distributor.t.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.15; /// External Dependencies import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; /// Import Distributor import {Distributor} from "policies/Distributor/Distributor.sol"; diff --git a/src/test/policies/Emergency.t.sol b/src/test/policies/Emergency.t.sol index 03abab8b7..36ab563e4 100644 --- a/src/test/policies/Emergency.t.sol +++ b/src/test/policies/Emergency.t.sol @@ -3,12 +3,12 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {larping} from "test/lib/larping.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {larping} from "src/test/lib/larping.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; -import {MockLegacyAuthority} from "test/mocks/MockLegacyAuthority.sol"; +import {ModuleTestFixtureGenerator} from "src/test/lib/ModuleTestFixtureGenerator.sol"; +import {MockLegacyAuthority} from "src/test/mocks/MockLegacyAuthority.sol"; import {FullMath} from "libraries/FullMath.sol"; diff --git a/src/test/policies/Heart.t.sol b/src/test/policies/Heart.t.sol index f3ea57089..d4b1e0d16 100644 --- a/src/test/policies/Heart.t.sol +++ b/src/test/policies/Heart.t.sol @@ -2,18 +2,18 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {console2} from "forge-std/console2.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {MockPrice} from "test/mocks/MockPrice.sol"; +import {MockPrice} from "src/test/mocks/MockPrice.sol"; import {OlympusMinter} from "modules/MINTR/OlympusMinter.sol"; import {OlympusRoles} from "modules/ROLES/OlympusRoles.sol"; import {ROLESv1} from "modules/ROLES/ROLES.v1.sol"; import {RolesAdmin} from "policies/RolesAdmin.sol"; import {ZeroDistributor} from "policies/Distributor/ZeroDistributor.sol"; -import {MockStakingZD} from "test/mocks/MockStakingForZD.sol"; -import {MockYieldRepo} from "test/mocks/MockYieldRepo.sol"; +import {MockStakingZD} from "src/test/mocks/MockStakingForZD.sol"; +import {MockYieldRepo} from "src/test/mocks/MockYieldRepo.sol"; import {FullMath} from "libraries/FullMath.sol"; diff --git a/src/test/policies/LegacyBurner.t.sol b/src/test/policies/LegacyBurner.t.sol index 858980c21..7ea27e58e 100644 --- a/src/test/policies/LegacyBurner.t.sol +++ b/src/test/policies/LegacyBurner.t.sol @@ -2,13 +2,13 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {console2} from "forge-std/console2.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {OlympusERC20Token} from "src/external/OlympusERC20.sol"; -import {MockLegacyAuthorityV2} from "test/mocks/MockLegacyAuthority.sol"; +import {MockLegacyAuthorityV2} from "src/test/mocks/MockLegacyAuthority.sol"; import "src/Kernel.sol"; @@ -20,7 +20,7 @@ import {RolesAdmin} from "policies/RolesAdmin.sol"; import {BondManager} from "policies/BondManager.sol"; import {LegacyBurner} from "policies/LegacyBurner.sol"; -import {MockLegacyInverseBondDepo} from "test/mocks/MockLegacyInverseBondDepo.sol"; +import {MockLegacyInverseBondDepo} from "src/test/mocks/MockLegacyInverseBondDepo.sol"; // solhint-disable-next-line max-states-count contract LegacyBurnerTest is Test { diff --git a/src/test/policies/Minter.t.sol b/src/test/policies/Minter.t.sol index e75111d47..f1506e97a 100644 --- a/src/test/policies/Minter.t.sol +++ b/src/test/policies/Minter.t.sol @@ -3,10 +3,10 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {MockOhm} from "test/mocks/MockOhm.sol"; +import {MockOhm} from "src/test/mocks/MockOhm.sol"; import "src/Kernel.sol"; import {OlympusMinter} from "modules/MINTR/OlympusMinter.sol"; diff --git a/src/test/policies/Operator.t.sol b/src/test/policies/Operator.t.sol index 2de328551..609db7de4 100644 --- a/src/test/policies/Operator.t.sol +++ b/src/test/policies/Operator.t.sol @@ -3,17 +3,17 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; -import {BondFixedTermSDA} from "test/lib/bonds/BondFixedTermSDA.sol"; -import {BondAggregator} from "test/lib/bonds/BondAggregator.sol"; -import {BondFixedTermTeller} from "test/lib/bonds/BondFixedTermTeller.sol"; +import {BondFixedTermSDA} from "src/test/lib/bonds/BondFixedTermSDA.sol"; +import {BondAggregator} from "src/test/lib/bonds/BondAggregator.sol"; +import {BondFixedTermTeller} from "src/test/lib/bonds/BondFixedTermTeller.sol"; import {RolesAuthority, Authority as SolmateAuthority} from "solmate/auth/authorities/RolesAuthority.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {MockERC4626, ERC4626} from "solmate/test/utils/mocks/MockERC4626.sol"; -import {MockPrice} from "test/mocks/MockPrice.sol"; -import {MockOhm} from "test/mocks/MockOhm.sol"; +import {MockPrice} from "src/test/mocks/MockPrice.sol"; +import {MockOhm} from "src/test/mocks/MockOhm.sol"; import {IBondSDA} from "interfaces/IBondSDA.sol"; import {IBondAggregator} from "interfaces/IBondAggregator.sol"; diff --git a/src/test/policies/Parthenon.t.sol b/src/test/policies/Parthenon.t.sol index 6460aa347..d90ba0f35 100644 --- a/src/test/policies/Parthenon.t.sol +++ b/src/test/policies/Parthenon.t.sol @@ -5,8 +5,8 @@ import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {ModuleTestFixtureGenerator} from "src/test/lib/ModuleTestFixtureGenerator.sol"; import {OlympusInstructions} from "src/modules/INSTR/OlympusInstructions.sol"; import {OlympusVotes} from "src/modules/VOTES/OlympusVotes.sol"; diff --git a/src/test/policies/PriceConfig.t.sol b/src/test/policies/PriceConfig.t.sol index 5766d62c4..4d75e2b1e 100644 --- a/src/test/policies/PriceConfig.t.sol +++ b/src/test/policies/PriceConfig.t.sol @@ -2,13 +2,13 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {console2 as console} from "forge-std/console2.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {FullMath} from "libraries/FullMath.sol"; -import {MockPriceFeed} from "test/mocks/MockPriceFeed.sol"; +import {MockPriceFeed} from "src/test/mocks/MockPriceFeed.sol"; import {OlympusPriceConfig} from "policies/PriceConfig.sol"; import {OlympusPrice} from "modules/PRICE/OlympusPrice.sol"; diff --git a/src/test/policies/RolesAdmin.t.sol b/src/test/policies/RolesAdmin.t.sol index 077c1e212..15a9cb20b 100644 --- a/src/test/policies/RolesAdmin.t.sol +++ b/src/test/policies/RolesAdmin.t.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; diff --git a/src/test/policies/TreasuryCustodian.t.sol b/src/test/policies/TreasuryCustodian.t.sol index 8ac07d06e..37a39bf90 100644 --- a/src/test/policies/TreasuryCustodian.t.sol +++ b/src/test/policies/TreasuryCustodian.t.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; diff --git a/src/test/policies/VohmVault.t.sol b/src/test/policies/VohmVault.t.sol index d8dddcaae..ddb37f210 100644 --- a/src/test/policies/VohmVault.t.sol +++ b/src/test/policies/VohmVault.t.sol @@ -6,8 +6,8 @@ import {ERC20} from "solmate/tokens/ERC20.sol"; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; -import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; +import {ModuleTestFixtureGenerator} from "src/test/lib/ModuleTestFixtureGenerator.sol"; import "src/Kernel.sol"; diff --git a/src/test/policies/YieldRepurchaseFacility.t.sol b/src/test/policies/YieldRepurchaseFacility.t.sol index feb4d748b..1a107238f 100644 --- a/src/test/policies/YieldRepurchaseFacility.t.sol +++ b/src/test/policies/YieldRepurchaseFacility.t.sol @@ -3,18 +3,18 @@ pragma solidity >=0.8.0; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; -import {BondFixedTermSDA} from "test/lib/bonds/BondFixedTermSDA.sol"; -import {BondAggregator} from "test/lib/bonds/BondAggregator.sol"; -import {BondFixedTermTeller} from "test/lib/bonds/BondFixedTermTeller.sol"; +import {BondFixedTermSDA} from "src/test/lib/bonds/BondFixedTermSDA.sol"; +import {BondAggregator} from "src/test/lib/bonds/BondAggregator.sol"; +import {BondFixedTermTeller} from "src/test/lib/bonds/BondFixedTermTeller.sol"; import {RolesAuthority, Authority as SolmateAuthority} from "solmate/auth/authorities/RolesAuthority.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {MockERC4626, ERC4626} from "solmate/test/utils/mocks/MockERC4626.sol"; -import {MockPrice} from "test/mocks/MockPrice.sol"; -import {MockOhm} from "test/mocks/MockOhm.sol"; -import {MockClearinghouse} from "test/mocks/MockClearinghouse.sol"; +import {MockPrice} from "src/test/mocks/MockPrice.sol"; +import {MockOhm} from "src/test/mocks/MockOhm.sol"; +import {MockClearinghouse} from "src/test/mocks/MockClearinghouse.sol"; import {IBondSDA} from "interfaces/IBondSDA.sol"; import {IBondAggregator} from "interfaces/IBondAggregator.sol"; diff --git a/src/test/policies/pOLY.t.sol b/src/test/policies/pOLY.t.sol index 3e38b5c9c..3f78fa9ff 100644 --- a/src/test/policies/pOLY.t.sol +++ b/src/test/policies/pOLY.t.sol @@ -3,10 +3,10 @@ pragma solidity 0.8.15; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {MockGenesisClaim} from "test/mocks/MockGenesisClaim.sol"; +import {MockGenesisClaim} from "src/test/mocks/MockGenesisClaim.sol"; import {OlympusMinter} from "modules/MINTR/OlympusMinter.sol"; import {OlympusTreasury} from "modules/TRSRY/OlympusTreasury.sol"; diff --git a/src/test/proposals/OIP_166.t.sol b/src/test/proposals/OIP_166.t.sol index 32e91debc..182d06c1a 100644 --- a/src/test/proposals/OIP_166.t.sol +++ b/src/test/proposals/OIP_166.t.sol @@ -11,7 +11,7 @@ import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelega import {Timelock} from "src/external/governance/Timelock.sol"; // OIP_166 imports -import {OIP_166} from "proposals/OIP_166.sol"; +import {OIP_166} from "src/proposals/OIP_166.sol"; /// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. /// @dev Update the `setUp` function to deploy your proposal and set the submission diff --git a/src/test/proposals/OIP_XXX.t.sol b/src/test/proposals/OIP_XXX.t.sol index a1be6d7e5..d22ec6eef 100644 --- a/src/test/proposals/OIP_XXX.t.sol +++ b/src/test/proposals/OIP_XXX.t.sol @@ -11,7 +11,7 @@ import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelega import {Timelock} from "src/external/governance/Timelock.sol"; // OIP_XXX imports -import {OIP_XXX, Clearinghouse, CHREGv1, IERC20, IERC4626} from "proposals/OIP_XXX.sol"; +import {OIP_XXX, Clearinghouse, CHREGv1, IERC20, IERC4626} from "src/proposals/OIP_XXX.sol"; /// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. /// @dev Update the `setUp` function to deploy your proposal and set the submission diff --git a/src/test/sim/RangeSim.sol b/src/test/sim/RangeSim.sol index 7b27c808a..25c883c6a 100644 --- a/src/test/sim/RangeSim.sol +++ b/src/test/sim/RangeSim.sol @@ -6,23 +6,23 @@ import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {MockERC4626} from "solmate/test/utils/mocks/MockERC4626.sol"; -import {MockOhm} from "test/mocks/MockOhm.sol"; -import {MockStakingZD} from "test/mocks/MockStakingForZD.sol"; -import {UserFactory} from "test/lib/UserFactory.sol"; +import {MockOhm} from "src/test/mocks/MockOhm.sol"; +import {MockStakingZD} from "src/test/mocks/MockStakingForZD.sol"; +import {UserFactory} from "src/test/lib/UserFactory.sol"; -import {BondFixedTermSDA} from "test/lib/bonds/BondFixedTermSDA.sol"; -import {BondAggregator} from "test/lib/bonds/BondAggregator.sol"; -import {BondFixedTermTeller} from "test/lib/bonds/BondFixedTermTeller.sol"; +import {BondFixedTermSDA} from "src/test/lib/bonds/BondFixedTermSDA.sol"; +import {BondAggregator} from "src/test/lib/bonds/BondAggregator.sol"; +import {BondFixedTermTeller} from "src/test/lib/bonds/BondFixedTermTeller.sol"; import {RolesAuthority, Authority} from "solmate/auth/authorities/RolesAuthority.sol"; import {IBondSDA} from "interfaces/IBondSDA.sol"; import {IBondAggregator} from "interfaces/IBondAggregator.sol"; -import {ZuniswapV2Factory} from "test/lib/zuniswapv2/ZuniswapV2Factory.sol"; -import {ZuniswapV2Pair} from "test/lib/zuniswapv2/ZuniswapV2Pair.sol"; -import {ZuniswapV2Library} from "test/lib/zuniswapv2/ZuniswapV2Library.sol"; -import {ZuniswapV2Router} from "test/lib/zuniswapv2/ZuniswapV2Router.sol"; -import {MathLibrary} from "test/lib/zuniswapv2/libraries/Math.sol"; +import {ZuniswapV2Factory} from "src/test/lib/zuniswapv2/ZuniswapV2Factory.sol"; +import {ZuniswapV2Pair} from "src/test/lib/zuniswapv2/ZuniswapV2Pair.sol"; +import {ZuniswapV2Library} from "src/test/lib/zuniswapv2/ZuniswapV2Library.sol"; +import {ZuniswapV2Router} from "src/test/lib/zuniswapv2/ZuniswapV2Router.sol"; +import {MathLibrary} from "src/test/lib/zuniswapv2/libraries/Math.sol"; import "src/Kernel.sol"; import {OlympusPrice} from "modules/PRICE/OlympusPrice.sol"; @@ -37,7 +37,7 @@ import {Operator} from "policies/Operator.sol"; import {OlympusHeart} from "policies/Heart.sol"; import {BondCallback} from "policies/BondCallback.sol"; import {OlympusPriceConfig} from "policies/PriceConfig.sol"; -import {MockPriceFeed} from "test/mocks/MockPriceFeed.sol"; +import {MockPriceFeed} from "src/test/mocks/MockPriceFeed.sol"; import {RolesAdmin} from "policies/RolesAdmin.sol"; import {ZeroDistributor} from "policies/Distributor/ZeroDistributor.sol"; import {YieldRepurchaseFacility} from "policies/YieldRepurchaseFacility.sol"; diff --git a/src/test/sim/SimTemplate.sol.x b/src/test/sim/SimTemplate.sol.x index 6d1b0fdea..8838773fe 100644 --- a/src/test/sim/SimTemplate.sol.x +++ b/src/test/sim/SimTemplate.sol.x @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Unlicense pragma solidity >=0.8.0; -import {RangeSim, SimIO} from "test/sim/RangeSim.sol"; +import {RangeSim, SimIO} from "src/test/sim/RangeSim.sol"; contract Seed{SEED}Test is RangeSim { From ad0efddc8f36b4c088f059414b0847f77ba152cd Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 12 Nov 2024 16:43:35 +0400 Subject: [PATCH 128/160] Add links to pull request --- src/proposals/ContractRegistryProposal.sol | 1 + src/proposals/LoanConsolidatorProposal.sol | 1 + 2 files changed, 2 insertions(+) diff --git a/src/proposals/ContractRegistryProposal.sol b/src/proposals/ContractRegistryProposal.sol index 5c764b93e..69b817143 100644 --- a/src/proposals/ContractRegistryProposal.sol +++ b/src/proposals/ContractRegistryProposal.sol @@ -54,6 +54,7 @@ contract ContractRegistryProposal is GovernorBravoProposal { "The ContractRegistryAdmin policy is used to manage the addresses registered in the RGSTY module.\n\n", "The RGSTY module will be used by the LoanConsolidator policy to lookup contract addresses. In order to roll-out the improved LoanConsolidator, this proposal must be executed first.\n\n", "[View the audit report here](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2024_10_LoanConsolidator_Audit.pdf)\n\n", + "[View the pull request here](https://github.com/OlympusDAO/olympus-v3/pull/19)\n\n", "## Assumptions\n\n", "- The RGSTY module has been deployed and activated as a module by the DAO MS.\n", "- The ContractRegistryAdmin policy has been deployed and activated as a policy by the DAO MS.\n\n", diff --git a/src/proposals/LoanConsolidatorProposal.sol b/src/proposals/LoanConsolidatorProposal.sol index 802949894..eea1ff2b6 100644 --- a/src/proposals/LoanConsolidatorProposal.sol +++ b/src/proposals/LoanConsolidatorProposal.sol @@ -44,6 +44,7 @@ contract LoanConsolidatorProposal is GovernorBravoProposal { "- Allows for migration of loans from one Clearinghouse to another (in preparation for a USDS Clearinghouse)\n", "- Allows for migration of loans from one owner to another\n\n", "[View the audit report here](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2024_10_LoanConsolidator_Audit.pdf)\n\n", + "[View the pull request here](https://github.com/OlympusDAO/olympus-v3/pull/19)\n\n", "## Assumptions\n\n", "- The Contract Registry module has been deployed and activated as a module by the DAO MS.\n", "- The ContractRegistryAdmin policy has been deployed and activated as a policy by the DAO MS.\n", From 8d5f32a50bb1017fd6e294f29dd1e7a8a446fca4 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 13 Nov 2024 12:47:16 +0400 Subject: [PATCH 129/160] Proposal: update formatting --- src/proposals/ContractRegistryProposal.sol | 35 ++++++++++++------- src/proposals/LoanConsolidatorProposal.sol | 39 ++++++++++++++-------- 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/proposals/ContractRegistryProposal.sol b/src/proposals/ContractRegistryProposal.sol index 69b817143..9f99af0a3 100644 --- a/src/proposals/ContractRegistryProposal.sol +++ b/src/proposals/ContractRegistryProposal.sol @@ -36,7 +36,7 @@ contract ContractRegistryProposal is GovernorBravoProposal { // Returns the id of the proposal. function id() public pure override returns (uint256) { - return 3; + return 5; } // Returns the name of the proposal. @@ -48,20 +48,31 @@ contract ContractRegistryProposal is GovernorBravoProposal { function description() public pure override returns (string memory) { return string.concat( - "# Contract Registry Activation\n\n", - "This proposal activates the RGSTY module (and associated ContractRegistryAdmin configuration policy).\n\n", - "The RGSTY module is used to register commonly-used addresses that can be referenced by other contracts. These addresses are marked as either mutable or immutable.\n\n", - "The ContractRegistryAdmin policy is used to manage the addresses registered in the RGSTY module.\n\n", - "The RGSTY module will be used by the LoanConsolidator policy to lookup contract addresses. In order to roll-out the improved LoanConsolidator, this proposal must be executed first.\n\n", - "[View the audit report here](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2024_10_LoanConsolidator_Audit.pdf)\n\n", - "[View the pull request here](https://github.com/OlympusDAO/olympus-v3/pull/19)\n\n", - "## Assumptions\n\n", + "# Contract Registry Activation\n", + "\n", + "This proposal activates the RGSTY module (and associated ContractRegistryAdmin configuration policy).\n", + "\n", + "The RGSTY module is used to register commonly-used addresses that can be referenced by other contracts. These addresses are marked as either mutable or immutable.\n", + "\n", + "The ContractRegistryAdmin policy is used to manage the addresses registered in the RGSTY module.\n", + "\n", + "The RGSTY module will be used by the LoanConsolidator policy to lookup contract addresses. In order to roll-out the improved LoanConsolidator, this proposal must be executed first.\n", + "\n", + "## Resources\n", + "\n", + "- [View the audit report here](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2024_10_LoanConsolidator_Audit.pdf)\n", + "- [View the pull request here](https://github.com/OlympusDAO/olympus-v3/pull/19)\n", + "\n", + "## Assumptions\n", + "\n", "- The RGSTY module has been deployed and activated as a module by the DAO MS.\n", - "- The ContractRegistryAdmin policy has been deployed and activated as a policy by the DAO MS.\n\n", - "## Proposal Steps\n\n", + "- The ContractRegistryAdmin policy has been deployed and activated as a policy by the DAO MS.\n", + "\n", + "## Proposal Steps\n", + "\n", "1. Grant the `contract_registry_admin` role to the OCG Timelock.\n", "2. Register immutable addresses for DAI, SDAI, USDS, SUSDS, GOHM and OHM.\n", - "3. Register mutable addresses for the Flash Lender and DAI-USDS Migrator contracts." + "3. Register mutable addresses for the Flash Lender and DAI-USDS Migrator contracts.\n" ); } diff --git a/src/proposals/LoanConsolidatorProposal.sol b/src/proposals/LoanConsolidatorProposal.sol index eea1ff2b6..846e1946c 100644 --- a/src/proposals/LoanConsolidatorProposal.sol +++ b/src/proposals/LoanConsolidatorProposal.sol @@ -23,36 +23,47 @@ contract LoanConsolidatorProposal is GovernorBravoProposal { // Returns the id of the proposal. function id() public pure override returns (uint256) { - return 4; + return 6; } // Returns the name of the proposal. function name() public pure override returns (string memory) { - return "LoanConsolidator and Contract Registry Activation"; + return "LoanConsolidator Activation"; } // Provides a brief description of the proposal. function description() public pure override returns (string memory) { return string.concat( - "# LoanConsolidator and Contract Registry Activation\n\n", - "This proposal activates the LoanConsolidator policy and installs the Contract Registry module (and associated ContractRegistryAdmin configuration policy).\n\n", - "The Contract Registry module is used to register commonly-used addresses that can be referenced by other contracts. These addresses are marked as either mutable or immutable.\n\n", - "The previous version of LoanConsolidator contained logic that, combined with infinite approvals, enabled an attacker to steal funds from users of the CoolerUtils contract (as it was known at the time).\n\n", - "This version introduces the following:\n\n", + "# LoanConsolidator Activation\n", + "\n", + "This proposal activates the LoanConsolidator policy.\n", + "\n", + "The previous version of LoanConsolidator contained logic that, combined with infinite approvals, enabled an attacker to steal funds from users of the CoolerUtils contract (as it was known at the time).\n", + "\n", + "This version introduces the following:\n", + "\n", "- Strict checks on callers, ownership and Clearinghouse validity\n", "- Allows for migration of loans from one Clearinghouse to another (in preparation for a USDS Clearinghouse)\n", - "- Allows for migration of loans from one owner to another\n\n", - "[View the audit report here](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2024_10_LoanConsolidator_Audit.pdf)\n\n", - "[View the pull request here](https://github.com/OlympusDAO/olympus-v3/pull/19)\n\n", - "## Assumptions\n\n", + "- Allows for migration of loans from one owner to another\n", + "\n", + "## Resources\n", + "\n", + "- [View the audit report here](https://storage.googleapis.com/olympusdao-landing-page-reports/audits/2024_10_LoanConsolidator_Audit.pdf)\n", + "\n", + "- [View the pull request here](https://github.com/OlympusDAO/olympus-v3/pull/19)\n", + "\n", + "## Assumptions\n", + "\n", "- The Contract Registry module has been deployed and activated as a module by the DAO MS.\n", "- The ContractRegistryAdmin policy has been deployed and activated as a policy by the DAO MS.\n", "- The mutable and immutable contract addresses required by LoanConsolidator have been registered in the Contract Registry.\n", - "- The LoanConsolidator contract has been deployed and activated as a policy by the DAO MS.\n\n", - "## Proposal Steps\n\n", + "- The LoanConsolidator contract has been deployed and activated as a policy by the DAO MS.\n", + "\n", + "## Proposal Steps\n", + "\n", "1. Grant the `loan_consolidator_admin` role to the OCG Timelock.\n", - "2. Activate the LoanConsolidator." + "2. Activate the LoanConsolidator.\n" ); } From 289d143b8c2ecdc68df4b9bd9e41bbc682a1df85 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 13 Nov 2024 14:49:51 +0400 Subject: [PATCH 130/160] chore: linting --- .../solidity-metrics.html | 4397 +++++++++-------- 1 file changed, 2255 insertions(+), 2142 deletions(-) diff --git a/audit/2024-10_loan-consolidator/solidity-metrics.html b/audit/2024-10_loan-consolidator/solidity-metrics.html index 170b40751..452d21c33 100644 --- a/audit/2024-10_loan-consolidator/solidity-metrics.html +++ b/audit/2024-10_loan-consolidator/solidity-metrics.html @@ -488,8 +488,8 @@ "object" == typeof exports && "undefined" != typeof module ? (module.exports = e()) : "function" == typeof define && define.amd - ? define(e) - : (t.Chart = e()); + ? define(e) + : (t.Chart = e()); })(this, function () { "use strict"; var t = { @@ -623,10 +623,10 @@ s == o ? (e = 0) : n == s - ? (e = (a - r) / l) - : a == s - ? (e = 2 + (r - n) / l) - : r == s && (e = 4 + (n - a) / l), + ? (e = (a - r) / l) + : a == s + ? (e = 2 + (r - n) / l) + : r == s && (e = 4 + (n - a) / l), (e = Math.min(60 * e, 360)) < 0 && (e += 360), (i = (o + s) / 2), [e, 100 * (s == o ? 0 : i <= 0.5 ? l / (s + o) : l / (2 - s - o)), 100 * i] @@ -646,10 +646,10 @@ s == o ? (e = 0) : n == s - ? (e = (a - r) / l) - : a == s - ? (e = 2 + (r - n) / l) - : r == s && (e = 4 + (n - a) / l), + ? (e = (a - r) / l) + : a == s + ? (e = 2 + (r - n) / l) + : r == s && (e = 4 + (n - a) / l), (e = Math.min(60 * e, 360)) < 0 && (e += 360), [e, i, ((s / 255) * 1e3) / 10] ); @@ -738,10 +738,10 @@ 6 * n < 1 ? e + 6 * (i - e) * n : 2 * n < 1 - ? i - : 3 * n < 2 - ? e + (i - e) * (2 / 3 - n) * 6 - : e), + ? i + : 3 * n < 2 + ? e + (i - e) * (2 / 3 - n) * 6 + : e), (a[u] = 255 * r); return a; } @@ -1344,14 +1344,14 @@ a && (i = Math.round((parseInt(a, 16) / 255) * 100) / 100); } else if ( (n = t.match( - /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i + /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, )) ) { for (r = 0; r < e.length; r++) e[r] = parseInt(n[r + 1]); i = parseFloat(n[4]); } else if ( (n = t.match( - /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i + /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, )) ) { for (r = 0; r < e.length; r++) @@ -1368,7 +1368,7 @@ function L(t) { if (t) { var e = t.match( - /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/ + /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/, ); if (e) { var i = parseFloat(e[4]); @@ -1384,7 +1384,7 @@ function W(t) { if (t) { var e = t.match( - /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/ + /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/, ); if (e) { var i = parseFloat(e[4]); @@ -1435,33 +1435,33 @@ return t instanceof j ? t : this instanceof j - ? ((this.valid = !1), - (this.values = { - rgb: [0, 0, 0], - hsl: [0, 0, 0], - hsv: [0, 0, 0], - hwb: [0, 0, 0], - cmyk: [0, 0, 0, 0], - alpha: 1, - }), - void ("string" == typeof t - ? (e = F.getRgba(t)) - ? this.setValues("rgb", e) - : (e = F.getHsla(t)) - ? this.setValues("hsl", e) - : (e = F.getHwb(t)) && this.setValues("hwb", e) - : "object" == typeof t && - (void 0 !== (e = t).r || void 0 !== e.red + ? ((this.valid = !1), + (this.values = { + rgb: [0, 0, 0], + hsl: [0, 0, 0], + hsv: [0, 0, 0], + hwb: [0, 0, 0], + cmyk: [0, 0, 0, 0], + alpha: 1, + }), + void ("string" == typeof t + ? (e = F.getRgba(t)) ? this.setValues("rgb", e) - : void 0 !== e.l || void 0 !== e.lightness - ? this.setValues("hsl", e) - : void 0 !== e.v || void 0 !== e.value - ? this.setValues("hsv", e) - : void 0 !== e.w || void 0 !== e.whiteness - ? this.setValues("hwb", e) - : (void 0 === e.c && void 0 === e.cyan) || - this.setValues("cmyk", e)))) - : new j(t); + : (e = F.getHsla(t)) + ? this.setValues("hsl", e) + : (e = F.getHwb(t)) && this.setValues("hwb", e) + : "object" == typeof t && + (void 0 !== (e = t).r || void 0 !== e.red + ? this.setValues("rgb", e) + : void 0 !== e.l || void 0 !== e.lightness + ? this.setValues("hsl", e) + : void 0 !== e.v || void 0 !== e.value + ? this.setValues("hsv", e) + : void 0 !== e.w || void 0 !== e.whiteness + ? this.setValues("hwb", e) + : (void 0 === e.c && void 0 === e.cyan) || + this.setValues("cmyk", e)))) + : new j(t); var e; }; (j.prototype = { @@ -1663,7 +1663,7 @@ return this.rgb( o * this.red() + s * i.red(), o * this.green() + s * i.green(), - o * this.blue() + s * i.blue() + o * this.blue() + s * i.blue(), ).alpha(this.alpha() * n + i.alpha() * (1 - n)); }, toJSON: function () { @@ -1681,8 +1681,8 @@ "[object Array]" === (e = {}.toString.call(t)) ? (a[r] = t.slice(0)) : "[object Number]" === e - ? (a[r] = t) - : console.error("unexpected color value:", t)); + ? (a[r] = t) + : console.error("unexpected color value:", t)); return i; }, }), @@ -1746,8 +1746,8 @@ return void 0 === i ? n[e] : i === n[e] - ? this - : ((n[e] = i), this.setValues(t, n), this); + ? this + : ((n[e] = i), this.setValues(t, n), this); }), "undefined" != typeof window && (window.Color = j); var U, @@ -1955,10 +1955,10 @@ return 0 === t ? 0 : 1 === t - ? 1 - : (t /= 0.5) < 1 - ? 0.5 * Math.pow(2, 10 * (t - 1)) - : 0.5 * (2 - Math.pow(2, -10 * --t)); + ? 1 + : (t /= 0.5) < 1 + ? 0.5 * Math.pow(2, 10 * (t - 1)) + : 0.5 * (2 - Math.pow(2, -10 * --t)); }, easeInCirc: function (t) { return t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1); @@ -1978,14 +1978,14 @@ return 0 === t ? 0 : 1 === t - ? 1 - : (i || (i = 0.3), - n < 1 - ? ((n = 1), (e = i / 4)) - : (e = (i / (2 * Math.PI)) * Math.asin(1 / n)), - -n * - Math.pow(2, 10 * (t -= 1)) * - Math.sin(((t - e) * (2 * Math.PI)) / i)); + ? 1 + : (i || (i = 0.3), + n < 1 + ? ((n = 1), (e = i / 4)) + : (e = (i / (2 * Math.PI)) * Math.asin(1 / n)), + -n * + Math.pow(2, 10 * (t -= 1)) * + Math.sin(((t - e) * (2 * Math.PI)) / i)); }, easeOutElastic: function (t) { var e = 1.70158, @@ -1994,15 +1994,15 @@ return 0 === t ? 0 : 1 === t - ? 1 - : (i || (i = 0.3), - n < 1 - ? ((n = 1), (e = i / 4)) - : (e = (i / (2 * Math.PI)) * Math.asin(1 / n)), - n * - Math.pow(2, -10 * t) * - Math.sin(((t - e) * (2 * Math.PI)) / i) + - 1); + ? 1 + : (i || (i = 0.3), + n < 1 + ? ((n = 1), (e = i / 4)) + : (e = (i / (2 * Math.PI)) * Math.asin(1 / n)), + n * + Math.pow(2, -10 * t) * + Math.sin(((t - e) * (2 * Math.PI)) / i) + + 1); }, easeInOutElastic: function (t) { var e = 1.70158, @@ -2011,21 +2011,21 @@ return 0 === t ? 0 : 2 == (t /= 0.5) - ? 1 - : (i || (i = 0.45), - n < 1 - ? ((n = 1), (e = i / 4)) - : (e = (i / (2 * Math.PI)) * Math.asin(1 / n)), - t < 1 - ? n * - Math.pow(2, 10 * (t -= 1)) * - Math.sin(((t - e) * (2 * Math.PI)) / i) * - -0.5 - : n * - Math.pow(2, -10 * (t -= 1)) * - Math.sin(((t - e) * (2 * Math.PI)) / i) * - 0.5 + - 1); + ? 1 + : (i || (i = 0.45), + n < 1 + ? ((n = 1), (e = i / 4)) + : (e = (i / (2 * Math.PI)) * Math.asin(1 / n)), + t < 1 + ? n * + Math.pow(2, 10 * (t -= 1)) * + Math.sin(((t - e) * (2 * Math.PI)) / i) * + -0.5 + : n * + Math.pow(2, -10 * (t -= 1)) * + Math.sin(((t - e) * (2 * Math.PI)) / i) * + 0.5 + + 1); }, easeInBack: function (t) { var e = 1.70158; @@ -2048,10 +2048,10 @@ return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 - ? 7.5625 * (t -= 1.5 / 2.75) * t + 0.75 - : t < 2.5 / 2.75 - ? 7.5625 * (t -= 2.25 / 2.75) * t + 0.9375 - : 7.5625 * (t -= 2.625 / 2.75) * t + 0.984375; + ? 7.5625 * (t -= 1.5 / 2.75) * t + 0.75 + : t < 2.5 / 2.75 + ? 7.5625 * (t -= 2.25 / 2.75) * t + 0.9375 + : 7.5625 * (t -= 2.625 / 2.75) * t + 0.984375; }, easeInOutBounce: function (t) { return t < 0.5 @@ -2085,12 +2085,12 @@ t.arc(u, d, o, 0, tt), t.arc(s, d, o, tt, K)) : s < u - ? (t.moveTo(s, i), - t.arc(u, l, o, -tt, tt), - t.arc(s, l, o, tt, K + tt)) - : l < d - ? (t.arc(s, l, o, -K, 0), t.arc(s, d, o, 0, K)) - : t.arc(s, l, o, -K, K), + ? (t.moveTo(s, i), + t.arc(u, l, o, -tt, tt), + t.arc(s, l, o, tt, K + tt)) + : l < d + ? (t.arc(s, l, o, -K, 0), t.arc(s, d, o, 0, K)) + : t.arc(s, l, o, -K, K), t.closePath(), t.moveTo(e, i); } else t.rect(e, i, n, a); @@ -2190,7 +2190,7 @@ n - e.width / 2, a - e.height / 2, e.width, - e.height + e.height, ); }, _isPointInArea: function (t, e) { @@ -2229,7 +2229,7 @@ n ? i.controlPointNextX : i.controlPointPreviousX, n ? i.controlPointNextY : i.controlPointPreviousY, i.x, - i.y + i.y, ) : t.lineTo(i.x, i.y); }, @@ -2286,7 +2286,7 @@ family: st(t.fontFamily, e.defaultFontFamily), lineHeight: Z.options.toLineHeight( st(t.lineHeight, e.defaultLineHeight), - i + i, ), size: i, style: st(t.fontStyle, e.defaultFontStyle), @@ -2638,17 +2638,17 @@ r(a.backgroundColor), ], void 0, - i + i, )), (a.borderColor = yt( [n.hoverBorderColor, e.hoverBorderColor, r(a.borderColor)], void 0, - i + i, )), (a.borderWidth = yt( [n.hoverBorderWidth, e.hoverBorderWidth, a.borderWidth], void 0, - i + i, )); }, resyncElements: function () { @@ -2766,14 +2766,14 @@ i.innerRadius - r, a + t, n - t, - !0 + !0, )) : e.arc( i.x, i.y, r, a + Math.PI / 2, - n - Math.PI / 2 + n - Math.PI / 2, ), e.closePath(), e.clip(), @@ -3275,7 +3275,7 @@ o = a.start + a.chunk * r + a.chunk / 2, s = Math.min( ut.valueOrDefault(n.maxBarThickness, 1 / 0), - a.chunk * a.ratio + a.chunk * a.ratio, ); return {base: o - s / 2, head: o + s / 2, center: o, size: s}; }, @@ -3385,7 +3385,7 @@ }), (e.backgroundColor = Ut( i.hoverBackgroundColor, - n(i.backgroundColor) + n(i.backgroundColor), )), (e.borderColor = Ut(i.hoverBorderColor, n(i.borderColor))), (e.borderWidth = Ut(i.hoverBorderWidth, i.borderWidth)), @@ -3420,7 +3420,7 @@ (d.radius = Gt( [s.radius, u ? u.r : void 0, o.radius, l.radius], h, - e + e, )), d ); @@ -3442,7 +3442,7 @@ e.push( '
  • ' + '">', ), a[r] && e.push(a[r]), e.push("
  • "); @@ -3468,17 +3468,17 @@ l.backgroundColor, ], void 0, - n + n, ), strokeStyle: Zt( [s.borderColor, r.borderColor, l.borderColor], void 0, - n + n, ), lineWidth: Zt( [s.borderWidth, r.borderWidth, l.borderWidth], void 0, - n + n, ), hidden: isNaN(r.data[n]) || a.data[n].hidden, index: n, @@ -3562,13 +3562,13 @@ ? -1 : Math.min( y.x * (y.x < 0 ? 1 : M), - b.x * (b.x < 0 ? 1 : M) + b.x * (b.x < 0 ? 1 : M), ), y: w ? -1 : Math.min( y.y * (y.y < 0 ? 1 : M), - b.y * (b.y < 0 ? 1 : M) + b.y * (b.y < 0 ? 1 : M), ), }, D = { @@ -3576,13 +3576,13 @@ ? 1 : Math.max( y.x * (y.x > 0 ? 1 : M), - b.x * (b.x > 0 ? 1 : M) + b.x * (b.x > 0 ? 1 : M), ), y: _ ? 1 : Math.max( y.y * (y.y > 0 ? 1 : M), - b.y * (b.y > 0 ? 1 : M) + b.y * (b.y > 0 ? 1 : M), ), }, C = {width: 0.5 * (D.x - S.x), height: 0.5 * (D.y - S.y)}; @@ -3627,9 +3627,9 @@ i && s.animateRotate ? 0 : t.hidden - ? 0 - : n.calculateCircumference(c.data[e]) * - (o.circumference / (2 * Math.PI)), + ? 0 + : n.calculateCircumference(c.data[e]) * + (o.circumference / (2 * Math.PI)), g = i && s.animateScale ? 0 : n.innerRadius, m = i && s.animateScale ? 0 : n.outerRadius, p = t._options || {}; @@ -3977,7 +3977,7 @@ ut.previousItem(l, t)._model, i, ut.nextItem(l, t)._model, - o.tension + o.tension, )), (i.controlPointPreviousX = n.previous.x), (i.controlPointPreviousY = n.previous.y), @@ -3992,24 +3992,24 @@ ((i.controlPointPreviousX = u( i.controlPointPreviousX, s.left, - s.right + s.right, )), (i.controlPointPreviousY = u( i.controlPointPreviousY, s.top, - s.bottom + s.bottom, ))), t < l.length - 1 && te(l[t + 1]._model, s) && ((i.controlPointNextX = u( i.controlPointNextX, s.left, - s.right + s.right, )), (i.controlPointNextY = u( i.controlPointNextY, s.top, - s.bottom + s.bottom, )))); }, draw: function () { @@ -4048,7 +4048,7 @@ }), (e.backgroundColor = Jt( i.hoverBackgroundColor, - n(i.backgroundColor) + n(i.backgroundColor), )), (e.borderColor = Jt(i.hoverBorderColor, n(i.borderColor))), (e.borderWidth = Jt(i.hoverBorderWidth, i.borderWidth)), @@ -4077,7 +4077,7 @@ e.push( '
  • ' + '">', ), a[r] && e.push(a[r]), e.push("
  • "); @@ -4102,17 +4102,17 @@ s.backgroundColor, ], void 0, - n + n, ), strokeStyle: ne( [o.borderColor, r.borderColor, s.borderColor], void 0, - n + n, ), lineWidth: ne( [o.borderWidth, r.borderWidth, s.borderWidth], void 0, - n + n, ), hidden: isNaN(r.data[n]) || a.data[n].hidden, index: n, @@ -4179,7 +4179,7 @@ (e.outerRadius = Math.max(a / 2, 0)), (e.innerRadius = Math.max( n.cutoutPercentage ? (e.outerRadius / 100) * n.cutoutPercentage : 1, - 0 + 0, )), (e.radiusLength = (e.outerRadius - e.innerRadius) / e.getVisibleDatasetCount()), @@ -4413,7 +4413,7 @@ ut.previousItem(o, t, !0)._model, i, ut.nextItem(o, t, !0)._model, - i.tension + i.tension, )), (i.controlPointPreviousX = s(n.previous.x, r.left, r.right)), (i.controlPointPreviousY = s(n.previous.y, r.top, r.bottom)), @@ -4765,11 +4765,11 @@ "undefined" != typeof window ? window : "undefined" != typeof global - ? global - : "undefined" != typeof self && self; + ? global + : "undefined" != typeof self && self; function _e() { throw new Error( - "Dynamic requires are not currently supported by rollup-plugin-commonjs" + "Dynamic requires are not currently supported by rollup-plugin-commonjs", ); } var ke, @@ -4879,7 +4879,7 @@ ut.requestAnimFrame.call(window, function () { (r = !1), n.apply(a, o); })); - }) + }), )); !(function (t, e) { var i = t[Me] || (t[Me] = {}), @@ -5002,9 +5002,9 @@ var i = Te[t.type] || t.type, n = ut.getRelativePosition(t, e); return Re(i, e, n.x, n.y, t); - })(e, t) + })(e, t), ); - }) + }), ); } else We(n, i, t); }, @@ -5035,7 +5035,7 @@ addEventListener: function () {}, removeEventListener: function () {}, }, - ze + ze, ); ot._set("global", {plugins: {}}); var He = { @@ -5180,8 +5180,8 @@ r.label ? (i = r.label) : r.xLabel - ? (i = r.xLabel) - : a > 0 && r.index < a && (i = n[r.index]); + ? (i = r.xLabel) + : a > 0 && r.index < a && (i = n[r.index]); } return i; }, @@ -5302,8 +5302,8 @@ return "center" === e ? t.x + t.width / 2 : "right" === e - ? t.x + t.width - t.xPadding - : t.x + t.xPadding; + ? t.x + t.width - t.xPadding + : t.x + t.xPadding; } function $e(t) { return Ue([], Ge(t)); @@ -5402,7 +5402,7 @@ datasetIndex: s, x: n._model.x, y: n._model.y, - }) + }), ); c.filter && (w = w.filter(function (t) { @@ -5462,13 +5462,13 @@ (i.font = ut.fontString( u, e._titleFontStyle, - e._titleFontFamily + e._titleFontFamily, )), ut.each(e.title, f), (i.font = ut.fontString( d, e._bodyFontStyle, - e._bodyFontFamily + e._bodyFontFamily, )), ut.each(e.beforeBody.concat(e.afterBody), f), (c = e.displayColors ? d + 2 : 0), @@ -5481,7 +5481,7 @@ (i.font = ut.fontString( h, e._footerFontStyle, - e._footerFontFamily + e._footerFontFamily, )), ut.each(e.footer, f), {width: (a += 2 * e.xPadding), height: n} @@ -5513,8 +5513,8 @@ ? (a += h) : "right" === u && (a -= h) : "left" === u - ? (a -= c) - : "right" === u && (a += c), + ? (a -= c) + : "right" === u && (a += c), {x: a, y: r} ); })( @@ -5576,7 +5576,7 @@ yAlign: g.yAlign ? g.yAlign : h, }; })(this, b)), - h._chart + h._chart, )); } else g.opacity = 0; return ( @@ -5626,8 +5626,8 @@ ("left" === h ? ((n = (a = f + d + u) - u), (r = a + u)) : "right" === h - ? ((n = (a = f + m - d - u) - u), (r = a + u)) - : ((n = (a = i.caretX) - u), (r = a + u)), + ? ((n = (a = f + m - d - u) - u), (r = a + u)) + : ((n = (a = i.caretX) - u), (r = a + u)), "top" === c) ) (s = (o = g) - u), (l = o); @@ -5653,7 +5653,7 @@ i.font = ut.fontString( o, e._titleFontStyle, - e._titleFontFamily + e._titleFontFamily, ), a = 0, r = n.length; @@ -5718,7 +5718,7 @@ (i.font = ut.fontString( e.footerFontSize, e._footerFontStyle, - e._footerFontFamily + e._footerFontFamily, )), ut.each(n, function (n) { i.fillText(n, t.x, t.y), @@ -5790,7 +5790,7 @@ : (i._active = i._chart.getElementsAtEventForMode( t, n.mode, - n + n, )), (e = !ut.arrayEquals(i._active, i._lastActive)) && ((i._lastActive = i._active), @@ -5833,8 +5833,8 @@ "scales" === t ? (e[t] = ti(a, r)) : "scale" === t - ? (e[t] = ut.merge(a, [Ee.getScaleDefaults(r.type), r])) - : ut._merger(t, e, i, n); + ? (e[t] = ut.merge(a, [Ee.getScaleDefaults(r.type), r])) + : ut._merger(t, e, i, n); }, }); } @@ -5892,7 +5892,7 @@ n && a ? (i.initialize(), i.update()) : console.error( - "Failed to create chart: can't acquire context from the given item" + "Failed to create chart: can't acquire context from the given item", ); }, initialize: function () { @@ -5965,7 +5965,7 @@ }), (e.scales.yAxes || []).map(function (t) { return {options: t, dtype: "linear", dposition: "left"}; - }) + }), )), e.scale && n.push({ @@ -6018,12 +6018,12 @@ var o = ue[a.type]; if (void 0 === o) throw new Error( - '"' + a.type + '" is not a chart type.' + '"' + a.type + '" is not a chart type.', ); (a.controller = new o(t, n)), e.push(a.controller); } }, - t + t, ), e ); @@ -6035,7 +6035,7 @@ function (e, i) { t.getDatasetMeta(i).controller.reset(); }, - t + t, ); }, reset: function () { @@ -6067,7 +6067,7 @@ function (t, e) { n.getDatasetMeta(e).controller.buildOrUpdateElements(); }, - n + n, ), n.updateLayout(), n.options.animation && @@ -6148,7 +6148,7 @@ function (t) { t.draw(e.chartArea); }, - e + e, ), e.drawDatasets(t), e._drawTooltip(t), @@ -6263,7 +6263,7 @@ _data: t.data, _options: t.options.tooltips, }, - t + t, ); }, bindEvents: function () { @@ -6350,7 +6350,7 @@ (ni.Controller = ni), (ni.types = {}), (ut.configMerge = ei), (ut.scaleMerge = ti); function ri() { throw new Error( - "This method is not implemented: either no adapter can be found or an incomplete integration was provided." + "This method is not implemented: either no adapter can be found or an incomplete integration was provided.", ); } function oi(t) { @@ -6400,8 +6400,8 @@ return 0 === t ? "0" : 1 === n || 2 === n || 5 === n || 0 === e || e === i.length - 1 - ? t.toExponential() - : ""; + ? t.toExponential() + : ""; }, }, }, @@ -6493,7 +6493,7 @@ u.maxHeight = e, u.margins = ut.extend( {left: 0, right: 0, top: 0, bottom: 0}, - i + i, ), u._maxLabelLines = 0, u.longestLabelWidth = 0, @@ -6636,8 +6636,8 @@ ? t.maxWidth - t.margins.left - t.margins.right : t.maxWidth : s && o.drawTicks - ? c - : 0), + ? c + : 0), (e.height = u ? (s && o.drawTicks ? c : 0) : t.maxHeight), r.display && s) ) { @@ -6685,7 +6685,7 @@ (t.paddingRight = Math.max(t.paddingRight - t.margins.right, 0)), (t.paddingBottom = Math.max( t.paddingBottom - t.margins.bottom, - 0 + 0, ))); }, afterFit: function () { @@ -6748,10 +6748,10 @@ return this.beginAtZero ? 0 : t < 0 && e < 0 - ? e - : t > 0 && e > 0 - ? t - : 0; + ? e + : t > 0 && e > 0 + ? t + : 0; }, _autoSkip: function (t) { var e, @@ -6793,8 +6793,8 @@ ? s / a : u / r : u * r < s * a - ? u / a - : s / r; + ? u / a + : s / r; }, _isVisible: function () { var t, @@ -6853,10 +6853,14 @@ "top" === f ? ((n = F(o, e.bottom, A)), (a = e.bottom - D), (r = n - A / 2)) : "bottom" === f - ? ((n = F(o, e.top, A)), (a = n + A / 2), (r = e.top + D)) - : "left" === f - ? ((n = F(o, e.right, A)), (a = e.right - D), (r = n - A / 2)) - : ((n = F(o, e.left, A)), (a = n + A / 2), (r = e.left + D)); + ? ((n = F(o, e.top, A)), (a = n + A / 2), (r = e.top + D)) + : "left" === f + ? ((n = F(o, e.right, A)), + (a = e.right - D), + (r = n - A / 2)) + : ((n = F(o, e.left, A)), + (a = n + A / 2), + (r = e.left + D)); if ( (ut.each(y, function (n, s) { if (!ut.isNullOrUndef(n.label)) { @@ -6895,11 +6899,11 @@ ? (n -= t.isHorizontal() ? Math.max( n - t.left, - t.right - n + t.right - n, ) : Math.max( n - t.top, - t.bottom - n + t.bottom - n, )) : (n -= 0 === e @@ -6908,7 +6912,7 @@ 2 : (n - t.getPixelForTick( - e - 1 + e - 1, )) / 2)), n @@ -7309,8 +7313,8 @@ e.relativePoints ? (u[n] = 100) : a < 0 - ? (d[n] += a) - : (u[n] += a)); + ? (d[n] += a) + : (u[n] += a)); }); }), ut.each(s, function (e) { @@ -7478,8 +7482,11 @@ (t.min > 0 ? (t.minNotZero = t.min) : t.max < 1 - ? (t.minNotZero = Math.pow(10, Math.floor(ut.log10(t.max)))) - : (t.minNotZero = 1)); + ? (t.minNotZero = Math.pow( + 10, + Math.floor(ut.log10(t.max)), + )) + : (t.minNotZero = 1)); }, buildTicks: function () { var t = this, @@ -7633,8 +7640,8 @@ return t === n || t === a ? {start: e - i / 2, end: e + i / 2} : t < n || t > a - ? {start: e - i, end: e} - : {start: e, end: e + i}; + ? {start: e - i, end: e} + : {start: e, end: e + i}; } function Ri(t) { return 0 === t || 180 === t ? "center" : t < 180 ? "left" : "right"; @@ -7691,7 +7698,7 @@ yi.prototype.convertTicksToLabels.call(t), (t.pointLabels = t.chart.data.labels.map( t.options.pointLabels.callback, - t + t, )); }, getLabelForIndex: function (t, e) { @@ -7749,7 +7756,7 @@ (s = Yi(s)), (n.drawingArea = Math.min( Math.floor(t - (a + r) / 2), - Math.floor(t - (o + s) / 2) + Math.floor(t - (o + s) / 2), )), n.setCenterPoint(a, r, o, s); }, @@ -7794,7 +7801,7 @@ e = this.max; return this.getPointPositionForValue( 0, - this.beginAtZero ? 0 : t < 0 && e < 0 ? e : t > 0 && e > 0 ? t : 0 + this.beginAtZero ? 0 : t < 0 && e < 0 ? e : t > 0 && e > 0 ? t : 0, ); }, draw: function () { @@ -7821,7 +7828,7 @@ (e.strokeStyle = s), e.setLineDash && (e.setLineDash( - Ti([n.borderDash, a.borderDash, []]) + Ti([n.borderDash, a.borderDash, []]), ), (e.lineDashOffset = Ti([ n.borderDashOffset, @@ -7829,7 +7836,7 @@ 0, ]))); var u = t.getDistanceFromCenterForValue( - i.ticks.reverse ? t.min : t.max + i.ticks.reverse ? t.min : t.max, ), d = ut.options._parseFont(r); (e.font = d.string), (e.textBaseline = "middle"); @@ -7847,7 +7854,7 @@ m = Pi( r.fontColor, h, - ot.global.defaultFontColor + ot.global.defaultFontColor, ); e.fillStyle = m; var p = t.getIndexAngle(h), @@ -7862,7 +7869,7 @@ ut.each(t.ticks, function (e, s) { if (s > 0 || n.reverse) { var l = t.getDistanceFromCenterForValue( - t.ticksAsNumbers[s] + t.ticksAsNumbers[s], ); if ( (i.display && @@ -7881,7 +7888,7 @@ (r.lineWidth = u), r.setLineDash && (r.setLineDash( - e.borderDash || [] + e.borderDash || [], ), (r.lineDashOffset = e.borderDashOffset || 0)), @@ -7893,7 +7900,7 @@ t.yCenter, i, 0, - 2 * Math.PI + 2 * Math.PI, ); else { (a = t.getPointPosition(0, i)), @@ -7921,7 +7928,7 @@ -d / 2 - n.backdropPaddingX, -l - o.size / 2 - n.backdropPaddingY, d + 2 * n.backdropPaddingX, - o.size + 2 * n.backdropPaddingY + o.size + 2 * n.backdropPaddingY, ); } (a.textAlign = "center"), @@ -8092,7 +8099,7 @@ return ( e.format && console.warn( - "options.time.format is deprecated and replaced by options.time.parser." + "options.time.format is deprecated and replaced by options.time.parser.", ), ut.mergeIf(e.displayFormats, i.formats()), fi.prototype.update.apply(this, arguments) @@ -8251,7 +8258,7 @@ t, "time", e[e.length - 2], - "pos" + "pos", )) / 2))), {start: s, end: l} @@ -8284,8 +8291,8 @@ r.tooltipFormat ? n.format(Zi(i, o), r.tooltipFormat) : "string" == typeof o - ? o - : n.format(Zi(i, o), r.displayFormats.datetime) + ? o + : n.format(Zi(i, o), r.displayFormats.datetime) ); }, tickFormatFunction: function (t, e, i, n) { @@ -8589,7 +8596,7 @@ "\nArguments: " + Array.prototype.slice.call(r).join("") + "\n" + - new Error().stack + new Error().stack, ), (i = !1); } @@ -8616,8 +8623,8 @@ (r(t[i]) && r(e[i]) ? ((n[i] = {}), h(n[i], t[i]), h(n[i], e[i])) : null != e[i] - ? (n[i] = e[i]) - : delete n[i]); + ? (n[i] = e[i]) + : delete n[i]); for (i in t) d(t, i) && !d(e, i) && r(t[i]) && (n[i] = h({}, n[i])); return n; @@ -8686,7 +8693,7 @@ (E[i] = function () { return this.localeData().ordinal( a.apply(this, arguments), - t + t, ); }); } @@ -8762,15 +8769,15 @@ /\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (t, e, i, n, a) { return e || i || n || a; - } - ) - ) + }, + ), + ), ); } function ht(t) { return t.replace( /[-\/\\^$*+?.()|[\]{}]/g, - "\\" + "\\", ); } var ct = {}; @@ -8866,7 +8873,7 @@ ? t._d["set" + (t._isUTC ? "UTC" : "") + e]( i, t.month(), - At(i, t.month()) + At(i, t.month()), ) : t._d["set" + (t._isUTC ? "UTC" : "") + e](i)); } @@ -8916,10 +8923,10 @@ var Ft = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/, Rt = "January_February_March_April_May_June_July_August_September_October_November_December".split( - "_" + "_", ), Lt = "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split( - "_" + "_", ); function Wt(t, e) { var i; @@ -8962,11 +8969,11 @@ (this._monthsShortRegex = this._monthsRegex), (this._monthsStrictRegex = new RegExp( "^(" + a.join("|") + ")", - "i" + "i", )), (this._monthsShortStrictRegex = new RegExp( "^(" + n.join("|") + ")", - "i" + "i", )); } function Ht(t) { @@ -8994,8 +9001,8 @@ u <= 0 ? (o = St((r = t - 1)) + u) : u > St(t) - ? ((r = t + 1), (o = u - St(t))) - : ((r = t), (o = u)), + ? ((r = t + 1), (o = u - St(t))) + : ((r = t), (o = u)), {year: r, dayOfYear: o} ); } @@ -9008,8 +9015,8 @@ o < 1 ? ((a = t.year() - 1), (n = o + Ut(a, e, i))) : o > Ut(t.year(), e, i) - ? ((n = o - Ut(t.year(), e, i)), (a = t.year() + 1)) - : ((a = t.year()), (n = o)), + ? ((n = o - Ut(t.year(), e, i)), (a = t.year() + 1)) + : ((a = t.year()), (n = o)), {week: n, year: a} ); } @@ -9073,7 +9080,7 @@ }); var qt = "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split( - "_" + "_", ), Zt = "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), $t = "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), @@ -9112,21 +9119,21 @@ (s[e] = ht(s[e])), (l[e] = ht(l[e])), (u[e] = ht(u[e])); (this._weekdaysRegex = new RegExp( "^(" + u.join("|") + ")", - "i" + "i", )), (this._weekdaysShortRegex = this._weekdaysRegex), (this._weekdaysMinRegex = this._weekdaysRegex), (this._weekdaysStrictRegex = new RegExp( "^(" + l.join("|") + ")", - "i" + "i", )), (this._weekdaysShortStrictRegex = new RegExp( "^(" + s.join("|") + ")", - "i" + "i", )), (this._weekdaysMinStrictRegex = new RegExp( "^(" + o.join("|") + ")", - "i" + "i", )); } function te() { @@ -9137,7 +9144,7 @@ return this.localeData().meridiem( this.hours(), this.minutes(), - e + e, ); }); } @@ -9295,7 +9302,7 @@ console.warn( "Locale " + t + - " not found. Did you forget to load it?" + " not found. Did you forget to load it?", )), ne._abbr ); @@ -9307,7 +9314,7 @@ if (((e.abbr = t), null != oe[t])) P( "defineLocaleOverride", - "use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info." + "use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info.", ), (n = oe[t]._config); else if (null != e.parentLocale) @@ -9378,21 +9385,21 @@ i[vt] < 0 || i[vt] > 11 ? vt : i[yt] < 1 || i[yt] > At(i[pt], i[vt]) - ? yt - : i[bt] < 0 || - i[bt] > 24 || - (24 === i[bt] && - (0 !== i[xt] || - 0 !== i[_t] || - 0 !== i[kt])) - ? bt - : i[xt] < 0 || i[xt] > 59 - ? xt - : i[_t] < 0 || i[_t] > 59 - ? _t - : i[kt] < 0 || i[kt] > 999 - ? kt - : -1), + ? yt + : i[bt] < 0 || + i[bt] > 24 || + (24 === i[bt] && + (0 !== i[xt] || + 0 !== i[_t] || + 0 !== i[kt])) + ? bt + : i[xt] < 0 || i[xt] > 59 + ? xt + : i[_t] < 0 || i[_t] > 59 + ? _t + : i[kt] < 0 || i[kt] > 999 + ? kt + : -1), f(t)._overflowDayOfYear && (e < pt || e > yt) && (e = yt), @@ -9439,7 +9446,7 @@ (i = ge( e.GG, t._a[pt], - jt(Ie(), 1, 4).year + jt(Ie(), 1, 4).year, )), (n = ge(e.W, 1)), ((a = ge(e.E, 1)) < 1 || a > 7) && @@ -9454,18 +9461,18 @@ ? ((a = e.d) < 0 || a > 6) && (l = !0) : null != e.e - ? ((a = e.e + r), - (e.e < 0 || e.e > 6) && - (l = !0)) - : (a = r); + ? ((a = e.e + r), + (e.e < 0 || e.e > 6) && + (l = !0)) + : (a = r); } n < 1 || n > Ut(i, r, o) ? (f(t)._overflowWeeks = !0) : null != l - ? (f(t)._overflowWeekday = !0) - : ((s = Bt(i, n, a, r, o)), - (t._a[pt] = s.year), - (t._dayOfYear = s.dayOfYear)); + ? (f(t)._overflowWeekday = !0) + : ((s = Bt(i, n, a, r, o)), + (t._a[pt] = s.year), + (t._dayOfYear = s.dayOfYear)); })(t), null != t._dayOfYear && ((o = ge(t._a[pt], a[pt])), @@ -9501,7 +9508,7 @@ n, a, r, - o + o, )), isFinite(s.getFullYear()) && s.setFullYear(t)) @@ -9512,7 +9519,7 @@ n, a, r, - o + o, )), s ); @@ -9621,7 +9628,7 @@ .replace(/\([^)]*\)|[\n\t]/g, " ") .replace(/(\s\s+)/g, " ") .replace(/^\s\s*/, "") - .replace(/\s\s*$/, "") + .replace(/\s\s*$/, ""), ); if (l) { var u = @@ -9715,12 +9722,14 @@ null == (c = t._meridiem) ? h : null != d.meridiemHour - ? d.meridiemHour(h, c) - : null != d.isPM - ? ((g = d.isPM(c)) && h < 12 && (h += 12), - g || 12 !== h || (h = 0), - h) - : h)), + ? d.meridiemHour(h, c) + : null != d.isPM + ? ((g = d.isPM(c)) && + h < 12 && + (h += 12), + g || 12 !== h || (h = 0), + h) + : h)), me(t), fe(t); } else Ce(t); @@ -9741,97 +9750,115 @@ : (l(e) ? (t._d = e) : a(i) - ? (function (t) { - var e, i, n, a, r; - if (0 === t._f.length) - return ( - (f(t).invalidFormat = !0), - void (t._d = new Date(NaN)) - ); - for (a = 0; a < t._f.length; a++) - (r = 0), - (e = v({}, t)), - null != t._useUTC && - (e._useUTC = t._useUTC), - (e._f = t._f[a]), - Pe(e), - g(e) && - ((r += - f(e).charsLeftOver), - (r += - 10 * - f(e).unusedTokens - .length), - (f(e).score = r), - (null == n || r < n) && - ((n = r), (i = e))); - h(t, i || e); - })(t) - : i - ? Pe(t) - : (function (t) { - var e = t._i; - o(e) - ? (t._d = new Date(n.now())) - : l(e) - ? (t._d = new Date(e.valueOf())) - : "string" == typeof e - ? (function (t) { - var e = ke.exec(t._i); - null === e - ? (we(t), - !1 === t._isValid && - (delete t._isValid, - Ce(t), - !1 === - t._isValid && - (delete t._isValid, - n.createFromInputFallback( - t - )))) - : (t._d = new Date( - +e[1] - )); - })(t) - : a(e) - ? ((t._a = u( - e.slice(0), - function (t) { - return parseInt(t, 10); - } - )), - me(t)) - : r(e) - ? (function (t) { - if (!t._d) { - var e = L(t._i); - (t._a = u( - [ - e.year, - e.month, - e.day || e.date, - e.hour, - e.minute, - e.second, - e.millisecond, - ], - function (t) { - return ( - t && - parseInt( - t, - 10 - ) - ); - } - )), - me(t); - } - })(t) - : s(e) - ? (t._d = new Date(e)) - : n.createFromInputFallback(t); - })(t), + ? (function (t) { + var e, i, n, a, r; + if (0 === t._f.length) + return ( + (f(t).invalidFormat = !0), + void (t._d = new Date(NaN)) + ); + for (a = 0; a < t._f.length; a++) + (r = 0), + (e = v({}, t)), + null != t._useUTC && + (e._useUTC = t._useUTC), + (e._f = t._f[a]), + Pe(e), + g(e) && + ((r += + f(e).charsLeftOver), + (r += + 10 * + f(e).unusedTokens + .length), + (f(e).score = r), + (null == n || r < n) && + ((n = r), (i = e))); + h(t, i || e); + })(t) + : i + ? Pe(t) + : (function (t) { + var e = t._i; + o(e) + ? (t._d = new Date(n.now())) + : l(e) + ? (t._d = new Date( + e.valueOf(), + )) + : "string" == typeof e + ? (function (t) { + var e = ke.exec( + t._i, + ); + null === e + ? (we(t), + !1 === + t._isValid && + (delete t._isValid, + Ce(t), + !1 === + t._isValid && + (delete t._isValid, + n.createFromInputFallback( + t, + )))) + : (t._d = + new Date( + +e[1], + )); + })(t) + : a(e) + ? ((t._a = u( + e.slice(0), + function (t) { + return parseInt( + t, + 10, + ); + }, + )), + me(t)) + : r(e) + ? (function (t) { + if (!t._d) { + var e = L( + t._i, + ); + (t._a = u( + [ + e.year, + e.month, + e.day || + e.date, + e.hour, + e.minute, + e.second, + e.millisecond, + ], + function ( + t, + ) { + return ( + t && + parseInt( + t, + 10, + ) + ); + }, + )), + me(t); + } + })(t) + : s(e) + ? (t._d = new Date( + e, + )) + : n.createFromInputFallback( + t, + ); + })(t), g(t) || (t._d = null), t)) ); @@ -9871,7 +9898,7 @@ "value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.", function (t) { t._d = new Date(t._i + (t._useUTC ? " UTC" : "")); - } + }, )), (n.ISO_8601 = function () {}), (n.RFC_2822 = function () {}); @@ -9884,7 +9911,7 @@ ? this : t : m(); - } + }, ), Fe = S( "moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/", @@ -9895,7 +9922,7 @@ ? this : t : m(); - } + }, ); function Re(t, e) { var i, n; @@ -10016,46 +10043,49 @@ Ye(t) ? (u = {ms: t._milliseconds, d: t._days, M: t._months}) : s(t) - ? ((u = {}), e ? (u[e] = t) : (u.milliseconds = t)) - : (h = Ue.exec(t)) - ? ((i = "-" === h[1] ? -1 : 1), - (u = { - y: 0, - d: k(h[yt]) * i, - h: k(h[bt]) * i, - m: k(h[xt]) * i, - s: k(h[_t]) * i, - ms: k(Ne(1e3 * h[kt])) * i, - })) - : (h = Ge.exec(t)) - ? ((i = "-" === h[1] ? -1 : 1), - (u = { - y: Ze(h[2], i), - M: Ze(h[3], i), - w: Ze(h[4], i), - d: Ze(h[5], i), - h: Ze(h[6], i), - m: Ze(h[7], i), - s: Ze(h[8], i), - })) - : null == u - ? (u = {}) - : "object" == typeof u && - ("from" in u || "to" in u) && - ((r = Ie(u.from)), - (o = Ie(u.to)), - (a = - r.isValid() && o.isValid() - ? ((o = Ee(o, r)), - r.isBefore(o) - ? (l = $e(r, o)) - : (((l = $e(o, r)).milliseconds = - -l.milliseconds), - (l.months = -l.months)), - l) - : {milliseconds: 0, months: 0}), - ((u = {}).ms = a.milliseconds), - (u.M = a.months)), + ? ((u = {}), e ? (u[e] = t) : (u.milliseconds = t)) + : (h = Ue.exec(t)) + ? ((i = "-" === h[1] ? -1 : 1), + (u = { + y: 0, + d: k(h[yt]) * i, + h: k(h[bt]) * i, + m: k(h[xt]) * i, + s: k(h[_t]) * i, + ms: k(Ne(1e3 * h[kt])) * i, + })) + : (h = Ge.exec(t)) + ? ((i = "-" === h[1] ? -1 : 1), + (u = { + y: Ze(h[2], i), + M: Ze(h[3], i), + w: Ze(h[4], i), + d: Ze(h[5], i), + h: Ze(h[6], i), + m: Ze(h[7], i), + s: Ze(h[8], i), + })) + : null == u + ? (u = {}) + : "object" == typeof u && + ("from" in u || "to" in u) && + ((r = Ie(u.from)), + (o = Ie(u.to)), + (a = + r.isValid() && o.isValid() + ? ((o = Ee(o, r)), + r.isBefore(o) + ? (l = $e(r, o)) + : (((l = $e( + o, + r, + )).milliseconds = + -l.milliseconds), + (l.months = -l.months)), + l) + : {milliseconds: 0, months: 0}), + ((u = {}).ms = a.milliseconds), + (u.M = a.months)), (n = new We(u)), Ye(t) && d(t, "_locale") && (n._locale = t._locale), n @@ -10087,7 +10117,7 @@ e + "(period, number) is deprecated. Please use moment()." + e + - "(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info." + "(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.", ), (a = i), (i = n), @@ -10140,7 +10170,7 @@ "moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.", function (t) { return void 0 === t ? this.localeData() : this.locale(t); - } + }, ); function ni() { return this._locale; @@ -10308,7 +10338,7 @@ r = n.calendarFormat(this, a) || "sameElse", o = e && (T(e[r]) ? e[r].call(this, i) : e[r]); return this.format( - o || this.localeData().calendar(r, this, Ie(i)) + o || this.localeData().calendar(r, this, Ie(i)), ); }), (bi.clone = function () { @@ -10369,7 +10399,7 @@ i( this.year(), this.month() - (this.month() % 3) + 3, - 1 + 1, ) - 1; break; case "month": @@ -10380,7 +10410,7 @@ i( this.year(), this.month(), - this.date() - this.weekday() + 7 + this.date() - this.weekday() + 7, ) - 1; break; case "isoWeek": @@ -10388,7 +10418,7 @@ i( this.year(), this.month(), - this.date() - (this.isoWeekday() - 1) + 7 + this.date() - (this.isoWeekday() - 1) + 7, ) - 1; break; case "day": @@ -10406,7 +10436,7 @@ (this._isUTC ? 0 : this.utcOffset() * ri), - oi + oi, ) - 1); break; @@ -10553,7 +10583,7 @@ e = i( this.year(), this.month() - (this.month() % 3), - 1 + 1, ); break; case "month": @@ -10563,14 +10593,14 @@ e = i( this.year(), this.month(), - this.date() - this.weekday() + this.date() - this.weekday(), ); break; case "isoWeek": e = i( this.year(), this.month(), - this.date() - (this.isoWeekday() - 1) + this.date() - (this.isoWeekday() - 1), ); break; case "day": @@ -10584,7 +10614,7 @@ (this._isUTC ? 0 : this.utcOffset() * ri), - oi + oi, )); break; case "minute": @@ -10632,22 +10662,23 @@ i, e ? "YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]" - : "YYYYYY-MM-DD[T]HH:mm:ss.SSSZ" + : "YYYYYY-MM-DD[T]HH:mm:ss.SSSZ", ) : T(Date.prototype.toISOString) - ? e - ? this.toDate().toISOString() - : new Date( - this.valueOf() + 60 * this.utcOffset() * 1e3 - ) - .toISOString() - .replace("Z", j(i, "Z")) - : j( - i, - e - ? "YYYY-MM-DD[T]HH:mm:ss.SSS[Z]" - : "YYYY-MM-DD[T]HH:mm:ss.SSSZ" - ); + ? e + ? this.toDate().toISOString() + : new Date( + this.valueOf() + + 60 * this.utcOffset() * 1e3, + ) + .toISOString() + .replace("Z", j(i, "Z")) + : j( + i, + e + ? "YYYY-MM-DD[T]HH:mm:ss.SSS[Z]" + : "YYYY-MM-DD[T]HH:mm:ss.SSSZ", + ); }), (bi.inspect = function () { if (!this.isValid()) @@ -10702,7 +10733,7 @@ this.week(), this.weekday(), this.localeData()._week.dow, - this.localeData()._week.doy + this.localeData()._week.doy, ); }), (bi.isoWeekYear = function (t) { @@ -10712,7 +10743,7 @@ this.isoWeek(), this.isoWeekday(), 1, - 4 + 4, ); }), (bi.quarter = bi.quarters = @@ -10754,11 +10785,11 @@ return "string" != typeof t ? t : isNaN(t) - ? "number" == - typeof (t = e.weekdaysParse(t)) - ? t - : null - : parseInt(t, 10); + ? "number" == + typeof (t = e.weekdaysParse(t)) + ? t + : null + : parseInt(t, 10); })(t, this.localeData())), this.add(t - e, "d")) : e; @@ -10775,8 +10806,8 @@ return "string" == typeof t ? e.weekdaysParse(t) % 7 || 7 : isNaN(t) - ? null - : t; + ? null + : t; })(t, this.localeData()); return this.day(this.day() % 7 ? e : e - 7); } @@ -10787,7 +10818,7 @@ Math.round( (this.clone().startOf("day") - this.clone().startOf("year")) / - 864e5 + 864e5, ) + 1; return null == t ? e : this.add(t - e, "d"); }), @@ -10869,15 +10900,15 @@ }), (bi.dates = S( "dates accessor is deprecated. Use date instead.", - fi + fi, )), (bi.months = S( "months accessor is deprecated. Use month instead", - Yt + Yt, )), (bi.years = S( "years accessor is deprecated. Use year instead", - Pt + Pt, )), (bi.zone = S( "moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/", @@ -10887,7 +10918,7 @@ this.utcOffset(t, e), this) : -this.utcOffset(); - } + }, )), (bi.isDSTShifted = S( "isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information", @@ -10900,7 +10931,7 @@ this.isValid() && w(t._a, e.toArray()) > 0; } else this._isDSTShifted = !1; return this._isDSTShifted; - } + }, )); var _i = I.prototype; function ki(t, e, i, n) { @@ -10944,7 +10975,7 @@ /MMMM|MM|DD|dddd/g, function (t) { return t.slice(1); - } + }, )), this._longDateFormat[t]); }), @@ -10973,7 +11004,7 @@ (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + "|" + - /\d{1,2}/.source + /\d{1,2}/.source, )); }), (_i.months = function (t, e) { @@ -10986,8 +11017,8 @@ : "standalone" ][t.month()] : a(this._months) - ? this._months - : this._months.standalone; + ? this._months + : this._months.standalone; }), (_i.monthsShort = function (t, e) { return t @@ -10997,8 +11028,8 @@ Ft.test(e) ? "format" : "standalone" ][t.month()] : a(this._monthsShort) - ? this._monthsShort - : this._monthsShort.standalone; + ? this._monthsShort + : this._monthsShort.standalone; }), (_i.monthsParse = function (t, e, i) { var n, a, r; @@ -11021,11 +11052,11 @@ (this._shortMonthsParse[n] = this.monthsShort( r, - "" + "", ).toLocaleLowerCase()), (this._longMonthsParse[n] = this.months( r, - "" + "", ).toLocaleLowerCase()); return i ? "MMM" === e @@ -11034,23 +11065,27 @@ ? a : null : -1 !== - (a = Ct.call(this._longMonthsParse, o)) - ? a - : null + (a = Ct.call(this._longMonthsParse, o)) + ? a + : null : "MMM" === e - ? -1 !== - (a = Ct.call(this._shortMonthsParse, o)) - ? a - : -1 !== + ? -1 !== + (a = Ct.call(this._shortMonthsParse, o)) + ? a + : -1 !== + (a = Ct.call( + this._longMonthsParse, + o, + )) + ? a + : null + : -1 !== (a = Ct.call(this._longMonthsParse, o)) ? a - : null - : -1 !== (a = Ct.call(this._longMonthsParse, o)) - ? a - : -1 !== - (a = Ct.call(this._shortMonthsParse, o)) - ? a - : null; + : -1 !== + (a = Ct.call(this._shortMonthsParse, o)) + ? a + : null; }.call(this, t, e, i); for ( this._monthsParse || @@ -11069,16 +11104,16 @@ "^" + this.months(a, "").replace(".", "") + "$", - "i" + "i", )), (this._shortMonthsParse[n] = new RegExp( "^" + this.monthsShort(a, "").replace( ".", - "" + "", ) + "$", - "i" + "i", ))), i || this._monthsParse[n] || @@ -11089,7 +11124,7 @@ this.monthsShort(a, "")), (this._monthsParse[n] = new RegExp( r.replace(".", ""), - "i" + "i", ))), i && "MMMM" === e && @@ -11146,22 +11181,22 @@ return !0 === t ? Gt(i, this._week.dow) : t - ? i[t.day()] - : i; + ? i[t.day()] + : i; }), (_i.weekdaysMin = function (t) { return !0 === t ? Gt(this._weekdaysMin, this._week.dow) : t - ? this._weekdaysMin[t.day()] - : this._weekdaysMin; + ? this._weekdaysMin[t.day()] + : this._weekdaysMin; }), (_i.weekdaysShort = function (t) { return !0 === t ? Gt(this._weekdaysShort, this._week.dow) : t - ? this._weekdaysShort[t.day()] - : this._weekdaysShort; + ? this._weekdaysShort[t.day()] + : this._weekdaysShort; }), (_i.weekdaysParse = function (t, e, i) { var n, a, r; @@ -11184,16 +11219,16 @@ (this._minWeekdaysParse[n] = this.weekdaysMin( r, - "" + "", ).toLocaleLowerCase()), (this._shortWeekdaysParse[n] = this.weekdaysShort( r, - "" + "", ).toLocaleLowerCase()), (this._weekdaysParse[n] = this.weekdays( r, - "" + "", ).toLocaleLowerCase()); return i ? "dddd" === e @@ -11202,44 +11237,66 @@ ? a : null : "ddd" === e - ? -1 !== - (a = Ct.call(this._shortWeekdaysParse, o)) + ? -1 !== + (a = Ct.call( + this._shortWeekdaysParse, + o, + )) + ? a + : null + : -1 !== + (a = Ct.call( + this._minWeekdaysParse, + o, + )) ? a : null - : -1 !== - (a = Ct.call(this._minWeekdaysParse, o)) - ? a - : null : "dddd" === e - ? -1 !== (a = Ct.call(this._weekdaysParse, o)) - ? a - : -1 !== + ? -1 !== (a = Ct.call(this._weekdaysParse, o)) + ? a + : -1 !== + (a = Ct.call( + this._shortWeekdaysParse, + o, + )) + ? a + : -1 !== + (a = Ct.call( + this._minWeekdaysParse, + o, + )) + ? a + : null + : "ddd" === e + ? -1 !== (a = Ct.call(this._shortWeekdaysParse, o)) - ? a - : -1 !== - (a = Ct.call(this._minWeekdaysParse, o)) - ? a - : null - : "ddd" === e - ? -1 !== - (a = Ct.call(this._shortWeekdaysParse, o)) - ? a - : -1 !== - (a = Ct.call(this._weekdaysParse, o)) - ? a + ? a + : -1 !== + (a = Ct.call( + this._weekdaysParse, + o, + )) + ? a + : -1 !== + (a = Ct.call( + this._minWeekdaysParse, + o, + )) + ? a + : null : -1 !== - (a = Ct.call(this._minWeekdaysParse, o)) - ? a - : null - : -1 !== - (a = Ct.call(this._minWeekdaysParse, o)) - ? a - : -1 !== (a = Ct.call(this._weekdaysParse, o)) - ? a - : -1 !== - (a = Ct.call(this._shortWeekdaysParse, o)) - ? a - : null; + (a = Ct.call(this._minWeekdaysParse, o)) + ? a + : -1 !== + (a = Ct.call(this._weekdaysParse, o)) + ? a + : -1 !== + (a = Ct.call( + this._shortWeekdaysParse, + o, + )) + ? a + : null; }.call(this, t, e, i); for ( this._weekdaysParse || @@ -11259,28 +11316,28 @@ "^" + this.weekdays(a, "").replace( ".", - "\\.?" + "\\.?", ) + "$", - "i" + "i", )), (this._shortWeekdaysParse[n] = new RegExp( "^" + this.weekdaysShort(a, "").replace( ".", - "\\.?" + "\\.?", ) + "$", - "i" + "i", )), (this._minWeekdaysParse[n] = new RegExp( "^" + this.weekdaysMin(a, "").replace( ".", - "\\.?" + "\\.?", ) + "$", - "i" + "i", ))), this._weekdaysParse[n] || ((r = @@ -11292,7 +11349,7 @@ this.weekdaysMin(a, "")), (this._weekdaysParse[n] = new RegExp( r.replace(".", ""), - "i" + "i", ))), i && "dddd" === e && @@ -11362,22 +11419,22 @@ 1 === k((t % 100) / 10) ? "th" : 1 === e - ? "st" - : 2 === e - ? "nd" - : 3 === e - ? "rd" - : "th"; + ? "st" + : 2 === e + ? "nd" + : 3 === e + ? "rd" + : "th"; return t + i; }, }), (n.lang = S( "moment.lang is deprecated. Use moment.locale instead.", - de + de, )), (n.langData = S( "moment.langData is deprecated. Use moment.localeData instead.", - ce + ce, )); var Si = Math.abs; function Di(t, e, i, n) { @@ -11636,7 +11693,7 @@ (Qi.localeData = ni), (Qi.toIsoString = S( "toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)", - Ji + Ji, )), (Qi.lang = ii), B("X", 0, 0, "unix"), @@ -11730,16 +11787,16 @@ return i < -6 ? "sameElse" : i < -1 - ? "lastWeek" - : i < 0 - ? "lastDay" - : i < 1 - ? "sameDay" - : i < 2 - ? "nextDay" - : i < 7 - ? "nextWeek" - : "sameElse"; + ? "lastWeek" + : i < 0 + ? "lastDay" + : i < 1 + ? "sameDay" + : i < 2 + ? "nextDay" + : i < 7 + ? "nextWeek" + : "sameElse"; }), (n.prototype = bi), (n.HTML5_FMT = { @@ -11809,7 +11866,7 @@ return nn(t); }, } - : {} + : {}, ), ot._set("global", {plugins: {filler: {propagate: !0}}}); var rn = { @@ -11872,12 +11929,12 @@ ("start" === a ? (r = void 0 === i.scaleBottom ? n.bottom : i.scaleBottom) : "end" === a - ? (r = void 0 === i.scaleTop ? n.top : i.scaleTop) - : void 0 !== i.scaleZero - ? (r = i.scaleZero) - : n.getBasePosition - ? (r = n.getBasePosition()) - : n.getBasePixel && (r = n.getBasePixel()), + ? (r = void 0 === i.scaleTop ? n.top : i.scaleTop) + : void 0 !== i.scaleZero + ? (r = i.scaleZero) + : n.getBasePosition + ? (r = n.getBasePosition()) + : n.getBasePixel && (r = n.getBasePixel()), null != r) ) { if (void 0 !== r.x && void 0 !== r.y) return r; @@ -12050,7 +12107,7 @@ e.push( '
  • ' + '">', ), t.data.datasets[i].label && e.push(t.data.datasets[i].label), e.push("
  • "); @@ -12229,7 +12286,7 @@ (u.lineCap = gn(n.lineCap, r.borderCapStyle)), (u.lineDashOffset = gn( n.lineDashOffset, - r.borderDashOffset + r.borderDashOffset, )), (u.lineJoin = gn(n.lineJoin, r.borderJoinStyle)), (u.lineWidth = o), @@ -12420,7 +12477,7 @@ f = t.right; (e.fillStyle = ut.valueOrDefault( i.fontColor, - ot.global.defaultFontColor + ot.global.defaultFontColor, )), (e.font = o.string), t.isHorizontal() @@ -12639,10 +12696,10 @@ !i || i.model.skip ? (n.mK = n.deltaK) : !a || a.model.skip - ? (n.mK = i.deltaK) - : this.sign(i.deltaK) !== this.sign(n.deltaK) - ? (n.mK = 0) - : (n.mK = (i.deltaK + n.deltaK) / 2); + ? (n.mK = i.deltaK) + : this.sign(i.deltaK) !== this.sign(n.deltaK) + ? (n.mK = 0) + : (n.mK = (i.deltaK + n.deltaK) / 2); } for (e = 0; e < h - 1; ++e) (n = d[e]), @@ -12678,8 +12735,8 @@ ? t[0] : t[e + 1] : e >= t.length - 1 - ? t[t.length - 1] - : t[e + 1]; + ? t[t.length - 1] + : t[e + 1]; }), (ut.previousItem = function (t, e, i) { return i @@ -12687,8 +12744,8 @@ ? t[t.length - 1] : t[e - 1] : e <= 0 - ? t[0] - : t[e - 1]; + ? t[0] + : t[e - 1]; }), (ut.niceNum = function (t, e) { var i = Math.floor(ut.log10(t)), @@ -12698,17 +12755,17 @@ ? n < 1.5 ? 1 : n < 3 - ? 2 - : n < 7 - ? 5 - : 10 + ? 2 + : n < 7 + ? 5 + : 10 : n <= 1 - ? 1 - : n <= 2 - ? 2 - : n <= 5 - ? 5 - : 10) * Math.pow(10, i) + ? 1 + : n <= 2 + ? 2 + : n <= 5 + ? 5 + : 10) * Math.pow(10, i) ); }), (ut.requestAnimFrame = @@ -12742,10 +12799,10 @@ f = o.bottom - o.top - u - h; return { x: (i = Math.round( - (((i - o.left - l) / c) * r.width) / e.currentDevicePixelRatio + (((i - o.left - l) / c) * r.width) / e.currentDevicePixelRatio, )), y: (n = Math.round( - (((n - o.top - u) / f) * r.height) / e.currentDevicePixelRatio + (((n - o.top - u) / f) * r.height) / e.currentDevicePixelRatio, )), }; }), @@ -12911,10 +12968,10 @@ e, ai.helpers.merge(i || {}, { type: t.charAt(0).toLowerCase() + t.slice(1), - }) + }), ); }; - } + }, ), Cn ); @@ -12935,8 +12992,8 @@ "object" == typeof exports && "undefined" != typeof module ? (module.exports = e(require("chart.js"))) : "function" == typeof define && define.amd - ? define(["chart.js"], e) - : (f["chartjs-plugin-colorschemes"] = e(f.Chart)); + ? define(["chart.js"], e) + : (f["chartjs-plugin-colorschemes"] = e(f.Chart)); })(this, function (f) { "use strict"; f = f && f.hasOwnProperty("default") ? f.default : f; @@ -16813,15 +16870,14 @@ case "doughnut": case "pie": void 0 === f.backgroundColor && - ((f.backgroundColor = f.data.map(function ( - f, - e - ) { - return ( - (r = e % b), - a[c.reverse ? b - r - 1 : r] - ); - })), + ((f.backgroundColor = f.data.map( + function (f, e) { + return ( + (r = e % b), + a[c.reverse ? b - r - 1 : r] + ); + }, + )), (f.colorschemes.backgroundColor = !0)); break; default: @@ -17324,7 +17380,7 @@ else { if ("object" != typeof e) throw new Error( - "obj does not seem to be an array or an iterable object" + "obj does not seem to be an array or an iterable object", ); for (var n in e) e.hasOwnProperty(n) && r(e[n], n, e); } @@ -17412,12 +17468,12 @@ e.slice(i[p].wholeMatch.start, i[p].wholeMatch.end), e.slice(i[p].match.start, i[p].match.end), e.slice(i[p].left.start, i[p].left.end), - e.slice(i[p].right.start, i[p].right.end) - ) + e.slice(i[p].right.start, i[p].right.end), + ), ), p < u - 1 && d.push( - e.slice(i[p].wholeMatch.end, i[p + 1].wholeMatch.start) + e.slice(i[p].wholeMatch.end, i[p + 1].wholeMatch.start), ); i[u - 1].wholeMatch.end < e.length && d.push(e.slice(i[u - 1].wholeMatch.end)), @@ -18677,7 +18733,7 @@ console.warn( "DEPRECATION WARNING: " + e + - " is an old extension that uses a deprecated loading method.Please inform the developer that the extension should be updated!" + " is an old extension that uses a deprecated loading method.Please inform the developer that the extension should be updated!", ), void (function (e, t) { "function" == typeof e && (e = e(new a.Converter())); @@ -18694,7 +18750,7 @@ break; default: throw Error( - "Extension loader error: Type unrecognized!!!" + "Extension loader error: Type unrecognized!!!", ); } })(a.extensions[e], e) @@ -18703,7 +18759,7 @@ throw Error( 'Extension "' + e + - '" could not be loaded. It was either not found or is not a valid extension.' + '" could not be loaded. It was either not found or is not a valid extension.', ); e = s[e]; } @@ -18728,13 +18784,13 @@ throw Error( "Invalid argument in converter.listen() method: name must be a string, but " + typeof e + - " given" + " given", ); if ("function" != typeof r) throw Error( "Invalid argument in converter.listen() method: callback must be a function, but " + typeof r + - " given" + " given", ); p.hasOwnProperty(e) || (p[e] = []), p[e].push(r); } @@ -18751,7 +18807,7 @@ throw Error( "Converter expects the passed parameter to be an object, but " + typeof e + - " was passed instead." + " was passed instead.", ); for (var n in e) e.hasOwnProperty(n) && (c[n] = e[n]); c.extensions && a.helper.forEach(c.extensions, t); @@ -18833,7 +18889,7 @@ .join(" ")), (a.nodeValue = a.nodeValue.replace( /(\s)+/g, - "$1" + "$1", ))) : (e.removeChild(a), --r) : 1 === a.nodeType && t(a); @@ -18847,7 +18903,7 @@ ) { if (!window || !window.document) throw new Error( - "HTMLParser is undefined. If in a webworker or nodejs environment, you need to provide a WHATWG DOM and HTML such as JSDOM" + "HTMLParser is undefined. If in a webworker or nodejs environment, you need to provide a WHATWG DOM and HTML such as JSDOM", ); r = window.document; } @@ -18867,13 +18923,13 @@ var s = r[n].firstChild.innerHTML.trim(), o = r[n].firstChild.getAttribute( - "data-language" + "data-language", ) || ""; if ("" === o) for ( var i = r[n].firstChild.className.split( - " " + " ", ), l = 0; l < i.length; @@ -18979,7 +19035,7 @@ ']*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g, - n + n, )), (e = e.replace( /\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]??(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g, - n + n, )), (e = e.replace(/\[([^\[\]]+)]()()()()()/g, n)), r.ghMentions && @@ -19019,7 +19075,7 @@ if ("\\" === n) return t + s; if (!a.helper.isString(r.ghMentionsLink)) throw new Error( - "ghMentionsLink option must be a string" + "ghMentionsLink option must be a string", ); var i = r.ghMentionsLink.replace(/\{u}/g, o), l = ""; @@ -19029,7 +19085,7 @@ ' rel="noopener noreferrer" target="¨E95Eblank"'), t + '" + s + "" ); - } + }, )), (e = t.converter._dispatch("anchors.after", e, r, t)) ); @@ -19047,7 +19103,7 @@ return function (r, t, n, s, o, i, l) { var c = (n = n.replace( a.helper.regexes.asteriskDashAndColon, - a.helper.escapeCharactersCallback + a.helper.escapeCharactersCallback, )), u = "", d = "", @@ -19136,7 +19192,7 @@ a.subParser("hashBlock")( "
    \n" + e + "\n
    ", r, - t + t, ) ); })), @@ -19163,7 +19219,7 @@ (o = "
    " + o + l + "
    "), a.subParser("hashBlock")(o, r, t) + i ); - } + }, )), (e = e.replace(/¨0/, "")), (e = t.converter._dispatch("codeBlocks.after", e, r, t)) @@ -19185,7 +19241,7 @@ (i = n + "" + i + ""), (i = a.subParser("hashHTMLSpans")(i, r, t)) ); - } + }, )), (e = t.converter._dispatch("codeSpans.after", e, r, t)) ); @@ -19289,7 +19345,7 @@ return a.helper.emojis.hasOwnProperty(r) ? a.helper.emojis[r] : e; - } + }, )), (e = t.converter._dispatch("emoji.after", e, r, t)) ); @@ -19312,7 +19368,7 @@ (e = e.replace(/\\(\\)/g, a.helper.escapeCharactersCallback)), (e = e.replace( /\\([`*_{}\[\]()>#+.!~=|-])/g, - a.helper.escapeCharactersCallback + a.helper.escapeCharactersCallback, )), (e = t.converter._dispatch("encodeBackslashEscapes.after", e, r, t)) ); @@ -19336,7 +19392,7 @@ "escapeSpecialCharsWithinTagAttributes.before", e, r, - t + t, )).replace(/<\/?[a-z\d_:-]+(?:[\s]+[\s\S]+?)?>/gi, function (e) { return e .replace(/(.)<\/?code>(?=.)/g, "$1`") @@ -19347,15 +19403,15 @@ function (e) { return e.replace( /([\\`*_~=|])/g, - a.helper.escapeCharactersCallback + a.helper.escapeCharactersCallback, ); - } + }, )), (e = t.converter._dispatch( "escapeSpecialCharsWithinTagAttributes.after", e, r, - t + t, )) ); }), @@ -19385,7 +19441,7 @@ (t.ghCodeBlocks.push({text: e, codeblock: o}) - 1) + "G\n\n" ); - } + }, )), (e = e.replace(/¨0/, "")), t.converter._dispatch("githubCodeBlocks.after", e, r, t)) @@ -19412,7 +19468,7 @@ }, "]*>", "", - "gim" + "gim", )), (e = t.converter._dispatch("hashCodeTags.after", e, r, t)) ); @@ -19497,7 +19553,7 @@ return ( (e = e.replace( /(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, - a.subParser("hashElement")(e, r, t) + a.subParser("hashElement")(e, r, t), )), (e = a.helper.replaceRecursiveRegExp( e, @@ -19506,11 +19562,11 @@ }, "^ {0,3}\x3c!--", "--\x3e", - "gm" + "gm", )), (e = e.replace( /(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, - a.subParser("hashElement")(e, r, t) + a.subParser("hashElement")(e, r, t), )), (e = t.converter._dispatch("hashHTMLBlocks.after", e, r, t)) ); @@ -19569,7 +19625,7 @@ }, "^ {0,3}]*>\\s*]*>", "^ {0,3}\\s*", - "gim" + "gim", )), (e = t.converter._dispatch("hashPreCodeTags.after", e, r, t)) ); @@ -19587,8 +19643,8 @@ (s = a.helper.isString(r.prefixHeaderId) ? r.prefixHeaderId : !0 === r.prefixHeaderId - ? "section-" - : ""), + ? "section-" + : ""), r.rawPrefixHeaderId || (n = s + n), (n = r.ghCompatibleHeaderId ? n @@ -19599,14 +19655,14 @@ .replace(/[&+$,\/:;=?@"#{}|^¨~\[\]`\\*)(%.!'<>]/g, "") .toLowerCase() : r.rawHeaderId - ? n - .replace(/ /g, "-") - .replace(/&/g, "&") - .replace(/¨T/g, "¨") - .replace(/¨D/g, "$") - .replace(/["']/g, "-") - .toLowerCase() - : n.replace(/[^\w]/g, "").toLowerCase()), + ? n + .replace(/ /g, "-") + .replace(/&/g, "&") + .replace(/¨T/g, "¨") + .replace(/¨D/g, "$") + .replace(/["']/g, "-") + .toLowerCase() + : n.replace(/[^\w]/g, "").toLowerCase()), r.rawPrefixHeaderId && (n = s + n), t.hashLinkCounts[n] ? (n = n + "-" + t.hashLinkCounts[n]++) @@ -19692,13 +19748,13 @@ .replace(/"/g, """) .replace( a.helper.regexes.asteriskDashAndColon, - a.helper.escapeCharactersCallback + a.helper.escapeCharactersCallback, ); var h = '' +
                                 r +
@@ -19712,7 +19768,7 @@
                                             .replace(/?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g, function (e, r, t, a, s, o, i, l) { return (a = a.replace(/\s/g, "")), n(e, r, t, a, s, o, 0, l); - } + }, )), (e = e.replace( /!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g, - n + n, )), (e = e.replace( /!\[([^\]]*?)][ \t]*()\([ \t]??(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g, - n + n, )), (e = e.replace(/!\[([^\[\]]+)]()()()()()/g, n)), (e = t.converter._dispatch("images.after", e, r, t)) @@ -19774,17 +19830,17 @@ /([^*]|^)\B\*\*\*(\S[\s\S]*?)\*\*\*\B(?!\*)/g, function (e, r, t) { return a(t, r + "", ""); - } + }, )).replace( /([^*]|^)\B\*\*(\S[\s\S]*?)\*\*\B(?!\*)/g, function (e, r, t) { return a(t, r + "", ""); - } + }, )).replace( /([^*]|^)\B\*(\S[\s\S]*?)\*\B(?!\*)/g, function (e, r, t) { return a(t, r + "", ""); - } + }, ) : (e = (e = e.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g, function (e, r) { return /\S$/.test(r) @@ -19827,19 +19883,19 @@ /^([-*+]|\d\.)[ \t]+[\S\n ]*/g, function (e) { return "¨A" + e; - } + }, )), n || d.search(/\n{2,}/) > -1 ? ((d = a.subParser("githubCodeBlocks")(d, r, t)), (d = a.subParser("blockGamut")(d, r, t))) : ((d = (d = a.subParser("lists")(d, r, t)).replace( /\n$/, - "" + "", )), (d = (d = a.subParser("hashHTMLBlocks")( d, r, - t + t, )).replace(/\n\n+/g, "\n\n")), (d = o ? a.subParser("paragraphs")(d, r, t) @@ -19910,13 +19966,13 @@ /^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm, function (e, r, t) { return o(r, t.search(/[*+-]/g) > -1 ? "ul" : "ol", !0); - } + }, ) : e.replace( /(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm, function (e, r, t, a) { return o(t, a.search(/[*+-]/g) > -1 ? "ul" : "ol", !1); - } + }, )), (e = e.replace(/¨0/, "")), (e = t.converter._dispatch("lists.after", e, r, t)) @@ -19928,7 +19984,7 @@ (t.metadata.raw = e), (e = (e = e.replace(/&/g, "&").replace(/"/g, """)).replace( /\n {4}/g, - " " + " ", )).replace(/^([\S ]+): +([\s\S]+?)$/gm, function (e, r, a) { return (t.metadata.parsed[r] = a), ""; }); @@ -19939,13 +19995,13 @@ /^\s*«««+(\S*?)\n([\s\S]+?)\n»»»+\n/, function (e, r, t) { return a(t), "¨M"; - } + }, )), (e = e.replace( /^\s*---+(\S*?)\n([\s\S]+?)\n---+\n/, function (e, r, n) { return r && (t.metadata.format = r), a(n), "¨M"; - } + }, )), (e = e.replace(/¨M/g, "")), (e = t.converter._dispatch("metadata.after", e, r, t))) @@ -19967,7 +20023,7 @@ "paragraphs.before", e, r, - t + t, )).replace(/^\n+/g, "")).replace(/\n+$/g, "")).split(/\n{2,}/g), s = [], o = n.length, @@ -19981,7 +20037,7 @@ : l.search(/\S/) >= 0 && ((l = (l = a.subParser("spanGamut")(l, r, t)).replace( /^([ \t]*)/g, - "

    " + "

    ", )), (l += "

    "), s.push(l)); @@ -19994,8 +20050,8 @@ "K" === p ? t.gHtmlBlocks[h] : d - ? a.subParser("encodeCode")(t.ghCodeBlocks[h].text, r, t) - : t.ghCodeBlocks[h].codeblock).replace(/\$/g, "$$")), + ? a.subParser("encodeCode")(t.ghCodeBlocks[h].text, r, t) + : t.ghCodeBlocks[h].codeblock).replace(/\$/g, "$$")), (u = u.replace(/(\n\n)?¨(K|G)\d+\2(\n\n)?/, c)), /^]*>\s*]*>/.test(u) && (d = !0); } @@ -20050,7 +20106,7 @@ "strikethrough.before", e, r, - t + t, )).replace(/(?:~){2}([\s\S]+?)(?:~){2}/g, function (e, n) { return (function (e) { return ( @@ -20085,11 +20141,11 @@ return ( (e = (e += "¨0").replace( /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm, - n + n, )), (e = e.replace( /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm, - n + n, )), (e = e.replace(/¨0/, "")) ); @@ -20100,10 +20156,10 @@ return /^:[ \t]*--*$/.test(e) ? ' style="text-align:left;"' : /^--*[ \t]*:[ \t]*$/.test(e) - ? ' style="text-align:right;"' - : /^:[ \t]*--*[ \t]*:$/.test(e) - ? ' style="text-align:center;"' - : ""; + ? ' style="text-align:right;"' + : /^:[ \t]*--*[ \t]*:$/.test(e) + ? ' style="text-align:center;"' + : ""; } function s(e, n) { var s = ""; @@ -20141,7 +20197,7 @@ d.push( l[i].split("|").map(function (e) { return e.trim(); - }) + }), ); if (c.length < u.length) return e; for (i = 0; i < u.length; ++i) h.push(n(u[i])); @@ -20173,11 +20229,11 @@ (e = e.replace(/\\(\|)/g, a.helper.escapeCharactersCallback)), (e = e.replace( /^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm, - i + i, )), (e = e.replace( /^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm, - i + i, )), (e = t.converter._dispatch("tables.after", e, r, t)) ); @@ -20526,8 +20582,8 @@ return a; }) : "undefined" != typeof module && module.exports - ? (module.exports = a) - : (this.showdown = a); + ? (module.exports = a) + : (this.showdown = a); }).call(this); //# sourceMappingURL=showdown.min.js.map @@ -20605,8 +20661,8 @@ h.push( c.tr.apply( this, - b.substring(1, b.length - 1).split("|") - ) + b.substring(1, b.length - 1).split("|"), + ), ), (b = f[++e]); h.push(""), @@ -20686,7 +20742,7 @@ ENVIRONMENT_IS_SHELL = true; } else { throw new Error( - "Module['ENVIRONMENT'] value is not valid. must be one of: WEB|WORKER|NODE|SHELL." + "Module['ENVIRONMENT'] value is not valid. must be one of: WEB|WORKER|NODE|SHELL.", ); } } else { @@ -20835,8 +20891,8 @@ typeof console !== "undefined" ? console.log : typeof print !== "undefined" - ? print - : null; + ? print + : null; Module["printErr"] = typeof printErr !== "undefined" ? printErr @@ -20931,7 +20987,7 @@ var func = Module["_" + ident]; assert( func, - "Cannot call unknown function " + ident + ", make sure it is exported" + "Cannot call unknown function " + ident + ", make sure it is exported", ); return func; } @@ -21004,12 +21060,13 @@ ? tempDouble > +0 ? (Math_min( +Math_floor(tempDouble / +4294967296), - +4294967295 + +4294967295, ) | 0) >>> 0 : ~~+Math_ceil( - (tempDouble - +(~~tempDouble >>> 0)) / +4294967296 + (tempDouble - +(~~tempDouble >>> 0)) / + +4294967296, ) >>> 0 : 0), ]), @@ -21048,7 +21105,7 @@ staticAlloc, dynamicAlloc, ][allocator === undefined ? ALLOC_STATIC : allocator]( - Math.max(size, singleType ? 1 : types.length) + Math.max(size, singleType ? 1 : types.length), ); } if (zeroinit) { @@ -21114,7 +21171,7 @@ while (length > 0) { curr = String.fromCharCode.apply( String, - HEAPU8.subarray(ptr, ptr + Math.min(length, MAX_CHUNK)) + HEAPU8.subarray(ptr, ptr + Math.min(length, MAX_CHUNK)), ); ret = ret ? ret + curr : curr; ptr += MAX_CHUNK; @@ -21180,7 +21237,7 @@ var ch = u0 - 65536; str += String.fromCharCode( 55296 | (ch >> 10), - 56320 | (ch & 1023) + 56320 | (ch & 1023), ); } } @@ -21327,7 +21384,7 @@ abort( "Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value " + TOTAL_MEMORY + - ", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime but prevents some optimizations, (3) set Module.TOTAL_MEMORY to a higher value before the program runs, or (4) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 " + ", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime but prevents some optimizations, (3) set Module.TOTAL_MEMORY to a higher value before the program runs, or (4) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ", ); } function enlargeMemory() { @@ -21341,7 +21398,7 @@ TOTAL_MEMORY + "! (TOTAL_STACK=" + TOTAL_STACK + - ")" + ")", ); if (Module["buffer"]) { buffer = Module["buffer"]; @@ -21541,7 +21598,7 @@ filename ? Pointer_stringify(filename) : "unknown filename", line, func ? Pointer_stringify(func) : "unknown function", - ] + ], ); } function _emscripten_get_now() { @@ -21861,7 +21918,7 @@ path.split("/").filter(function (p) { return !!p; }), - !isAbsolute + !isAbsolute, ).join("/"); if (!path && !isAbsolute) { path = "."; @@ -21906,7 +21963,7 @@ var path = i >= 0 ? arguments[i] : FS.cwd(); if (typeof path !== "string") { throw new TypeError( - "Arguments to path.resolve must be strings" + "Arguments to path.resolve must be strings", ); } else if (!path) { return ""; @@ -21918,7 +21975,7 @@ resolvedPath.split("/").filter(function (p) { return !!p; }), - !resolvedAbsolute + !resolvedAbsolute, ).join("/"); return (resolvedAbsolute ? "/" : "") + resolvedPath || "."; }, @@ -22213,7 +22270,7 @@ newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2 : 1.125)) | - 0 + 0, ); if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); var oldContents = node.contents; @@ -22237,7 +22294,7 @@ node.contents = new Uint8Array(new ArrayBuffer(newSize)); if (oldContents) { node.contents.set( - oldContents.subarray(0, Math.min(newSize, node.usedBytes)) + oldContents.subarray(0, Math.min(newSize, node.usedBytes)), ); } node.usedBytes = newSize; @@ -22349,7 +22406,7 @@ if (size > 8 && contents.subarray) { buffer.set( contents.subarray(position, position + size), - offset + offset, ); } else { for (var i = 0; i < size; i++) @@ -22368,14 +22425,14 @@ return length; } else if (node.usedBytes === 0 && position === 0) { node.contents = new Uint8Array( - buffer.subarray(offset, offset + length) + buffer.subarray(offset, offset + length), ); node.usedBytes = length; return length; } else if (position + length <= node.usedBytes) { node.contents.set( buffer.subarray(offset, offset + length), - position + position, ); return length; } @@ -22384,7 +22441,7 @@ if (node.contents.subarray && buffer.subarray) node.contents.set( buffer.subarray(offset, offset + length), - position + position, ); else { for (var i = 0; i < length; i++) { @@ -22412,7 +22469,7 @@ MEMFS.expandFileStorage(stream.node, offset + length); stream.node.usedBytes = Math.max( stream.node.usedBytes, - offset + length + offset + length, ); }, mmap: function (stream, buffer, offset, length, position, prot, flags) { @@ -22434,13 +22491,13 @@ if (contents.subarray) { contents = contents.subarray( position, - position + length + position + length, ); } else { contents = Array.prototype.slice.call( contents, position, - position + length + position + length, ); } } @@ -22466,7 +22523,7 @@ 0, length, offset, - false + false, ); return 0; }, @@ -22565,7 +22622,7 @@ if (FS.isDir(stat.mode)) { check.push.apply( check, - FS.readdir(path).filter(isRealDir).map(toAbsolute(path)) + FS.readdir(path).filter(isRealDir).map(toAbsolute(path)), ); } entries[path] = {timestamp: stat.mtime}; @@ -22579,7 +22636,7 @@ try { var transaction = db.transaction( [IDBFS.DB_STORE_NAME], - "readonly" + "readonly", ); transaction.onerror = function (e) { callback(this.error); @@ -22956,7 +23013,7 @@ path = fs.readlinkSync(path); path = NODEJS_PATH.relative( NODEJS_PATH.resolve(node.mount.opts.root), - path + path, ); return path; } catch (e) { @@ -22972,7 +23029,7 @@ if (FS.isFile(stream.node.mode)) { stream.nfd = fs.openSync( path, - NODEFS.flagsForNode(stream.flags) + NODEFS.flagsForNode(stream.flags), ); } } catch (e) { @@ -22998,7 +23055,7 @@ NODEFS.bufferFrom(buffer.buffer), offset, length, - position + position, ); } catch (e) { throw new FS.ErrnoError(ERRNO_CODES[e.code]); @@ -23011,7 +23068,7 @@ NODEFS.bufferFrom(buffer.buffer), offset, length, - position + position, ); } catch (e) { throw new FS.ErrnoError(ERRNO_CODES[e.code]); @@ -23057,7 +23114,7 @@ parent, parts[i], WORKERFS.DIR_MODE, - 0 + 0, ); } parent = createdParents[curr]; @@ -23077,9 +23134,9 @@ WORKERFS.FILE_MODE, 0, file, - file.lastModifiedDate + file.lastModifiedDate, ); - } + }, ); (mount.opts["blobs"] || []).forEach(function (obj) { WORKERFS.createNode( @@ -23087,7 +23144,7 @@ base(obj["name"]), WORKERFS.FILE_MODE, 0, - obj["data"] + obj["data"], ); }); (mount.opts["packages"] || []).forEach(function (pack) { @@ -23098,7 +23155,7 @@ base(name), WORKERFS.FILE_MODE, 0, - pack["blob"].slice(file.start, file.end) + pack["blob"].slice(file.start, file.end), ); }); }); @@ -23248,7 +23305,7 @@ path.split("/").filter(function (p) { return !!p; }), - false + false, ); var current = FS.root; var current_path = "/"; @@ -23270,7 +23327,7 @@ var link = FS.readlink(current_path); current_path = PATH.resolve( PATH.dirname(current_path), - link + link, ); var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count, @@ -23624,7 +23681,7 @@ console.log( "warning: " + FS.syncFSRequests + - " FS.syncfs operations in flight at once, probably just doing extra work" + " FS.syncfs operations in flight at once, probably just doing extra work", ); } var mounts = FS.getMounts(FS.root.mount); @@ -23854,7 +23911,7 @@ "', '" + new_path + "') threw an exception: " + - e.message + e.message, ); } FS.hashRemoveNode(old_node); @@ -23875,7 +23932,7 @@ "', '" + new_path + "') threw an exception: " + - e.message + e.message, ); } }, @@ -23903,7 +23960,7 @@ "FS.trackingDelegate['willDeletePath']('" + path + "') threw an exception: " + - e.message + e.message, ); } parent.node_ops.rmdir(parent, name); @@ -23916,7 +23973,7 @@ "FS.trackingDelegate['onDeletePath']('" + path + "') threw an exception: " + - e.message + e.message, ); } }, @@ -23952,7 +24009,7 @@ "FS.trackingDelegate['willDeletePath']('" + path + "') threw an exception: " + - e.message + e.message, ); } parent.node_ops.unlink(parent, name); @@ -23965,7 +24022,7 @@ "FS.trackingDelegate['onDeletePath']('" + path + "') threw an exception: " + - e.message + e.message, ); } }, @@ -23980,7 +24037,7 @@ } return PATH.resolve( FS.getPath(link.parent), - link.node_ops.readlink(link) + link.node_ops.readlink(link), ); }, stat: function (path, dontFollow) { @@ -24150,7 +24207,7 @@ error: false, }, fd_start, - fd_end + fd_end, ); if (stream.stream_ops.open) { stream.stream_ops.open(stream); @@ -24178,7 +24235,7 @@ "FS.trackingDelegate['onOpenFile']('" + path + "', flags) threw an exception: " + - e.message + e.message, ); } return stream; @@ -24227,7 +24284,7 @@ buffer, offset, length, - position + position, ); if (!seeking) stream.position += bytesRead; return bytesRead; @@ -24260,7 +24317,7 @@ offset, length, position, - canOwn + canOwn, ); if (!seeking) stream.position += bytesWritten; try { @@ -24271,7 +24328,7 @@ "FS.trackingDelegate['onWriteToFile']('" + path + "') threw an exception: " + - e.message + e.message, ); } return bytesWritten; @@ -24305,7 +24362,7 @@ length, position, prot, - flags + flags, ); }, msync: function (stream, buffer, offset, length, mmapFlags) { @@ -24317,7 +24374,7 @@ buffer, offset, length, - mmapFlags + mmapFlags, ); }, munmap: function (stream) { @@ -24434,7 +24491,7 @@ "/proc/self", "fd", 16384 | 511, - 73 + 73, ); node.node_ops = { lookup: function (parent, name) { @@ -24459,7 +24516,7 @@ }, }, {}, - "/proc/self/fd" + "/proc/self/fd", ); }, createStandardStreams: function () { @@ -24483,12 +24540,12 @@ var stdout = FS.open("/dev/stdout", "w"); assert( stdout.fd === 1, - "invalid handle for stdout (" + stdout.fd + ")" + "invalid handle for stdout (" + stdout.fd + ")", ); var stderr = FS.open("/dev/stderr", "w"); assert( stderr.fd === 2, - "invalid handle for stderr (" + stderr.fd + ")" + "invalid handle for stderr (" + stderr.fd + ")", ); }, ensureErrnoError: function () { @@ -24536,7 +24593,7 @@ init: function (input, output, error) { assert( !FS.init.initialized, - "FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)" + "FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)", ); FS.init.initialized = true; FS.ensureErrnoError(); @@ -24619,7 +24676,7 @@ createFolder: function (parent, name, canRead, canWrite) { var path = PATH.join2( typeof parent === "string" ? parent : FS.getPath(parent), - name + name, ); var mode = FS.getMode(canRead, canWrite); return FS.mkdir(path, mode); @@ -24641,7 +24698,7 @@ createFile: function (parent, name, properties, canRead, canWrite) { var path = PATH.join2( typeof parent === "string" ? parent : FS.getPath(parent), - name + name, ); var mode = FS.getMode(canRead, canWrite); return FS.create(path, mode); @@ -24650,7 +24707,7 @@ var path = name ? PATH.join2( typeof parent === "string" ? parent : FS.getPath(parent), - name + name, ) : parent; var mode = FS.getMode(canRead, canWrite); @@ -24673,7 +24730,7 @@ createDevice: function (parent, name, input, output) { var path = PATH.join2( typeof parent === "string" ? parent : FS.getPath(parent), - name + name, ); var mode = FS.getMode(!!input, !!output); if (!FS.createDevice.major) FS.createDevice.major = 64; @@ -24727,7 +24784,7 @@ createLink: function (parent, name, target, canRead, canWrite) { var path = PATH.join2( typeof parent === "string" ? parent : FS.getPath(parent), - name + name, ); return FS.symlink(target, path); }, @@ -24737,13 +24794,13 @@ var success = true; if (typeof XMLHttpRequest !== "undefined") { throw new Error( - "Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread." + "Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.", ); } else if (Module["read"]) { try { obj.contents = intArrayFromString( Module["read"](obj.url), - true + true, ); obj.usedBytes = obj.contents.length; } catch (e) { @@ -24784,10 +24841,10 @@ ) ) throw new Error( - "Couldn't load " + url + ". Status: " + xhr.status + "Couldn't load " + url + ". Status: " + xhr.status, ); var datalength = Number( - xhr.getResponseHeader("Content-length") + xhr.getResponseHeader("Content-length"), ); var header; var hasByteServing = @@ -24805,26 +24862,26 @@ from + ", " + to + - ") or no bytes requested!" + ") or no bytes requested!", ); if (to > datalength - 1) throw new Error( "only " + datalength + - " bytes available! programmer error!" + " bytes available! programmer error!", ); var xhr = new XMLHttpRequest(); xhr.open("GET", url, false); if (datalength !== chunkSize) xhr.setRequestHeader( "Range", - "bytes=" + from + "-" + to + "bytes=" + from + "-" + to, ); if (typeof Uint8Array != "undefined") xhr.responseType = "arraybuffer"; if (xhr.overrideMimeType) { xhr.overrideMimeType( - "text/plain; charset=x-user-defined" + "text/plain; charset=x-user-defined", ); } xhr.send(null); @@ -24835,7 +24892,7 @@ ) ) throw new Error( - "Couldn't load " + url + ". Status: " + xhr.status + "Couldn't load " + url + ". Status: " + xhr.status, ); if (xhr.response !== undefined) { return new Uint8Array(xhr.response || []); @@ -24860,7 +24917,7 @@ datalength = this.getter(0).length; chunkSize = datalength; console.log( - "LazyFiles on gzip forces download of the whole file when length is accessed" + "LazyFiles on gzip forces download of the whole file when length is accessed", ); } this._length = datalength; @@ -24923,7 +24980,7 @@ buffer, offset, length, - position + position, ) { if (!FS.forceLoadFile(node)) { throw new FS.ErrnoError(ERRNO_CODES.EIO); @@ -24956,7 +25013,7 @@ onerror, dontCreateFile, canOwn, - preFinish + preFinish, ) { Browser.init(); var fullname = name ? PATH.resolve(PATH.join2(parent, name)) : parent; @@ -24971,7 +25028,7 @@ byteArray, canRead, canWrite, - canOwn + canOwn, ); } if (onload) onload(); @@ -24997,7 +25054,7 @@ function (byteArray) { processData(byteArray); }, - onerror + onerror, ); } else { processData(url); @@ -25044,7 +25101,7 @@ paths.forEach(function (path) { var putRequest = files.put( FS.analyzePath(path).object.contents, - path + path, ); putRequest.onsuccess = function putRequest_onsuccess() { ok++; @@ -25074,7 +25131,7 @@ try { var transaction = db.transaction( [FS.DB_STORE_NAME], - "readonly" + "readonly", ); } catch (e) { onerror(e); @@ -25100,7 +25157,7 @@ getRequest.result, true, true, - true + true, ); ok++; if (ok + fail == total) finish(); @@ -25605,7 +25662,7 @@ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, ], "i8", - ALLOC_STATIC + ALLOC_STATIC, ); function ___unlock() {} function __exit(status) { @@ -25781,7 +25838,7 @@ stringy, u8array, 0, - u8array.length + u8array.length, ); if (dontAddNull) u8array.length = numBytesWritten; return u8array; @@ -25800,7 +25857,7 @@ String.fromCharCode(chr) + ") at offset " + i + - " not in 0x00-0xFF." + " not in 0x00-0xFF.", ); } chr &= 255; @@ -25966,7 +26023,7 @@ a7, a8, a9, - a10 + a10, ); } catch (e) { if (typeof e !== "number" && e !== "longjmp") throw e; @@ -26542,7 +26599,7 @@ 34, t | 0, k | 0, - j | 0 + j | 0, ) | 0; h = o; o = 0; @@ -26557,12 +26614,12 @@ 2 ] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!d) bb( h | 0, - p | 0 + p | 0, ); z = p; } else d = -1; @@ -26584,7 +26641,7 @@ 64, t | 0, k | 0, - j | 0 + j | 0, ); f = o; o = 0; @@ -26599,12 +26656,12 @@ 2 ] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!h) bb( f | 0, - p | 0 + p | 0, ); z = p; } else h = -1; @@ -26628,7 +26685,7 @@ 34, t | 0, j | 0, - k | 0 + k | 0, ) | 0; h = o; o = 0; @@ -26643,12 +26700,12 @@ 2 ] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!d) bb( h | 0, - p | 0 + p | 0, ); z = p; } else d = -1; @@ -26670,7 +26727,7 @@ 64, t | 0, j | 0, - k | 0 + k | 0, ); f = o; o = 0; @@ -26685,12 +26742,12 @@ 2 ] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!h) bb( f | 0, - p | 0 + p | 0, ); z = p; } else h = -1; @@ -26723,7 +26780,7 @@ V6( c[f >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!h) bb(f | 0, p | 0); z = p; @@ -26753,7 +26810,7 @@ V6( c[f >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!h) bb(f | 0, p | 0); z = p; @@ -26824,7 +26881,7 @@ V6( c[e >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!f) bb(e | 0, p | 0); z = p; @@ -26858,7 +26915,7 @@ V6( c[e >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!f) bb(e | 0, p | 0); @@ -26926,7 +26983,7 @@ m | 0, g | 0, i | 0, - d | 0 + d | 0, ); e = o; o = 0; @@ -26938,7 +26995,7 @@ V6( c[e >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!f) bb(e | 0, p | 0); @@ -26968,7 +27025,7 @@ V6( c[e >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!f) bb(e | 0, p | 0); @@ -26997,7 +27054,7 @@ V6( c[e >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!f) bb(e | 0, p | 0); @@ -27021,7 +27078,7 @@ 35, m | 0, i | 0, - d | 0 + d | 0, ) | 0; f = o; o = 0; @@ -27033,7 +27090,7 @@ V6( c[f >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!g) bb(f | 0, p | 0); z = p; @@ -27064,7 +27121,7 @@ s | 0, m | 0, d | 0, - i | 0 + i | 0, ); f = o; o = 0; @@ -27076,7 +27133,7 @@ V6( c[f >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!g) bb(f | 0, p | 0); z = p; @@ -27108,7 +27165,7 @@ V6( c[e >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!f) bb(e | 0, p | 0); z = p; @@ -27138,7 +27195,7 @@ V6( c[e >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!f) bb(e | 0, p | 0); z = p; @@ -27156,7 +27213,7 @@ V6( c[f >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!g) bb(f | 0, p | 0); z = p; @@ -27175,7 +27232,7 @@ V6( c[f >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!g) bb(f | 0, p | 0); @@ -27188,7 +27245,7 @@ 66, n | 0, j | 0, - r | 0 + r | 0, ); e = o; o = 0; @@ -27201,12 +27258,12 @@ c[e >> 2] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!f) bb( e | 0, - p | 0 + p | 0, ); z = p; } else f = -1; @@ -27216,7 +27273,7 @@ 189296, 1, w | 0, - x | 0 + x | 0, ) | 0; x = z; o = 0; @@ -27233,12 +27290,12 @@ 2 ] | 0, w | 0, - x | 0 + x | 0, ) | 0; if (!f) bb( e | 0, - p | 0 + p | 0, ); z = p; } else f = -1; @@ -27621,7 +27678,7 @@ nR( k, ((c[e >> 2] & 1) | 0) == 0 ? e : f, - (d + ((g * 40) | 0)) | 0 + (d + ((g * 40) | 0)) | 0, ); d = c[k >> 2] | 0; e = c[i >> 2] | 0; @@ -27884,7 +27941,7 @@ p, p, n ? (o > w ? 4 : 2) : x > k ? 1 : 3, - 0 + 0, ); n = (z + 2) | 0; g = (B + ((q * 56) | 0)) | 0; @@ -28402,11 +28459,11 @@ if (0 > 1) { P = KB( - c[((((c[z >> 2] & 3) | 0) == 3 ? z : A) + 40) >> 2] | 0 + c[((((c[z >> 2] & 3) | 0) == 3 ? z : A) + 40) >> 2] | 0, ) | 0; Q = KB( - c[((((c[z >> 2] & 3) | 0) == 2 ? z : C) + 40) >> 2] | 0 + c[((((c[z >> 2] & 3) | 0) == 2 ? z : C) + 40) >> 2] | 0, ) | 0; c[M >> 2] = P; c[(M + 4) >> 2] = Q; @@ -28417,7 +28474,7 @@ c[((((c[z >> 2] & 3) | 0) == 2 ? z : C) + 40) >> 2] | 0, m, D, - 25680 + 25680, ); if ( (!w ? ((H = c[((c[B >> 2] | 0) + 96) >> 2] | 0), H | 0) : 0) @@ -28810,7 +28867,7 @@ c[ ((c[a >> 2] | 0) + (c[d >> 2] << 2)) >> 2 - ] | 0 + ] | 0, ); T4(153599, i) | 0; d = nb[c[e >> 2] & 63](e, d, 8) | 0; @@ -28979,7 +29036,7 @@ g, 0, a, - b + b, ); VQ( c[s >> 2] | 0, @@ -28987,7 +29044,7 @@ f, 1, d, - b + b, ); break b; } @@ -28998,7 +29055,7 @@ 0, g, a, - b + b, ); VQ( c[o >> 2] | 0, @@ -29006,7 +29063,7 @@ 1, f, d, - b + b, ); break b; } @@ -29017,7 +29074,7 @@ 0, g, a, - b + b, ); VQ( c[o >> 2] | 0, @@ -29025,7 +29082,7 @@ 1, f, d, - b + b, ); break b; } @@ -29135,7 +29192,7 @@ (sR( c[(i + 32) >> 2] | 0, c[(b + 40) >> 2] | 0, - c[(d + 40) >> 2] | 0 + c[(d + 40) >> 2] | 0, ) | 0) != 0; @@ -29145,7 +29202,7 @@ qR( b, c[(m + 40) >> 2] | 0, - c[(l + 40) >> 2] | 0 + c[(l + 40) >> 2] | 0, ); i = j; break; @@ -29153,7 +29210,7 @@ qR( b, c[(l + 40) >> 2] | 0, - c[(m + 40) >> 2] | 0 + c[(m + 40) >> 2] | 0, ); i = j; break; @@ -29173,7 +29230,7 @@ (sR( c[(i + 32) >> 2] | 0, c[(b + 40) >> 2] | 0, - c[(d + 40) >> 2] | 0 + c[(d + 40) >> 2] | 0, ) | 0) != 0; @@ -29183,7 +29240,7 @@ qR( b, c[(l + 40) >> 2] | 0, - c[(m + 40) >> 2] | 0 + c[(m + 40) >> 2] | 0, ); i = j; break; @@ -29191,7 +29248,7 @@ qR( b, c[(m + 40) >> 2] | 0, - c[(l + 40) >> 2] | 0 + c[(l + 40) >> 2] | 0, ); i = j; break; @@ -29214,7 +29271,7 @@ (sR( c[(i + 32) >> 2] | 0, c[(b + 40) >> 2] | 0, - c[(d + 40) >> 2] | 0 + c[(d + 40) >> 2] | 0, ) | 0) != 0; @@ -29224,7 +29281,7 @@ qR( b, c[(l + 40) >> 2] | 0, - c[(m + 40) >> 2] | 0 + c[(m + 40) >> 2] | 0, ); i = j; break; @@ -29232,7 +29289,7 @@ qR( b, c[(m + 40) >> 2] | 0, - c[(l + 40) >> 2] | 0 + c[(l + 40) >> 2] | 0, ); i = j; break; @@ -29252,7 +29309,7 @@ (sR( c[(i + 32) >> 2] | 0, c[(b + 40) >> 2] | 0, - c[(d + 40) >> 2] | 0 + c[(d + 40) >> 2] | 0, ) | 0) != 0; @@ -29262,7 +29319,7 @@ qR( b, c[(m + 40) >> 2] | 0, - c[(l + 40) >> 2] | 0 + c[(l + 40) >> 2] | 0, ); i = j; break; @@ -29270,7 +29327,7 @@ qR( b, c[(l + 40) >> 2] | 0, - c[(m + 40) >> 2] | 0 + c[(m + 40) >> 2] | 0, ); i = j; break; @@ -29312,7 +29369,7 @@ ((a[b >> 0] | 0) == 0 ? (f + 20) | 0 : (f + 24) | 0) >> 2 ] | 0, - b + b, ) | 0) + 32) >> @@ -29427,8 +29484,8 @@ ((d | 0) == (c[(b + 32) >> 2] | 0) ? 0 : (d | 0) == (e | 0) - ? 1 - : -1) | 0 + ? 1 + : -1) | 0 ); } else { d = c[(a + 32) >> 2] | 0; @@ -29436,8 +29493,8 @@ (d | 0) == (c[(b + 36) >> 2] | 0) ? 0 : (d | 0) == (e | 0) - ? 1 - : -1; + ? 1 + : -1; break; } if (!(j == i)) @@ -29448,7 +29505,7 @@ c[(b + 32) >> 2] | 0, c[(a + 32) >> 2] | 0, d, - e + e, ) | 0; return (o ? d : (0 - d) | 0) | 0; } else { @@ -29458,7 +29515,7 @@ c[(a + 32) >> 2] | 0, c[(b + 32) >> 2] | 0, d, - e + e, ) | 0; b = o ? b : (0 - b) | 0; break; @@ -29590,7 +29647,7 @@ c[(a + 36) >> 2] | 0, c[(b + 36) >> 2] | 0, d, - e + e, ) | 0; break; } else { @@ -29600,7 +29657,7 @@ c[(b + 36) >> 2] | 0, c[(a + 36) >> 2] | 0, d, - e + e, ) | 0)) | 0; @@ -30473,7 +30530,7 @@ a, c[((c[((c[a >> 2] | 0) + 308) >> 2] | 0) + (b << 2)) >> 2] | 0, b, - c[(a + 156) >> 2] | 0 + c[(a + 156) >> 2] | 0, ); } return; @@ -32022,11 +32079,11 @@ : (d + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0, 2, 141101, - 0 + 0, ) | 0; if (!b) break a; c[e >> 2] = EK(RA(d, b) | 0, d) | 0; @@ -32258,11 +32315,11 @@ GF( h, 1, - KB(c[((((c[b >> 2] & 3) | 0) == 3 ? b : k) + 40) >> 2] | 0) | 0 + KB(c[((((c[b >> 2] & 3) | 0) == 3 ? b : k) + 40) >> 2] | 0) | 0, ); k = (xB( - xC(c[((((c[b >> 2] & 3) | 0) == 3 ? b : k) + 40) >> 2] | 0) | 0 + xC(c[((((c[b >> 2] & 3) | 0) == 3 ? b : k) + 40) >> 2] | 0) | 0, ) | 0) != 0; @@ -32271,13 +32328,13 @@ GF( h, 4, - KB(c[((((c[b >> 2] & 3) | 0) == 2 ? b : k) + 40) >> 2] | 0) | 0 + KB(c[((((c[b >> 2] & 3) | 0) == 2 ? b : k) + 40) >> 2] | 0) | 0, ); c[(a + 596) >> 2] = 7; i = (a + 604) | 0; j = yC( - xC(c[((((c[b >> 2] & 3) | 0) == 2 ? b : k) + 40) >> 2] | 0) | 0 + xC(c[((((c[b >> 2] & 3) | 0) == 2 ? b : k) + 40) >> 2] | 0) | 0, ) | 0; g = 7; d = 0; @@ -32315,17 +32372,17 @@ xC(c[((((c[b >> 2] & 3) | 0) == 2 ? b : k) + 40) >> 2] | 0) | 0, 2, 141055, - 0 + 0, ) | 0; if (!d) { d = GA( xC( - c[((((c[b >> 2] & 3) | 0) == 2 ? b : k) + 40) >> 2] | 0 + c[((((c[b >> 2] & 3) | 0) == 2 ? b : k) + 40) >> 2] | 0, ) | 0, 2, 111477, - 0 + 0, ) | 0; if (d | 0) l = 13; } else l = 13; @@ -32354,7 +32411,7 @@ e, c[(f + ((b * 20) | 0) + 8) >> 2] | 0, g, - (f + ((b * 20) | 0)) | 0 + (f + ((b * 20) | 0)) | 0, ) | 0; b = (b + 1) | 0; } @@ -33890,7 +33947,7 @@ y, z, A, - B + B, ); a[e >> 0] = ~~+h[y >> 3] * 255; a[(e + 1) >> 0] = ~~+h[z >> 3] * 255; @@ -34515,7 +34572,7 @@ (+h[(a + 248) >> 3] + e)) * d) * (c - b) - ) + ), ) ); } @@ -35189,7 +35246,7 @@ c[(f + 12) >> 2] | 0, c[(f + 8) >> 2] | 0, h, - c[(m + 8) >> 2] | 0 + c[(m + 8) >> 2] | 0, ) | 0; if (i) oA(t); do @@ -35209,7 +35266,7 @@ c[o >> 2] | 0, c[p >> 2] | 0, c[q >> 2] | 0, - c[r >> 2] | 0 + c[r >> 2] | 0, ); } while (0); @@ -36232,7 +36289,7 @@ +( O( ((a[o >> 0] | 0) + -1) | 0, - ((d & 65535) + -1) | 0 + ((d & 65535) + -1) | 0, ) | 0 )) / +(d & 65535) @@ -36248,7 +36305,7 @@ +( O( ((a[o >> 0] | 0) + -1) | 0, - ((j & 65535) + -1) | 0 + ((j & 65535) + -1) | 0, ) | 0 )) / +(j & 65535) @@ -36359,7 +36416,7 @@ tC( d, uU(((e[(g + 80) >> 1] | 0) + (e[o >> 1] | 0)) | 0) | 0, - 0 + 0, ) | 0; wU(d, n, o, ~~+h[(g + 64) >> 3]); o = (g + 86) | 0; @@ -36368,7 +36425,7 @@ tC( b, uU(((e[(g + 82) >> 1] | 0) + (e[o >> 1] | 0)) | 0) | 0, - 0 + 0, ) | 0; wU(b, n, o, ~~+h[(g + 72) >> 3]); f = (f + 4) | 0; @@ -36611,7 +36668,7 @@ ((((c[b >> 2] & 3) | 0) == 2 ? b : (b + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; break; } @@ -36665,7 +36722,7 @@ HU( c[((c[(m + 16) >> 2] | 0) + 144) >> 2] | 0, c[f >> 2] | 0, - k + k, ); n = c[f >> 2] | 0; q = +h[(n + 24) >> 3] * 0.5; @@ -36717,8 +36774,8 @@ : (b + 48) | 0) + 40) >> 2 - ] | 0 - ) | 0 + ] | 0, + ) | 0, ) | 0; e = (b + -48) | 0; nA( @@ -36726,8 +36783,8 @@ KB( c[ ((((c[b >> 2] & 3) | 0) == 2 ? b : e) + 40) >> 2 - ] | 0 - ) | 0 + ] | 0, + ) | 0, ) | 0; if ( !( @@ -36737,8 +36794,8 @@ ((((c[b >> 2] & 3) | 0) == 2 ? b : e) + 40) >> 2 - ] | 0 - ) | 0 + ] | 0, + ) | 0, ) | 0 ) ) { @@ -37240,7 +37297,7 @@ ((m * 56) | 0)) >> 2 ] | 0, - c[E >> 2] | 0 + c[E >> 2] | 0, ) | 0; i = ((c[e >> 2] | 0) + ((n * 24) | 0)) | 0; g = c[((c[i >> 2] | 0) + ((m * 56) | 0) + 4) >> 2] | 0; @@ -37270,7 +37327,7 @@ ((c[((c[e >> 2] | 0) + ((n * 24) | 0)) >> 2] | 0) + ((m * 56) | 0)) >> 2 - ] | 0 + ] | 0, ); i = c[e >> 2] | 0; r = c[(i + ((n * 24) | 0)) >> 2] | 0; @@ -37569,7 +37626,7 @@ x = b[t >> 1] & 768; KU( c[j >> 2] | 0, - (x & 1023) == 512 ? 108 : (x & 1023) == 256 ? 114 : 110 + (x & 1023) == 512 ? 108 : (x & 1023) == 256 ? 114 : 110, ); } } @@ -37743,7 +37800,7 @@ HU( c[((c[(f + 16) >> 2] | 0) + 144) >> 2] | 0, c[n >> 2] | 0, - j + j, ); i = c[n >> 2] | 0; n = 0; @@ -38134,7 +38191,7 @@ 0 ] | 0 ? k - : l + : l, ); b = c[n >> 2] | 0; } @@ -39371,7 +39428,7 @@ c[n >> 2] | 0, C, k, - 2 + 2, ) | 0; g = l; l = 2; @@ -39389,7 +39446,7 @@ c[i >> 2] | 0, C, k, - 2 + 2, ) | 0; g = l; i = n; @@ -39438,7 +39495,7 @@ c[i >> 2] | 0, C, k, - 2 + 2, ) | 0; d = uV( @@ -39451,7 +39508,7 @@ c[A >> 2] | 0, C, k, - 1 + 1, ) | 0; d = uV( @@ -39464,7 +39521,7 @@ c[u >> 2] | 0, C, k, - 2 + 2, ) | 0; g = l; i = E; @@ -39483,7 +39540,7 @@ c[u >> 2] | 0, C, k, - 2 + 2, ) | 0; d = uV( @@ -39496,7 +39553,7 @@ c[E >> 2] | 0, C, k, - 1 + 1, ) | 0; d = uV( @@ -39509,7 +39566,7 @@ c[i >> 2] | 0, C, k, - 2 + 2, ) | 0; g = l; i = A; @@ -39522,13 +39579,13 @@ +( +h[x >> 3] - +h[(e + ((m * 56) | 0) + 24) >> 3] - ) + ), ) <= 1.0e-7 ? +B( +( +h[w >> 3] - +h[(e + ((m * 56) | 0) + 16) >> 3] - ) + ), ) <= 1.0e-7 : 0 ) { @@ -39547,7 +39604,7 @@ c[i >> 2] | 0, C, k, - 2 + 2, ) | 0; d = uV( @@ -39560,7 +39617,7 @@ c[A >> 2] | 0, C, k, - 1 + 1, ) | 0; d = uV( @@ -39573,7 +39630,7 @@ c[u >> 2] | 0, C, k, - 2 + 2, ) | 0; g = l; i = (f + ((C * 72) | 0) + 52) | 0; @@ -39592,7 +39649,7 @@ c[u >> 2] | 0, C, k, - 2 + 2, ) | 0; d = uV( @@ -39605,7 +39662,7 @@ c[A >> 2] | 0, C, k, - 1 + 1, ) | 0; d = uV( @@ -39619,7 +39676,7 @@ 0, C, k, - 1 + 1, ) | 0; g = l; l = 2; @@ -39641,7 +39698,7 @@ c[u >> 2] | 0, C, k, - 2 + 2, ) | 0; d = uV( @@ -39654,7 +39711,7 @@ c[(f + ((C * 72) | 0) + 52) >> 2] | 0, C, k, - 1 + 1, ) | 0; d = uV( @@ -39667,7 +39724,7 @@ c[A >> 2] | 0, C, k, - 1 + 1, ) | 0; g = l; l = 2; @@ -39685,7 +39742,7 @@ c[i >> 2] | 0, C, k, - 2 + 2, ) | 0; d = uV( @@ -39698,7 +39755,7 @@ c[A >> 2] | 0, C, k, - 1 + 1, ) | 0; d = uV( @@ -39711,7 +39768,7 @@ c[(f + ((C * 72) | 0) + 52) >> 2] | 0, C, k, - 1 + 1, ) | 0; g = l; i = u; @@ -39733,13 +39790,13 @@ +( +h[t >> 3] - +h[(e + ((n * 56) | 0) + 8) >> 3] - ) + ), ) <= 1.0e-7 ? +B( +( +h[v >> 3] - +h[(e + ((n * 56) | 0)) >> 3] - ) + ), ) <= 1.0e-7 : 0 ) { @@ -39757,7 +39814,7 @@ c[A >> 2] | 0, C, k, - 1 + 1, ) | 0; d = uV( @@ -39770,7 +39827,7 @@ c[i >> 2] | 0, C, k, - 2 + 2, ) | 0; d = uV( @@ -39783,7 +39840,7 @@ c[(f + ((C * 72) | 0) + 44) >> 2] | 0, C, k, - 2 + 2, ) | 0; g = l; i = G; @@ -39802,7 +39859,7 @@ c[(f + ((C * 72) | 0) + 44) >> 2] | 0, C, k, - 2 + 2, ) | 0; d = uV( @@ -39815,7 +39872,7 @@ c[G >> 2] | 0, C, k, - 1 + 1, ) | 0; d = uV( @@ -39828,7 +39885,7 @@ c[i >> 2] | 0, C, k, - 2 + 2, ) | 0; g = l; i = A; @@ -39862,7 +39919,7 @@ c[(f + ((C * 72) | 0) + 44) >> 2] | 0, C, k, - 2 + 2, ) | 0; d = uV(a, b, d, e, f, l, c[i >> 2] | 0, C, k, 2) | @@ -39890,7 +39947,7 @@ c[(f + ((C * 72) | 0) + 44) >> 2] | 0, C, k, - 2 + 2, ) | 0; g = l; i = G; @@ -39909,7 +39966,7 @@ +( +h[v >> 3] - +h[(e + ((n * 56) | 0)) >> 3] - ) + ), ) <= 1.0e-7 : 0 ) @@ -39918,12 +39975,12 @@ +( +h[x >> 3] - +h[(e + ((J * 56) | 0) + 8) >> 3] - ) + ), ) <= 1.0e-7) : 0 ) ? +B( - +(+h[w >> 3] - +h[(e + ((J * 56) | 0)) >> 3]) + +(+h[w >> 3] - +h[(e + ((J * 56) | 0)) >> 3]), ) <= 1.0e-7 : 0 ) @@ -39943,7 +40000,7 @@ c[(f + ((C * 72) | 0) + 44) >> 2] | 0, C, k, - 2 + 2, ) | 0; d = uV( @@ -39956,7 +40013,7 @@ c[(f + ((C * 72) | 0) + 52) >> 2] | 0, C, k, - 1 + 1, ) | 0; g = l; i = A; @@ -39975,7 +40032,7 @@ c[(f + ((C * 72) | 0) + 52) >> 2] | 0, C, k, - 1 + 1, ) | 0; d = uV(a, b, d, e, f, g, c[A >> 2] | 0, C, k, 1) | @@ -39998,7 +40055,7 @@ +( +h[v >> 3] - +h[(e + ((j * 56) | 0) + 16) >> 3] - ) + ), ) <= 1.0e-7 : 0 ) @@ -40006,7 +40063,7 @@ +( +h[x >> 3] - +h[(e + ((n * 56) | 0) + 24) >> 3] - ) + ), ) <= 1.0e-7 : 0 ) @@ -40014,7 +40071,7 @@ +( +h[w >> 3] - +h[(e + ((n * 56) | 0) + 16) >> 3] - ) + ), ) <= 1.0e-7 : 0 ) { @@ -40036,7 +40093,7 @@ c[(f + ((C * 72) | 0) + 44) >> 2] | 0, C, k, - 2 + 2, ) | 0; d = uV( @@ -40049,7 +40106,7 @@ c[(f + ((C * 72) | 0) + 52) >> 2] | 0, C, k, - 1 + 1, ) | 0; g = l; i = A; @@ -40068,7 +40125,7 @@ c[(f + ((C * 72) | 0) + 52) >> 2] | 0, C, k, - 1 + 1, ) | 0; d = uV(a, b, d, e, f, g, c[A >> 2] | 0, C, k, 1) | @@ -40095,7 +40152,7 @@ c[(f + ((C * 72) | 0) + 44) >> 2] | 0, C, k, - 2 + 2, ) | 0; i = (f + ((C * 72) | 0) + 52) | 0; l = 1; @@ -41485,7 +41542,7 @@ +( g - +h[(e + ((i * 56) | 0) + 24) >> 3] - ) + ), ) <= 1.0e-7 ? ((p = +h[a >> 3]), +B( @@ -41497,7 +41554,7 @@ 16) >> 3 ] - ) + ), ) <= 1.0e-7) : 0 ) { @@ -43853,7 +43910,8 @@ f = (f + 52) | 0; e = UY( - wK(d[((c[((c[f >> 2] | 0) + 16) >> 2] | 0) + 115) >> 0] | 0) | 0 + wK(d[((c[((c[f >> 2] | 0) + 16) >> 2] | 0) + 115) >> 0] | 0) | + 0, ) | 0; c[47407] = e; h_(e, c[((c[((c[f >> 2] | 0) + 16) >> 2] | 0) + 144) >> 2] | 0); @@ -46461,7 +46519,7 @@ +h[(a + 24) >> 3] - +h[m >> 3] * j, +h[(a + 40) >> 3] - +h[n >> 3] * j, +h[(a + 56) >> 3] - +h[o >> 3] * j, - s + s, ); h[s >> 3] = j * p - q + +h[s >> 3]; e = BY(s, t) | 0; @@ -46482,7 +46540,7 @@ +h[m >> 3], +h[n >> 3], +h[o >> 3], - s + s, ), (q = (+h[s >> 3] - @@ -46505,7 +46563,7 @@ +h[(a + 16) >> 3], +h[(a + 32) >> 3], +h[(a + 48) >> 3], - s + s, ); h[s >> 3] = +h[s >> 3] - p; r = BY(s, t) | 0; @@ -46533,7 +46591,7 @@ +h[n >> 3], +h[o >> 3], +h[e >> 3], - s + s, ), (p = (+h[s >> 3] - @@ -46556,7 +46614,7 @@ +h[(a + 24) >> 3], +h[(a + 40) >> 3], +h[(a + 56) >> 3], - s + s, ); h[s >> 3] = +h[s >> 3] - q; i = BY(s, k) | 0; @@ -47092,7 +47150,7 @@ 36, u | 0, c[c[(e + (c[47507] << 2)) >> 2] >> 2] | 0, - c[k >> 2] | 0 + c[k >> 2] | 0, ) | 0; g = o; o = 0; @@ -47129,7 +47187,7 @@ 2 ] >> 2 ] | 0, - c[m >> 2] | 0 + c[m >> 2] | 0, ) | 0; g = o; o = 0; @@ -47507,7 +47565,7 @@ vY( c[(a + (e << 2)) >> 2] | 0, c[(a + (d << 2)) >> 2] | 0, - c[(a + (g << 2)) >> 2] | 0 + c[(a + (g << 2)) >> 2] | 0, ); b = (b + -1) | 0; while (1) { @@ -47605,7 +47663,7 @@ c[ c[(f + ((a * 52) | 0) + 4 + (e << 4) + 4) >> 2] >> 2 ] | 0, - b + b, ) | 0) != 2) & @@ -47741,7 +47799,7 @@ (lY( c[c[(g + (d << 2)) >> 2] >> 2] | 0, c[c[(g + (b << 2)) >> 2] >> 2] | 0, - c[a >> 2] | 0 + c[a >> 2] | 0, ) | 0) == 1 @@ -47762,7 +47820,7 @@ (lY( c[c[(g + (d << 2)) >> 2] >> 2] | 0, c[c[(g + (b << 2)) >> 2] >> 2] | 0, - c[a >> 2] | 0 + c[a >> 2] | 0, ) | 0) == 2 @@ -47830,7 +47888,7 @@ i, j, c[c[(d + (g << 2)) >> 2] >> 2] | 0, - c[c[(d + (h << 2)) >> 2] >> 2] | 0 + c[c[(d + (h << 2)) >> 2] >> 2] | 0, ) | 0 ) { f = 0; @@ -49553,7 +49611,7 @@ nb[((a[(b + 232) >> 0] << 24) >> 24 ? 38 : 37) & 63]( (b + 148) | 0, (b + 144) | 0, - c[d >> 2] | 0 + c[d >> 2] | 0, ) | 0 ) ) @@ -49586,7 +49644,7 @@ i, c[h >> 2] | 0, f, - ((a[(b + 480) >> 0] | 0) == 0) & 1 + ((a[(b + 480) >> 0] | 0) == 0) & 1, ) | 0; l = g; return f | 0; @@ -49839,7 +49897,7 @@ c[z >> 2] | 0, c[x >> 2] | 0, c[C >> 2] | 0, - 1 + 1, ); c[z >> 2] = 0; mZ(y); @@ -49874,7 +49932,7 @@ o, n, c[Da >> 2] | 0, - p + p, ) | 0 ) ) { @@ -49888,7 +49946,7 @@ y, o, (n + i) | 0, - ((c[Da >> 2] | 0) + (0 - i)) | 0 + ((c[Da >> 2] | 0) + (0 - i)) | 0, ) | 0; if (!i) { i = 1; @@ -49916,7 +49974,7 @@ i, c[x >> 2] | 0, c[C >> 2] | 0, - 0 + 0, ); mZ(y); i = 0; @@ -49945,7 +50003,7 @@ 0, c[e >> 2] | 0, c[(h + 16) >> 2] | 0, - c[(h + 24) >> 2] | 0 + c[(h + 24) >> 2] | 0, ) | 0 ) ) { @@ -50063,7 +50121,7 @@ ? (h | 0) == 32 ? 142243 : 142253 - : 142255 + : 142255, ) | 0 ) ) { @@ -50092,7 +50150,7 @@ a[Q >> 0] | 0, a[S >> 0] | 0, 0, - b + b, ) | 0 ) ) { @@ -50152,7 +50210,7 @@ c[c[P >> 2] >> 2] | 0, c[R >> 2] | 0, 0, - ((h | 0) == 36) & 1 + ((h | 0) == 36) & 1, ); mZ(y); i = o; @@ -50171,7 +50229,7 @@ a[Q >> 0] | 0, (n + i) | 0, ((c[Da >> 2] | 0) + (0 - i)) | 0, - H + H, ) | 0; if (i | 0) { m = 280; @@ -50187,7 +50245,7 @@ a[Q >> 0] | 0, 0, e, - b + b, ) | 0 ) ) { @@ -50247,7 +50305,7 @@ c[c[P >> 2] >> 2] | 0, c[R >> 2] | 0, e, - ((h | 0) == 38) & 1 + ((h | 0) == 38) & 1, ); mZ(y); i = o; @@ -50263,7 +50321,7 @@ b, o, (n + i) | 0, - ((c[Da >> 2] | 0) + (0 - i)) | 0 + ((c[Da >> 2] | 0) + (0 - i)) | 0, ) | 0; e = c[F >> 2] | 0; f = c[U >> 2] | 0; @@ -50285,7 +50343,7 @@ c[Aa >> 2] | 0, 0, 0, - 0 + 0, ); e = 0; } @@ -50316,7 +50374,7 @@ y, o, (n + m) | 0, - ((c[Da >> 2] | 0) + (0 - m)) | 0 + ((c[Da >> 2] | 0) + (0 - m)) | 0, ) | 0; c[x >> 2] = m; if (!m) { @@ -50379,7 +50437,7 @@ c[(i + 20) >> 2] | 0, c[(i + 16) >> 2] | 0, c[(i + 24) >> 2] | 0, - 0 + 0, ); i = o; } else m = 275; @@ -50404,7 +50462,7 @@ c[(i + 20) >> 2] | 0, c[(i + 16) >> 2] | 0, c[(i + 24) >> 2] | 0, - c[(i + 28) >> 2] | 0 + c[(i + 28) >> 2] | 0, ); i = o; break c; @@ -50421,7 +50479,7 @@ c[(i + 20) >> 2] | 0, c[(i + 16) >> 2] | 0, c[(i + 24) >> 2] | 0, - c[(i + 28) >> 2] | 0 + c[(i + 28) >> 2] | 0, ); i = o; } else m = 275; @@ -50531,7 +50589,7 @@ o, n, c[Da >> 2] | 0, - p + p, ) | 0 ) ) { @@ -50547,7 +50605,7 @@ y, o, (n + i) | 0, - ((c[Da >> 2] | 0) + (0 - i)) | 0 + ((c[Da >> 2] | 0) + (0 - i)) | 0, ) | 0; if (!i) { i = 1; @@ -50569,7 +50627,7 @@ y, o, (n + i) | 0, - ((c[Da >> 2] | 0) + (0 - i)) | 0 + ((c[Da >> 2] | 0) + (0 - i)) | 0, ) | 0; if (!i) { i = 1; @@ -50582,7 +50640,7 @@ c[Z >> 2] | 0, c[Aa >> 2] | 0, i, - c[Y >> 2] | 0 + c[Y >> 2] | 0, ); i = 0; } else i = 1; @@ -50598,7 +50656,7 @@ c[Z >> 2] | 0, c[Aa >> 2] | 0, 0, - c[Y >> 2] | 0 + c[Y >> 2] | 0, ); i = 0; } else i = 1; @@ -50648,7 +50706,7 @@ i = lb[c[ia >> 2] & 127]( i, - c[aa >> 2] << 2 + c[aa >> 2] << 2, ) | 0; if (!i) { i = 1; @@ -50733,7 +50791,7 @@ H, o, (n + e) | 0, - ((c[Da >> 2] | 0) + (0 - e)) | 0 + ((c[Da >> 2] | 0) + (0 - e)) | 0, ) | 0; if (!e) { i = 1; @@ -50811,7 +50869,7 @@ 0, c[(f + 20) >> 2] | 0, c[(f + 16) >> 2] | 0, - c[(f + 24) >> 2] | 0 + c[(f + 24) >> 2] | 0, ) | 0) == 0; @@ -50886,7 +50944,7 @@ vb[c[ga >> 2] & 127]( c[ra >> 2] | 0, c[c[N >> 2] >> 2] | 0, - i + i, ); i = 0; } @@ -51034,7 +51092,7 @@ H, o, (n + e) | 0, - ((c[Da >> 2] | 0) + (0 - e)) | 0 + ((c[Da >> 2] | 0) + (0 - e)) | 0, ) | 0; c[((c[F >> 2] | 0) + 16) >> 2] = e; e = c[F >> 2] | 0; @@ -51104,7 +51162,7 @@ vb[c[ga >> 2] & 127]( c[ra >> 2] | 0, c[c[N >> 2] >> 2] | 0, - e + e, ); } a[ca >> 0] = 0; @@ -51121,7 +51179,7 @@ H, o, (n + e) | 0, - ((c[Da >> 2] | 0) + (0 - e)) | 0 + ((c[Da >> 2] | 0) + (0 - e)) | 0, ) | 0; if (!e) { i = 1; @@ -51222,7 +51280,7 @@ 0, c[Da >> 2] | 0, c[(i + 16) >> 2] | 0, - c[(i + 24) >> 2] | 0 + c[(i + 24) >> 2] | 0, ) | 0 ) ) { @@ -51322,7 +51380,7 @@ j, p, m, - k + k, ) | 0 ) { if ( @@ -51353,7 +51411,8 @@ (b + 436) | 0, f, d, - (d + (lb[c[(f + 28) >> 2] & 127](f, d) | 0)) | 0 + (d + (lb[c[(f + 28) >> 2] & 127](f, d) | 0)) | + 0, ) | 0; if (!d) { d = 1; @@ -51371,7 +51430,7 @@ g, ((c[j >> 2] | 0) + (0 - (c[(i + 64) >> 2] | 0))) | - 0 + 0, ) | 0; if (!g) { d = 1; @@ -51382,7 +51441,7 @@ c[(b + 4) >> 2] | 0, g, d, - c[k >> 2] | 0 + c[k >> 2] | 0, ); e = g; } @@ -51418,7 +51477,7 @@ (g + (lb[c[(d + 28) >> 2] & 127](d, g) | 0)) | - 0 + 0, ) | 0; if (!d) { d = 1; @@ -51701,7 +51760,7 @@ d, e, f, - ((a[(b + 480) >> 0] | 0) == 0) & 1 + ((a[(b + 480) >> 0] | 0) == 0) & 1, ) | 0; if ((d | 0) == 0 ? ((RZ(b) | 0) << 24) >> 24 == 0 : 0) d = 1; return d | 0; @@ -51804,7 +51863,7 @@ b, (m + 60) | 0, (d + 7) | 0, - 8 + 8, ) | 0; break; } @@ -52012,7 +52071,7 @@ f = lb[c[(h + 16) >> 2] & 127]( c[i >> 2] | 0, - (m * 24) | 0 + (m * 24) | 0, ) | 0; if (!f) { f = 0; @@ -52114,7 +52173,7 @@ b = lb[c[((c[(a + 20) >> 2] | 0) + 4) >> 2] & 127]( e, - b + b, ) | 0; if (!b) { b = 0; @@ -52303,7 +52362,7 @@ q, d, (e + g) | 0, - ((c[B >> 2] | 0) + (0 - g)) | 0 + ((c[B >> 2] | 0) + (0 - g)) | 0, ) | 0; if (!g) { g = 1; @@ -52329,7 +52388,7 @@ b, c[v >> 2] | 0, g, - (g + (c[(h + 8) >> 2] | 0)) | 0 + (g + (c[(h + 8) >> 2] | 0)) | 0, ) | 0; a[i >> 0] = 0; if (!g) break b; @@ -52344,7 +52403,7 @@ 0, c[(h + 20) >> 2] | 0, c[g >> 2] | 0, - c[(h + 24) >> 2] | 0 + c[(h + 24) >> 2] | 0, ) | 0) == 0; @@ -52503,7 +52562,7 @@ vb[c[m >> 2] & 127]( c[e >> 2] | 0, q, - ((c[o >> 2] | 0) - q) | 0 + ((c[o >> 2] | 0) - q) | 0, ); c[h >> 2] = c[n >> 2]; } while (b >>> 0 > 1); @@ -52600,7 +52659,7 @@ g, e, f, - ((a[(b + 480) >> 0] | 0) == 0) & 1 + ((a[(b + 480) >> 0] | 0) == 0) & 1, ) | 0; g = c[g >> 2] | 0; if (!(((d | 0) != 0) | ((g | 0) == 0))) { @@ -52745,7 +52804,7 @@ g, e, n, - 0 + 0, ) | 0; else { d = (b + 224) | 0; @@ -52787,7 +52846,7 @@ f = (b + 176) | 0; b = kb[c[(a + 12) >> 2] & 63]( - ((((c[f >> 2] | 0) * 20) | 0) + (c[(b + 168) >> 2] | 0)) | 0 + ((((c[f >> 2] | 0) * 20) | 0) + (c[(b + 168) >> 2] | 0)) | 0, ) | 0; if (!b) b = 0; else { @@ -53117,7 +53176,7 @@ (nb[c[v >> 2] & 63]( e, ((c[ja >> 2] | 0) + s) | 0, - (k + (0 - s)) | 0 + (k + (0 - s)) | 0, ) | 0) & 255; @@ -53138,7 +53197,7 @@ x, e, ((c[ja >> 2] | 0) + j) | 0, - ((c[ea >> 2] | 0) + (0 - j)) | 0 + ((c[ea >> 2] | 0) + (0 - j)) | 0, ) | 0; if (!j) { f = 1; @@ -53163,7 +53222,7 @@ b, e, c[ja >> 2] | 0, - c[ea >> 2] | 0 + c[ea >> 2] | 0, ); break; } @@ -53197,7 +53256,7 @@ b, e, c[ja >> 2] | 0, - c[ea >> 2] | 0 + c[ea >> 2] | 0, ); break; } @@ -53216,7 +53275,7 @@ j, c[(k + 20) >> 2] | 0, c[(k + 16) >> 2] | 0, - c[(k + 24) >> 2] | 0 + c[(k + 24) >> 2] | 0, ) | 0 ) ) { @@ -53242,13 +53301,13 @@ b, e, c[ja >> 2] | 0, - c[ea >> 2] | 0 + c[ea >> 2] | 0, ); } else vb[f & 127]( c[ha >> 2] | 0, c[k >> 2] | 0, - 0 + 0, ); break b; } @@ -53300,7 +53359,7 @@ Z, p, ka, - (j + -1) | 0 + (j + -1) | 0, ) | 0; j = c[ka >> 2] | 0; m = c[q >> 2] | 0; @@ -53340,7 +53399,7 @@ vb[f & 127]( c[ha >> 2] | 0, c[s >> 2] | 0, - c[P >> 2] | 0 + c[P >> 2] | 0, ); mZ(O); break; @@ -53354,7 +53413,7 @@ O, e, na, - (na + (lb[c[L >> 2] & 127](e, na) | 0)) | 0 + (na + (lb[c[L >> 2] & 127](e, na) | 0)) | 0, ) | 0; c[Z >> 2] = na; if (!na) { @@ -53375,7 +53434,7 @@ vb[f & 127]( c[ha >> 2] | 0, c[Z >> 2] | 0, - c[P >> 2] | 0 + c[P >> 2] | 0, ); j = 0; } @@ -53472,7 +53531,7 @@ } else { ub[f & 63]( c[ha >> 2] | 0, - c[c[m >> 2] >> 2] | 0 + c[c[m >> 2] >> 2] | 0, ); f = m; j = c[k >> 2] | 0; @@ -53549,14 +53608,14 @@ ja, f, ka, - c[da >> 2] | 0 + c[da >> 2] | 0, ) | 0; c[Y >> 2] = c[ja >> 2]; s = c[ca >> 2] | 0; vb[j & 127]( c[ha >> 2] | 0, s, - ((c[ka >> 2] | 0) - s) | 0 + ((c[ka >> 2] | 0) - s) | 0, ); if (na >>> 0 < 2) break; c[fa >> 2] = c[ja >> 2]; @@ -53711,7 +53770,7 @@ vb[c[$ >> 2] & 127]( c[ha >> 2] | 0, na, - ((c[ka >> 2] | 0) - na) | 0 + ((c[ka >> 2] | 0) - na) | 0, ); break; } else { @@ -53785,7 +53844,7 @@ g, j, p, - 0 + 0, ) | 0; else { s = (b + 224) | 0; @@ -53817,7 +53876,7 @@ d, e, f, - ((a[(b + 480) >> 0] | 0) == 0) & 1 + ((a[(b + 480) >> 0] | 0) == 0) & 1, ) | 0; break; } else { @@ -53834,7 +53893,7 @@ g, c[p >> 2] | 0, f, - ((a[(b + 480) >> 0] | 0) == 0) & 1 + ((a[(b + 480) >> 0] | 0) == 0) & 1, ) | 0; break; } @@ -54153,7 +54212,7 @@ H, e, c[(n + (p << 4) + 4) >> 2] | 0, - c[(n + (p << 4) + 8) >> 2] | 0 + c[(n + (p << 4) + 8) >> 2] | 0, ) | 0; c[(J + (f << 2)) >> 2] = G; if (!G) { @@ -54188,7 +54247,7 @@ i, c[(n + (p << 4) + 4) >> 2] | 0, c[(n + (p << 4) + 8) >> 2] | 0, - H + H, ) | 0; if (i | 0) break a; c[(J + (f << 2)) >> 2] = c[I >> 2]; @@ -54337,7 +54396,7 @@ k = lb[c[(b + 16) >> 2] & 127]( c[m >> 2] | 0, - 12 << i + 12 << i, ) | 0; if (!k) { a[F >> 0] = f; @@ -54894,7 +54953,7 @@ vb[j & 127]( c[v >> 2] | 0, i, - ((c[x >> 2] | 0) - i) | 0 + ((c[x >> 2] | 0) - i) | 0, ); if (r >>> 0 < 2) break; c[s >> 2] = c[y >> 2]; @@ -54976,7 +55035,7 @@ g, e, f, - ((a[(b + 480) >> 0] | 0) == 0) & 1 + ((a[(b + 480) >> 0] | 0) == 0) & 1, ) | 0; g = c[g >> 2] | 0; do @@ -55009,7 +55068,7 @@ d, e, f, - ((a[(b + 480) >> 0] | 0) == 0) & 1 + ((a[(b + 480) >> 0] | 0) == 0) & 1, ) | 0; if ((d | 0) == 0 ? ((RZ(b) | 0) << 24) >> 24 == 0 : 0) d = 1; return d | 0; @@ -55301,7 +55360,7 @@ m = lb[c[(b + 16) >> 2] & 127]( c[k >> 2] | 0, - l + l, ) | 0; if (!m) { h = 1; @@ -55341,7 +55400,7 @@ vb[p & 127]( c[(b + 4) >> 2] | 0, c[d >> 2] | 0, - c[i >> 2] | 0 ? f : 0 + c[i >> 2] | 0 ? f : 0, ); h = 0; } else h = 0; @@ -55835,7 +55894,7 @@ (nb[c[x >> 2] & 63]( d, (f + j) | 0, - ((c[I >> 2] | 0) + (0 - j)) | 0 + ((c[I >> 2] | 0) + (0 - j)) | 0, ) | 0) & 255; @@ -55849,7 +55908,7 @@ z, d, (f + j) | 0, - ((c[I >> 2] | 0) + (0 - j)) | 0 + ((c[I >> 2] | 0) + (0 - j)) | 0, ) | 0; if (!j) { i = 1; @@ -56433,7 +56492,7 @@ j, c[d >> 2] | 0, c[g >> 2] | 0, - (b + 404) | 0 + (b + 404) | 0, ); c[d >> 2] = c[g >> 2]; g = 2; @@ -56568,7 +56627,7 @@ W6( e | 0, (d + (0 - o)) | 0, - ((c[n >> 2] | 0) - q + o) | 0 + ((c[n >> 2] | 0) - q + o) | 0, ) | 0; tb[c[(a + 20) >> 2] & 127](c[l >> 2] | 0); c[l >> 2] = e; @@ -56688,7 +56747,7 @@ k, c[h >> 2] | 0, c[g >> 2] | 0, - (b + 404) | 0 + (b + 404) | 0, ); c[h >> 2] = c[g >> 2]; } @@ -58640,7 +58699,7 @@ W6( c[(j + 28) >> 2] | 0, g | 0, - O(c[(j + 40) >> 2] | 0, a) | 0 + O(c[(j + 40) >> 2] | 0, a) | 0, ) | 0; g = 0; while (1) { @@ -59939,7 +59998,7 @@ O( c[(n + (k << 2)) >> 2] | 0, - c[a >> 2] | 0 + c[a >> 2] | 0, ) | 0; e = (e + 1) | 0; } else { @@ -59953,7 +60012,7 @@ O( c[(n + (k << 2)) >> 2] | 0, - c[a >> 2] | 0 + c[a >> 2] | 0, ) | 0; s = (b + (j << 2)) | 0; c[s >> 2] = (c[s >> 2] | 0) + i; @@ -60396,7 +60455,7 @@ w = O( c[o >> 2] | 0, - c[q >> 2] | 0 + c[q >> 2] | 0, ) | 0; w = O( @@ -60405,7 +60464,7 @@ (b + (l << 2)) >> 2 - ] | 0 + ] | 0, ) | 0; x = (d + (f << 2)) | 0; c[x >> 2] = @@ -60423,7 +60482,7 @@ w = O( c[o >> 2] | 0, - c[q >> 2] | 0 + c[q >> 2] | 0, ) | 0; w = O( @@ -60432,7 +60491,7 @@ (b + (l << 2)) >> 2 - ] | 0 + ] | 0, ) | 0; x = (d + (k << 2)) | 0; c[x >> 2] = @@ -63011,7 +63070,7 @@ a, ((c[(p + (j << 2)) >> 2] | 0) - (c[(p + (a << 2)) >> 2] | 0)) | - 0 + 0, ) | 0; a = j; } @@ -63027,7 +63086,7 @@ j, ((c[(p + ((j + 1) << 2)) >> 2] | 0) - (c[(p + (j << 2)) >> 2] | 0)) | - 0 + 0, ) | 0; a = (a + 1) | 0; } @@ -63170,7 +63229,7 @@ g, h, b, - e + e, ), (d = c[i >> 2] | 0), d | 0) @@ -63365,7 +63424,7 @@ Fy( c[((c[f >> 2] | 0) + (b << 2)) >> 2] | 0, 105, - ((c[e >> 2] | 0) + (d << 2)) | 0 + ((c[e >> 2] | 0) + (d << 2)) | 0, ); a = (a + 12) | 0; if ( @@ -63782,7 +63841,7 @@ case 2: { s = +D( +(+Zw(e, b, p, c[g >> 2] | 0)), - 0.4 + 0.4, ); break; } @@ -64529,7 +64588,7 @@ e, d, c[(z + (m << 2)) >> 2] | 0, - c[(z + ((m + 1) << 2)) >> 2] | 0 + c[(z + ((m + 1) << 2)) >> 2] | 0, ); c[(v + (o << 2)) >> 2] = p; c[(u + (o << 2)) >> 2] = p; @@ -65700,7 +65759,7 @@ 1, g, ((c[(e + ((b * 12) | 0) + 4) >> 2] | 0) + (d << 2)) | 0, - i + i, ) | 0; d = (d + 1) | 0; b = c[g >> 2] | 0; @@ -66364,7 +66423,7 @@ j = +B( +(+h[ ((c[(o + (a << 2)) >> 2] | 0) + (l << 3)) >> 3 - ]) + ]), ); j = +h[(n + (a << 3)) >> 3] * j; a = f < j; @@ -66707,7 +66766,7 @@ ] >> 2 ] | 0, c[e >> 2] | 0, - 0 + 0, ) | 0) << 24) >> @@ -67087,7 +67146,7 @@ Zy( u, (u + 56) | 0, - j > 3.141592653589793 ? 6.283185307179586 - j : j + j > 3.141592653589793 ? 6.283185307179586 - j : j, ); d = 0; break; @@ -69822,7 +69881,7 @@ a, s, i, - E + E, ) | 0; while (0); if (!i) { @@ -70231,7 +70290,7 @@ a, 0, 12, - E + E, ) | 0; if (e | 0) { c[(e + 8) >> 2] = g; @@ -70731,7 +70790,7 @@ O( (((d[f >> 0] | 0) << 8) + b + (d[(f + 1) >> 0] | 0)) | 0, - 17109811 + 17109811, ) | 0; f = (f + 2) | 0; } @@ -71172,7 +71231,7 @@ c[(g + 8) >> 2] | 0, c[(g + 12) >> 2] | 0, c[(g + 16) >> 2] | 0, - f + f, ) | 0; a[(h + 22) >> 0] = a[(g + 22) >> 0] | 0; a[(h + 21) >> 0] = a[(g + 21) >> 0] | 0; @@ -71352,7 +71411,7 @@ c[(g + 12) >> 2] | 0, c[b >> 2] | 0, e, - (e + 4) | 0 + (e + 4) | 0, ) | 0; } else b = (b + 12) | 0; g = fD(a, c[(d + 12) >> 2] | 0) | 0; @@ -71684,7 +71743,7 @@ YA( a, c[((((c[b >> 2] & 3) | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2] | - 0 + 0, ) | 0; if (!d) a = 0; else { @@ -71723,7 +71782,7 @@ a, c[ ((((c[b >> 2] & 3) | 0) == 2 ? b : (b + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; if (!d) a = 0; else { @@ -72120,7 +72179,7 @@ a, c[((((c[b >> 2] & 3) | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2] | 0, - d + d, ) | 0; g = oC( @@ -72128,7 +72187,7 @@ c[ ((((c[b >> 2] & 3) | 0) == 2 ? b : (b + -48) | 0) + 40) >> 2 ] | 0, - d + d, ) | 0; do if (((f | 0) != 0) & ((g | 0) != 0)) { @@ -72168,7 +72227,7 @@ c[b >> 2] | 0, c[(b + 4) >> 2] | 0, c[e >> 2] | 0, - c[(e + 4) >> 2] | 0 + c[(e + 4) >> 2] | 0, ) | 0; return ((e | 0) == 0 ? 0 : (e >> 31) | 1) | 0; } @@ -72354,7 +72413,7 @@ GC(b, c[f >> 2] | 0) | 0; } tb[c[((c[(e + 4) >> 2] | 0) + 20) >> 2] & 127]( - c[(e + 16) >> 2] | 0 + c[(e + 16) >> 2] | 0, ); if (!(aD(b) | 0)) { d = c[d >> 2] | 0; @@ -72505,7 +72564,7 @@ d, e, f, - g + g, ) | 0), h | 0) : 0 @@ -72524,7 +72583,7 @@ d, 0, f, - g + g, ) | 0; if (i & ((h | 0) != 0)) { j = f; @@ -72544,7 +72603,7 @@ c[(a + 16) >> 2] | 0, b, d, - e + e, ) | 0 ); } @@ -72559,7 +72618,7 @@ c[(a + 16) >> 2] | 0, b, d, - e + e, ); return; } @@ -72590,7 +72649,7 @@ c[(b + 16) >> 2] | 0, c[a >> 2] & 3, c[e >> 2] | 0, - c[(e + 4) >> 2] | 0 + c[(e + 4) >> 2] | 0, ) | 0), e | 0) : 0 @@ -72622,7 +72681,7 @@ vb[c[((c[(a + 4) >> 2] | 0) + 24) >> 2] & 127]( c[(a + 16) >> 2] | 0, b, - d + d, ); return; } @@ -75260,7 +75319,7 @@ nb[c[c[((c[46967] | 0) + 8) >> 2] >> 2] & 63]( c[46968] | 0, ((c[(f + 4) >> 2] | 0) + l) | 0, - g >>> 0 < 8192 ? g : 8192 + g >>> 0 < 8192 ? g : 8192, ) | 0; e = c[c[46972] >> 2] | 0; c[46977] = d; @@ -76250,7 +76309,7 @@ 2 ] | 0, b, - d + d, ) | 0) == -1 @@ -76313,7 +76372,7 @@ return ( lb[c[((c[((c[(a + 64) >> 2] | 0) + 8) >> 2] | 0) + 4) >> 2] & 127]( b, - d + d, ) | 0 ); } @@ -76635,7 +76694,7 @@ ((c[i >> 2] | 0) + (c[f >> 2] << 2)) >> 2 - ] | 0 + ] | 0, ) | 0) == -1 @@ -77189,7 +77248,7 @@ JE( c[g >> 2] | 0, c[(g + -4) >> 2] | 0, - c[(g + -8) >> 2] | 0 + c[(g + -8) >> 2] | 0, ); break; } @@ -77253,7 +77312,7 @@ NE( c[(g + -16) >> 2] | 0, c[(g + -8) >> 2] | 0, - c[g >> 2] | 0 + c[g >> 2] | 0, ); break; } @@ -77469,7 +77528,7 @@ c[(d + 4) >> 2] | 0, c[(d + 8) >> 2] | 0, c[b >> 2] | 0, - h + h, ); d = (d + 12) | 0; } @@ -78047,7 +78106,7 @@ if (!f) { ub[c[((c[(b + 12) >> 2] | 0) + 16) >> 2] & 63]( 98264, - n + n, ); Sa(1); } else { @@ -78622,7 +78681,7 @@ ((c[((c[h >> 2] | 0) + 12) >> 2] | 0) + 8) >> 2 - ] | 0 + ] | 0, ) | 0 : 0 ) @@ -78718,7 +78777,7 @@ ((c[((c[g >> 2] | 0) + 12) >> 2] | 0) + 8) >> 2 - ] | 0 + ] | 0, ) | 0; e = 0; } @@ -79057,7 +79116,7 @@ $3( f, c[(20228 + (d << 4)) >> 2] | 0, - c[(20228 + (d << 4) + 4) >> 2] | 0 + c[(20228 + (d << 4) + 4) >> 2] | 0, ) | 0 ) ) @@ -80264,7 +80323,7 @@ V6( c[g >> 2] | 0, G | 0, - F | 0 + F | 0, ) | 0; if (!h) bb(g | 0, p | 0); z = p; @@ -80291,7 +80350,7 @@ V6( c[g >> 2] | 0, G | 0, - F | 0 + F | 0, ) | 0; if (!h) bb(g | 0, p | 0); z = p; @@ -80318,7 +80377,7 @@ V6( c[g >> 2] | 0, G | 0, - F | 0 + F | 0, ) | 0; if (!h) bb(g | 0, p | 0); z = p; @@ -80544,7 +80603,7 @@ V6( c[g >> 2] | 0, G | 0, - F | 0 + F | 0, ) | 0; if (!h) bb(g | 0, p | 0); z = p; @@ -81056,7 +81115,7 @@ V6( c[d >> 2] | 0, A | 0, - y | 0 + y | 0, ) | 0; if (!f) bb(d | 0, p | 0); z = p; @@ -81078,7 +81137,7 @@ V6( c[d >> 2] | 0, A | 0, - y | 0 + y | 0, ) | 0; if (!f) bb(d | 0, p | 0); z = p; @@ -81511,7 +81570,7 @@ 40) >> 2 ] | 0, - 0 + 0, ) | 0 ) { mB(a, e, 1) | 0; @@ -81666,7 +81725,7 @@ 2 ] | 0, 99031, - 0 + 0, ) | 0) + 12) >> @@ -82191,7 +82250,7 @@ (g + (c[(C + 12) >> 2] << 3)) | 0, p, c[o >> 2] | 0, - q + q, ); } i = (i + 1) | 0; @@ -82208,7 +82267,7 @@ (g + (c[(D + 12) >> 2] << 3)) | 0, p, c[o >> 2] | 0, - q + q, ); i = (i + 1) | 0; } @@ -83087,7 +83146,7 @@ d, e, f, - i + i, ) | 0) != 0) @@ -83467,7 +83526,7 @@ n, c[ ((((c[a >> 2] & 3) | 0) == 2 ? a : (a + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ); c[z >> 2] = c[n >> 2]; c[(z + 4) >> 2] = c[(n + 4) >> 2]; @@ -83594,7 +83653,7 @@ (d + (c[(o + 12) >> 2] << 3)) | 0, k, c[j >> 2] | 0, - b + b, ); e = (e + 1) | 0; } @@ -85125,10 +85184,10 @@ c[(((m | 0) == 2 ? b : j) + 40) >> 2] | 0, c[(((m | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2] | 0, 0, - 0 + 0, ) | 0, k, - n + n, ); c[e >> 2] = c[k >> 2] | c[e >> 2]; c[d >> 2] = c[n >> 2] | c[d >> 2]; @@ -86539,7 +86598,7 @@ if ((e | 0) > 1 ? (c[(b + 152) >> 2] & 64) | 0 : 0) { nA( d, - c[((c[((c[b >> 2] | 0) + 308) >> 2] | 0) + (e << 2)) >> 2] | 0 + c[((c[((c[b >> 2] | 0) + 308) >> 2] | 0) + (e << 2)) >> 2] | 0, ) | 0; f = (d + 4) | 0; e = c[f >> 2] | 0; @@ -87299,7 +87358,7 @@ w, +h[(f + 8) >> 3], +h[(f + 16) >> 3], - c[J >> 2] | 0 + c[J >> 2] | 0, ); J = (f + 80) | 0; c[J >> 2] = c[w >> 2]; @@ -87849,7 +87908,7 @@ c[f >> 2] | 0, c[(n + 228) >> 2] | 0, c[(n + 244) >> 2] | 0, - c[(n + 212) >> 2] | 0 + c[(n + 212) >> 2] | 0, ); } while (0); @@ -88243,7 +88302,7 @@ : (e + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ); KI(a, e); e = ZA(b, e) | 0; @@ -88384,7 +88443,7 @@ c[s >> 2] | 0, c[(r + 228) >> 2] | 0, c[(r + 244) >> 2] | 0, - c[(r + 212) >> 2] | 0 + c[(r + 212) >> 2] | 0, ); } c[H >> 2] = 0; @@ -88578,7 +88637,7 @@ c[s >> 2] | 0, c[(r + 228) >> 2] | 0, c[(r + 244) >> 2] | 0, - c[(r + 212) >> 2] | 0 + c[(r + 212) >> 2] | 0, ); } dS(e); @@ -88715,8 +88774,8 @@ KB( c[ ((((c[d >> 2] & 3) | 0) == 3 ? d : i) + 40) >> 2 - ] | 0 - ) | 0 + ] | 0, + ) | 0, ) | 0; e = T1( @@ -88728,25 +88787,25 @@ ((((c[d >> 2] & 3) | 0) == 2 ? d : f) + 40) >> 2 - ] | 0 - ) | 0 + ] | 0, + ) | 0, ) | 0)) | - 0 + 0, ) | 0; r3( e, KB( - c[((((c[d >> 2] & 3) | 0) == 3 ? d : i) + 40) >> 2] | 0 - ) | 0 + c[((((c[d >> 2] & 3) | 0) == 3 ? d : i) + 40) >> 2] | 0, + ) | 0, ) | 0; i = (xB( xC( c[ ((((c[d >> 2] & 3) | 0) == 2 ? d : f) + 40) >> 2 - ] | 0 - ) | 0 + ] | 0, + ) | 0, ) | 0) == 0; @@ -88763,8 +88822,8 @@ F4( e, KB( - c[((((c[d >> 2] & 3) | 0) == 2 ? d : f) + 40) >> 2] | 0 - ) | 0 + c[((((c[d >> 2] & 3) | 0) == 2 ? d : f) + 40) >> 2] | 0, + ) | 0, ) | 0; rS(b, e); U1(e); @@ -88952,7 +89011,7 @@ 2 ] | 0, c[47167] | 0, - 195059 + 195059, ) | 0; if (!(a[f >> 0] | 0)) { e = 1; @@ -89407,7 +89466,7 @@ kA( (186632 + (g << 4)) | 0, 1024, - ((c[46690] | 0) + 40 + (g << 10)) | 0 + ((c[46690] | 0) + 40 + (g << 10)) | 0, ); g = (g + 1) | 0; } @@ -90045,7 +90104,7 @@ fc( c[(11416 + (c[((c[(a + 16) >> 2] | 0) + 12) >> 2] << 2)) >> 2] | 0, b, - d + d, ); return; } @@ -90771,7 +90830,7 @@ 2 ] | 0, 141060, - 0 + 0, ) | 0) + 8) >> @@ -90788,7 +90847,7 @@ 2 ] | 0, 141060, - 0 + 0, ) | 0) + 8) >> @@ -92076,7 +92135,7 @@ c[(b + 208) >> 2] | 0, c[(b + 228) >> 2] | 0, c[(b + 244) >> 2] | 0, - c[(b + 212) >> 2] | 0 + c[(b + 212) >> 2] | 0, ); break; } @@ -92089,7 +92148,7 @@ c[(b + 208) >> 2] | 0, c[(b + 228) >> 2] | 0, c[(b + 244) >> 2] | 0, - c[(b + 212) >> 2] | 0 + c[(b + 212) >> 2] | 0, ); oF(a, 68212) | 0; break; @@ -92115,7 +92174,7 @@ b, d, e, - f + f, ); return; } @@ -98005,7 +98064,7 @@ c[((((c[d >> 2] & 3) | 0) == 3 ? d : (d + 48) | 0) + 40) >> 2] | 0, c[47168] | 0, - 195059 + 195059, ) | 0; i = aP( @@ -98013,7 +98072,7 @@ ((((c[d >> 2] & 3) | 0) == 2 ? d : (d + -48) | 0) + 40) >> 2 ] | 0, c[47168] | 0, - 195059 + 195059, ) | 0; e = c[g >> 2] | 0; f = (e + 154) | 0; @@ -98162,7 +98221,7 @@ if ((e | 0) > (b[(d + 238) >> 1] | 0)) break; U1( c[((c[(d + 196) >> 2] | 0) + (e << 6) + 12) >> 2] | - 0 + 0, ); e = (e + 1) | 0; d = c[f >> 2] | 0; @@ -98459,7 +98518,7 @@ bg( c[((c[((c[h >> 2] | 0) + 184) >> 2] | 0) + (f << 2)) >> 2] | 0, - d + d, ); f = (f + 1) | 0; e = (e + 1) | 0; @@ -98510,7 +98569,7 @@ c[((c[((c[g >> 2] | 0) + 184) >> 2] | 0) + (d << 2)) >> 2] = e; bg( c[((c[((c[f >> 2] | 0) + 184) >> 2] | 0) + (d << 2)) >> 2] | 0, - e + e, ); d = (d + 1) | 0; } @@ -98915,7 +98974,8 @@ g = MK( g, - ((q << 2) + 516) | 0 + ((q << 2) + 516) | + 0, ) | 0; i = g; j = g; @@ -98946,7 +99006,8 @@ g = MK( i, - ((q << 2) + 516) | 0 + ((q << 2) + 516) | + 0, ) | 0; f = g; j = g; @@ -98990,7 +99051,7 @@ g = MK( n, - ((q << 2) + 516) | 0 + ((q << 2) + 516) | 0, ) | 0; f = g; j = g; @@ -99466,7 +99527,7 @@ 0) + 100) >> 2 - ] | 0 + ] | 0, ); } f = $A(d, f) | 0; @@ -99488,7 +99549,7 @@ d, c[ ((c[g >> 2] | 0) + 104) >> 2 - ] | 0 + ] | 0, ); f = ZA(d, f) | 0; } @@ -100120,7 +100181,7 @@ f, g, i, - 2 + 2, ); break; } @@ -100275,7 +100336,7 @@ ] | 0, n, o, - 17296 + 17296, ); c[q >> 2] = 0; } @@ -100693,7 +100754,7 @@ d, p, u, - c[c[((c[(p + 16) >> 2] | 0) + 180) >> 2] >> 2] | 0 + c[c[((c[(p + 16) >> 2] | 0) + 180) >> 2] >> 2] | 0, ); c[ca >> 2] = c[M >> 2]; c[(ca + 4) >> 2] = c[(M + 4) >> 2]; @@ -100723,7 +100784,7 @@ d, p, u, - c[c[((c[(p + 16) >> 2] | 0) + 180) >> 2] >> 2] | 0 + c[c[((c[(p + 16) >> 2] | 0) + 180) >> 2] >> 2] | 0, ); c[U >> 2] = c[N >> 2]; c[(U + 4) >> 2] = c[(N + 4) >> 2]; @@ -100745,8 +100806,8 @@ : (u + -48) | 0) + 40) >> 2 - ] | 0 - ) | 0 + ] | 0, + ) | 0, ); ca = (U + 56 + (((c[C >> 2] | 0) + -1) << 5)) | 0; t = c[(v + 16) >> 2] | 0; @@ -100837,7 +100898,7 @@ c[c[((c[(v + 16) >> 2] | 0) + 180) >> 2] >> 2] | 0, q, t, - Z + Z, ) | 0; Dg(I, e); p = c[o >> 2] & 3; @@ -100851,7 +100912,7 @@ d, k, c[c[((c[I >> 2] | 0) + 172) >> 2] >> 2] | 0, - o + o, ); c[T >> 2] = c[P >> 2]; c[(T + 4) >> 2] = c[(P + 4) >> 2]; @@ -100935,8 +100996,8 @@ 1, U, xg( - c[((((c[u >> 2] & 3) | 0) == 2 ? u : s) + 40) >> 2] | 0 - ) | 0 + c[((((c[u >> 2] & 3) | 0) == 2 ? u : s) + 40) >> 2] | 0, + ) | 0, ); ca = ((c[C >> 2] | 0) + -1) | 0; h[D >> 3] = +h[(U + 56 + (ca << 5) + 24) >> 3]; @@ -101153,7 +101214,7 @@ ] | 0, n, v, - 17296 + 17296, ); q = (q + 1) | 0; } @@ -101971,7 +102032,7 @@ ] | 0)) | 0, - b + b, ) | 0) >= 1 @@ -102005,7 +102066,7 @@ 0) - f) | 0, - b + b, ) | 0) <= 0 @@ -102072,7 +102133,7 @@ ] | 0)) | 0, - b + b, ) | 0) >= 1 @@ -102106,7 +102167,7 @@ 0) - f) | 0, - b + b, ) | 0) <= 0 @@ -103149,7 +103210,7 @@ c[((((c[f >> 2] & 3) | 0) == 2 ? f : x) + 40) >> 2] | 0, j, i, - 17296 + 17296, ); l = z; return; @@ -103324,7 +103385,7 @@ ] | 0, L, a, - 17296 + 17296, ); i = i + z; b = (b + 1) | 0; @@ -103483,7 +103544,7 @@ ] | 0, o, p, - 17296 + 17296, ); c[d >> 2] = 0; } @@ -103805,7 +103866,7 @@ c[((((c[C >> 2] & 3) | 0) == 2 ? C : (C + -48) | 0) + 40) >> 2] | 0, X, 4, - 17296 + 17296, ); C = c[((c[(C + 16) >> 2] | 0) + 96) >> 2] | 0; h[(C + 56) >> 3] = v; @@ -103927,7 +103988,7 @@ ] | 0, k, b, - 17296 + 17296, ); m = (m + 1) | 0; } @@ -104015,7 +104076,7 @@ ] | 0, k, b, - 17296 + 17296, ); m = (m + 1) | 0; p = t; @@ -104094,7 +104155,7 @@ KK( ((l2(c[c[((c[f >> 2] | 0) + 104) >> 2] >> 2] | 0) | 0) + 3) | - 0 + 0, ) | 0; c[d >> 2] = c[c[((c[f >> 2] | 0) + 104) >> 2] >> 2]; m4(b, 85488, d) | 0; @@ -104570,7 +104631,7 @@ 0) + 180) | 0, - a + a, ); hh( ((c[ @@ -104586,7 +104647,7 @@ 0) + 172) | 0, - a + a, ); return; } @@ -104651,7 +104712,7 @@ ] | 0) + 204) | - 0 + 0, ); return; } @@ -104988,7 +105049,7 @@ 0) + 188) | 0, - a + a, ); hh( ((c[ @@ -105002,7 +105063,7 @@ 0) + 196) | 0, - a + a, ); return; } @@ -105995,7 +106056,7 @@ if ( (O( ((c[((c[(a + 16) >> 2] | 0) + 236) >> 2] | 0) - e) | 0, - b + b, ) | 0) > 0 @@ -106240,7 +106301,7 @@ ((c[(i + 196) >> 2] | 0) + (e << 2)) >> 2 - ] | 0 + ] | 0, ) | 0) != 0) & @@ -106259,7 +106320,7 @@ ((c[(e + 188) >> 2] | 0) + (g << 2)) >> 2 - ] | 0 + ] | 0, ) | 0) != 0) & @@ -106459,7 +106520,7 @@ ((((c[b >> 2] & 3) | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0) != 0 @@ -106472,7 +106533,7 @@ ((((c[b >> 2] & 3) | 0) == 2 ? b : (b + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0) != 0) & @@ -106618,9 +106679,8 @@ 16) >> 2 ] | 0; - c[ - ((c[(d + 204) >> 2] | 0) + (c[(d + 208) >> 2] << 2)) >> 2 - ] = 0; + c[((c[(d + 204) >> 2] | 0) + (c[(d + 208) >> 2] << 2)) >> 2] = + 0; } while (0); if ((m | 0) == 15) { @@ -106702,7 +106762,7 @@ : (j + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0) != 0 @@ -106714,7 +106774,7 @@ : (j + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0) != 0 @@ -107233,7 +107293,7 @@ if ((f | 0) > (i | 0)) break; ti( c[(g + (f << 2)) >> 2] | 0, - c[(g + ((h - f) << 2)) >> 2] | 0 + c[(g + ((h - f) << 2)) >> 2] | 0, ); f = (f + 1) | 0; } @@ -107294,7 +107354,7 @@ c[(o + (f << 6) + 4) >> 2] | 0, c[(o + (f << 6)) >> 2] | 0, 4, - 62 + 62, ); o = c[n >> 2] | 0; f = (f + 1) | 0; @@ -107503,7 +107563,7 @@ k = O( c[(b + 4) >> 2] | 0, - c[((c[((j ? d : e) + 16) >> 2] | 0) + 284) >> 2] | 0 + c[((c[((j ? d : e) + 16) >> 2] | 0) + 284) >> 2] | 0, ) | 0; b = a[ @@ -108472,7 +108532,7 @@ b, c[ ((((c[d >> 2] & 3) | 0) == 2 ? d : e) + 40) >> 2 - ] | 0 + ] | 0, ); } g = (g + 1) | 0; @@ -108502,7 +108562,7 @@ b, c[ ((((c[d >> 2] & 3) | 0) == 3 ? d : e) + 40) >> 2 - ] | 0 + ] | 0, ); } g = (g + 1) | 0; @@ -108606,7 +108666,7 @@ d = ((O( c[(j + (g << 2)) >> 2] | 0, - b[((c[k >> 2] | 0) + 154) >> 1] | 0 + b[((c[k >> 2] | 0) + 154) >> 1] | 0, ) | 0) + d) | @@ -109050,7 +109110,7 @@ (17308 + ((d * 12) | 0) + ((Ji( - c[(((b | 0) == 2 ? a : (a + -48) | 0) + 40) >> 2] | 0 + c[(((b | 0) == 2 ? a : (a + -48) | 0) + 40) >> 2] | 0, ) | 0) << 2)) >> @@ -110349,7 +110409,7 @@ i = +( ((O( c[((c[x >> 2] | 0) + 248) >> 2] | 0, - e[(d + 170) >> 1] | 0 + e[(d + 170) >> 1] | 0, ) | 0) / 2) | @@ -110370,7 +110430,7 @@ +h[((c[(g + 16) >> 2] | 0) + 88) >> 3] ) | 0 ), - c[(d + 156) >> 2] | 0 + c[(d + 156) >> 2] | 0, ) | 0; d = c[j >> 2] & 3; f = c[(((d | 0) == 3 ? j : (j + 48) | 0) + 40) >> 2] | 0; @@ -110386,7 +110446,7 @@ +h[((c[(d + 16) >> 2] | 0) + 88) >> 3] ) | 0 ), - c[((c[k >> 2] | 0) + 156) >> 2] | 0 + c[((c[k >> 2] | 0) + 156) >> 2] | 0, ) | 0; o = 0; } else o = 0; @@ -110415,7 +110475,7 @@ +( O( c[((c[x >> 2] | 0) + 248) >> 2] | 0, - e[((c[k >> 2] | 0) + 170) >> 1] | 0 + e[((c[k >> 2] | 0) + 170) >> 1] | 0, ) | 0 ) ); @@ -110507,7 +110567,7 @@ ((((c[e >> 2] & 3) | 0) == 3 ? e : m) + 40) >> 2 ] | 0, +((i + 1) | 0), - c[(k + 156) >> 2] | 0 + c[(k + 156) >> 2] | 0, ) | 0; k = (e + -48) | 0; Zi( @@ -110516,7 +110576,7 @@ ((((c[e >> 2] & 3) | 0) == 2 ? e : k) + 40) >> 2 ] | 0, +((j + 1) | 0), - c[((c[l >> 2] | 0) + 156) >> 2] | 0 + c[((c[l >> 2] | 0) + 156) >> 2] | 0, ) | 0; l = c[e >> 2] & 3; i = @@ -110593,7 +110653,7 @@ c[(f + 256) >> 2] | 0, c[(f + 260) >> 2] | 0, d < 65535.0 ? d : 65535.0, - 1e3 + 1e3, ) | 0; } return; @@ -110638,7 +110698,7 @@ +h[((c[(d + 16) >> 2] | 0) + 88) >> 3] + f + +h[(g + 96) >> 3], - 0 + 0, ) | 0; g = c[i >> 2] | 0; d = c[(g + 196) >> 2] | 0; @@ -110655,7 +110715,7 @@ +h[((c[(d + 16) >> 2] | 0) + 96) >> 3] + f + +h[(g + 64) >> 3], - 0 + 0, ) | 0; break; } @@ -110704,7 +110764,7 @@ 3 ] | 0 ), - 0 + 0, ) | 0; } d = c[d >> 2] | 0; @@ -110816,7 +110876,7 @@ j, c[((c[o >> 2] | 0) + 256) >> 2] | 0, +h[(e + 96) >> 3] + m, - 0 + 0, ) | 0; } f = @@ -110877,7 +110937,7 @@ c[((c[o >> 2] | 0) + 260) >> 2] | 0, i, +h[(e + 88) >> 3] + m, - 0 + 0, ) | 0; } while (0); @@ -110915,14 +110975,14 @@ c[(f + 256) >> 2] | 0, c[((c[g >> 2] | 0) + 256) >> 2] | 0, +h[(f + 96) >> 3] + d, - 0 + 0, ) | 0; f = c[b >> 2] | 0; Zi( c[((c[g >> 2] | 0) + 260) >> 2] | 0, c[(f + 260) >> 2] | 0, +h[(f + 64) >> 3] + d, - 0 + 0, ) | 0; ij(e); a = (a + 1) | 0; @@ -111019,7 +111079,7 @@ c[((c[((a ? i : e) + 16) >> 2] | 0) + 260) >> 2] | 0, c[((c[((a ? e : i) + 16) >> 2] | 0) + 256) >> 2] | 0, m, - 0 + 0, ) | 0; a = c[n >> 2] | 0; d = a; @@ -111054,7 +111114,7 @@ ((((c[d >> 2] & 3) | 0) == 3 ? d : (d + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0 ) ) @@ -111065,7 +111125,7 @@ ((((c[d >> 2] & 3) | 0) == 2 ? d : (d + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0) == 0) & @@ -111105,7 +111165,7 @@ 40) >> 2 ] | 0, - b + b, ) | 0 ) ) @@ -111223,7 +111283,7 @@ : (j + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; while (1) { if (!j) break a; @@ -111331,7 +111391,7 @@ : (i + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; while (1) { if (!i) break a; @@ -112455,7 +112515,7 @@ ((((c[b >> 2] & 3) | 0) == 2 ? b : (b + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0 ) mB(d, b, 1) | 0; @@ -112784,7 +112844,7 @@ : j) + 40) >> 2 - ] | 0 + ] | 0, ) | 0) + 16) >> @@ -112819,7 +112879,7 @@ ((c[((c[e >> 2] | 0) + 16) >> 2] | 0) + 212) >> 2 - ] | 0 + ] | 0, ) | 0) == 0 @@ -112868,7 +112928,7 @@ (((e | 0) == 2 ? i : j) + 40) >> 2 - ] | 0 + ] | 0, ) | 0), (g | 0) != (c[ @@ -113046,9 +113106,8 @@ c[d >> 2] = e + 1; c[(b + (e << 2)) >> 2] = f; e = c[h >> 2] | 0; - c[ - ((c[(e + 180) >> 2] | 0) + (c[(e + 184) >> 2] << 2)) >> 2 - ] = 0; + c[((c[(e + 180) >> 2] | 0) + (c[(e + 184) >> 2] << 2)) >> 2] = + 0; e = (f + -48) | 0; b = c[ @@ -113090,9 +113149,8 @@ 16) >> 2 ] | 0; - c[ - ((c[(e + 172) >> 2] | 0) + (c[(e + 176) >> 2] << 2)) >> 2 - ] = 0; + c[((c[(e + 172) >> 2] | 0) + (c[(e + 176) >> 2] << 2)) >> 2] = + 0; f = ZA(a, f) | 0; } g = kC(a, g) | 0; @@ -113297,7 +113355,7 @@ 40) >> 2 ] | 0, - d + d, ); e = ZA(a, e) | 0; } @@ -113311,7 +113369,7 @@ 40) >> 2 ] | 0, - d + d, ); e = $A(a, e) | 0; } @@ -113403,7 +113461,7 @@ c[(((d | 0) == 2 ? b : g) + 40) >> 2] | 0, c[(((d | 0) == 3 ? b : f) + 40) >> 2] | 0, 0, - 0 + 0, ) | 0; if (!d) { d = c[b >> 2] & 3; @@ -113413,7 +113471,7 @@ c[(((d | 0) == 2 ? b : g) + 40) >> 2] | 0, c[(((d | 0) == 3 ? b : f) + 40) >> 2] | 0, 0, - 1 + 1, ) | 0; } g = c[(b + 16) >> 2] | 0; @@ -113552,7 +113610,7 @@ ((((c[h >> 2] & 3) | 0) == 3 ? h : (h + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; if ( n | 0 @@ -114157,14 +114215,14 @@ b = dh( c[(((b | 0) == 2 ? a : d) + 40) >> 2] | 0, - c[(((b | 0) == 3 ? a : e) + 40) >> 2] | 0 + c[(((b | 0) == 3 ? a : e) + 40) >> 2] | 0, ) | 0; if (!b) { b = c[a >> 2] & 3; nh( c[(((b | 0) == 2 ? a : d) + 40) >> 2] | 0, c[(((b | 0) == 3 ? a : e) + 40) >> 2] | 0, - a + a, ) | 0; } else vh(a, b); return; @@ -114772,7 +114830,7 @@ c[(k + ((e * 40) | 0) + 4) >> 2] | 0, c[(k + ((e * 40) | 0) + 12) >> 2] | 0, 4, - 64 + 64, ); g = c[46750] | 0; r = +h[(g + ((e * 40) | 0) + 24) >> 3]; @@ -114837,7 +114895,7 @@ (g << 2)) >> 2 ] | 0, - 0 + 0, ) | 0) + 16) >> @@ -115012,7 +115070,7 @@ : e) + 40) >> 2 - ] | 0 + ] | 0, ) | 0), (g = (d + -48) | 0), (h = @@ -115023,7 +115081,7 @@ : g) + 40) >> 2 - ] | 0 + ] | 0, ) | 0), (f | 0) != (h | 0)) : 0 @@ -115049,7 +115107,7 @@ a, c[(((i | 0) == 3 ? d : e) + 40) >> 2] | 0, c[(((i | 0) == 2 ? d : g) + 40) >> 2] | 0, - d + d, ); } while (0); @@ -115123,7 +115181,7 @@ b, i, (k | 0) > 0 ? 0.0 : +((0 - k) | 0), - ((c[((c[j >> 2] | 0) + 156) >> 2] | 0) * 10) | 0 + ((c[((c[j >> 2] | 0) + 156) >> 2] | 0) * 10) | 0, ) | 0; c[ ((c[ @@ -115131,7 +115189,7 @@ b, d, +(((k | 0) > 0 ? k : 0) | 0), - c[((c[j >> 2] | 0) + 156) >> 2] | 0 + c[((c[j >> 2] | 0) + 156) >> 2] | 0, ) | 0) + 16) >> @@ -115480,7 +115538,7 @@ b, l, c[((c[o >> 2] | 0) + 172) >> 2] | 0, - 1 + 1, ); jh(l); break; @@ -115509,7 +115567,7 @@ ((((c[l >> 2] & 3) | 0) == 2 ? l : j) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; h = c[l >> 2] & 3; if ( @@ -115567,7 +115625,7 @@ 2 ] | 0, q, - e + e, ); if ( (c[((c[k >> 2] | 0) + 96) >> 2] | 0) == @@ -115586,7 +115644,7 @@ c[ ((c[f >> 2] | 0) + 172) >> 2 ] | 0, - 1 + 1, ); break a; } else { @@ -115602,7 +115660,7 @@ b, c[(((d | 0) == 2 ? l : j) + 40) >> 2] | 0, c[(((d | 0) == 3 ? l : i) + 40) >> 2] | 0, - l + l, ); d = l; } @@ -115670,13 +115728,13 @@ f = Yk( c[((((c[d >> 2] & 3) | 0) == 3 ? d : (d + 48) | 0) + 40) >> 2] | - 0 + 0, ) | 0; g = Yk( c[ ((((c[d >> 2] & 3) | 0) == 2 ? d : (d + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; i = (c[((c[(f + 16) >> 2] | 0) + 232) >> 2] | 0) > @@ -115947,7 +116005,7 @@ e, d, c[((c[(d + 16) >> 2] | 0) + 172) >> 2] | 0, - ((f | 0) == (a | 0)) & ((e | 0) == (b | 0)) ? 1 : 5 + ((f | 0) == (a | 0)) & ((e | 0) == (b | 0)) ? 1 : 5, ); return; } @@ -116025,7 +116083,7 @@ ((((c[g >> 2] & 3) | 0) == 2 ? g : j) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; k = g; } else { @@ -116105,7 +116163,7 @@ 40) >> 2 ] | 0, - f + f, ) | 0; c[((c[n >> 2] | 0) + 172) >> 2] = l; ih(g); @@ -116131,7 +116189,7 @@ 2 ] | 0, e, - f + f, ) | 0) + 16) >> @@ -116857,7 +116915,7 @@ : (a + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; L = KB( @@ -116867,7 +116925,7 @@ : (a + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; c[f >> 2] = K; c[(f + 4) >> 2] = L; @@ -116901,7 +116959,7 @@ ((((c[a >> 2] & 3) | 0) == 3 ? a : u) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; F = KB( @@ -116909,7 +116967,7 @@ ((((c[a >> 2] & 3) | 0) == 2 ? a : s) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; K = QA(a, 87798) | 0; c[i >> 2] = o; @@ -116939,7 +116997,7 @@ : u) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; F = KB( @@ -116949,7 +117007,7 @@ : s) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; K = QA(a, 87798) | 0; c[n >> 2] = o; @@ -117096,7 +117154,7 @@ ((((c[a >> 2] & 3) | 0) == 3 ? a : u) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; H = KB( @@ -117104,7 +117162,7 @@ ((((c[a >> 2] & 3) | 0) == 2 ? a : s) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; K = QA(a, 87804) | 0; c[w >> 2] = G; @@ -117134,7 +117192,7 @@ : u) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; H = KB( @@ -117144,7 +117202,7 @@ : s) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; K = QA(a, 87804) | 0; c[x >> 2] = G; @@ -117781,7 +117839,7 @@ ((c[(e + (m << 6) + 4) >> 2] | 0) + (g << 2)) >> 2 - ] | 0 + ] | 0, ) | 0; e = o; o = 0; @@ -117868,7 +117926,7 @@ 0) + (g << 2)) >> 2 - ] | 0 + ] | 0, ) | 0; e = o; o = 0; @@ -117877,7 +117935,7 @@ V6( c[e >> 2] | 0, t | 0, - u | 0 + u | 0, ) | 0; if (!f) bb(e | 0, p | 0); z = p; @@ -117902,7 +117960,7 @@ V6( c[d >> 2] | 0, t | 0, - u | 0 + u | 0, ) | 0; if (!e) bb(d | 0, p | 0); z = p; @@ -117955,7 +118013,7 @@ o = 0; ia( 101, - c[((c[(d + 184) >> 2] | 0) + (f << 2)) >> 2] | 0 + c[((c[(d + 184) >> 2] | 0) + (f << 2)) >> 2] | 0, ); d = o; o = 0; @@ -118143,7 +118201,7 @@ 40) >> 2 ] | 0, - j + j, ) | 0; } while (1) { @@ -118197,7 +118255,7 @@ 2 ] | 0, q, - j + j, ) | 0; } while (1) { @@ -118468,7 +118526,7 @@ : (f + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0 ) { j = @@ -118480,7 +118538,7 @@ : (f + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0) == 0; @@ -118614,7 +118672,7 @@ 2 ] | 0)) | - 0 + 0, ) | 0) > 0) & @@ -119306,7 +119364,7 @@ nh( d, c[((((c[b >> 2] & 3) | 0) == 3 ? b : m) + 40) >> 2] | 0, - b + b, ) | 0; e = c[(e + 16) >> 2] | 0; h[(e + 16) >> 3] = -+h[((c[g >> 2] | 0) + 88) >> 3]; @@ -119328,7 +119386,7 @@ nh( d, c[((((c[b >> 2] & 3) | 0) == 2 ? b : e) + 40) >> 2] | 0, - b + b, ) | 0; g = c[g >> 2] | 0; d = c[(d + 16) >> 2] | 0; @@ -119418,7 +119476,7 @@ ((((c[f >> 2] & 3) | 0) == 3 ? f : g) + 40) >> 2 - ] | 0 + ] | 0, ) | 0) + 16) >> @@ -119757,7 +119815,7 @@ g, c[(z + 228) >> 2] | 0, c[(z + 244) >> 2] | 0, - c[(z + 212) >> 2] | 0 + c[(z + 212) >> 2] | 0, ); l = A; return; @@ -120377,7 +120435,7 @@ c[q >> 2] | 0, c[m >> 2] | 0, c[n >> 2] | 0, - c[o >> 2] | 0 + c[o >> 2] | 0, ); dS(a); y = c[h >> 2] | 0; @@ -120431,7 +120489,7 @@ r, (((((p << 8) & 65535) << 16) >> 16) >> 15) & 255, o, - (((((p << 14) & 65535) << 16) >> 16) >> 15) & 255 + (((((p << 14) & 65535) << 16) >> 16) >> 15) & 255, ); p = c[((c[q >> 2] | 0) + 8) >> 2] | 0; o = c[p >> 2] | 0; @@ -120472,7 +120530,7 @@ r, (((((d << 7) & 65535) << 16) >> 16) >> 15) & 255, y, - (((((d << 13) & 65535) << 16) >> 16) >> 15) & 255 + (((((d << 13) & 65535) << 16) >> 16) >> 15) & 255, ); d = c[q >> 2] | 0; } @@ -120507,7 +120565,7 @@ c[(w + 240) >> 2] | 0, c[(w + 256) >> 2] | 0, c[p >> 2] | 0, - 0 + 0, ); UI( a, @@ -120518,7 +120576,7 @@ c[(w + 236) >> 2] | 0, c[(w + 252) >> 2] | 0, c[p >> 2] | 0, - 0 + 0, ); bS(a); bI(a); @@ -120692,7 +120750,7 @@ XI( i, +h[m >> 3] + p * 0.5, - +h[(d + 64) >> 3] - +h[(d + 32) >> 3] * 0.5 + +h[(d + 64) >> 3] - +h[(d + 32) >> 3] * 0.5, ); n = (i + 16) | 0; XI(j, +h[i >> 3] - p, +h[(i + 8) >> 3]); @@ -120814,7 +120872,7 @@ ((((c[b >> 2] & 3) | 0) == 2 ? b : (b + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; c[j >> 2] = z; c[(j + 4) >> 2] = A ? 100079 : 100084; @@ -121053,8 +121111,8 @@ ((((c[b >> 2] & 3) | 0) == 2 ? b : (b + -48) | 0) + 40) >> 2 - ] | 0 - ) | 0 + ] | 0, + ) | 0, ) | 0) != 0; @@ -121225,7 +121283,7 @@ 3, (b - f + q) / q, ((c[d >> 2] | 0) + (k << 4)) | 0, - c[e >> 2] | 0 + c[e >> 2] | 0, ); U1(n); } @@ -121997,7 +122055,7 @@ FJ( +h[(p + (i << 4)) >> 3], +h[(p + (i << 4) + 8) >> 3], - g + g, ) | 0; j = 0; while (1) { @@ -122459,7 +122517,7 @@ a, f, c[d >> 2] | 0, - (c[r >> 2] | 0) == 2 ? j : 0 + (c[r >> 2] | 0) == 2 ? j : 0, ); d = j; } @@ -122496,7 +122554,7 @@ c[d >> 2] | 0, 0, 0, - (c[r >> 2] | 0) == 4 ? j & 255 : 0 + (c[r >> 2] | 0) == 4 ? j & 255 : 0, ); d = j; } @@ -122582,7 +122640,7 @@ d = ~~( +K( +(+h[(r + 40) >> 3] - +h[(r + 24) >> 3]), - +(+h[(r + 32) >> 3] - +h[d >> 3]) + +(+h[(r + 32) >> 3] - +h[d >> 3]), ) * 57.29577951308232 ); jS(a, c[(z + 4) >> 2] | 0); @@ -124866,7 +124924,7 @@ e ? 2 : 0, g, i, - bP(b, GA(b, 0, 101446, 0) | 0, 137314) | 0 + bP(b, GA(b, 0, 101446, 0) | 0, 137314) | 0, ) | 0; i = (b + 16) | 0; c[((c[i >> 2] | 0) + 12) >> 2] = d; @@ -125197,8 +125255,8 @@ : (b + -48) | 0) + 40) >> 2 - ] | 0 - ) | 0 + ] | 0, + ) | 0, ) | 0; n = 0; o = 0; @@ -125247,7 +125305,7 @@ : (m + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; q = (xB(s) | 0) != 0; s = @@ -125258,7 +125316,7 @@ : (m + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; c[r >> 2] = p; c[(r + 4) >> 2] = q ? 137738 : 141747; @@ -125434,14 +125492,14 @@ ((((c[d >> 2] & 3) | 0) == 3 ? d : h) + 40) >> 2 - ] | 0 - ) | 0 - ) | 0 + ] | 0, + ) | 0, + ) | 0, ) | 0; g = l2(i) | 0; l = KB( - c[((((c[d >> 2] & 3) | 0) == 3 ? d : h) + 40) >> 2] | 0 + c[((((c[d >> 2] & 3) | 0) == 3 ? d : h) + 40) >> 2] | 0, ) | 0; k = l2(l) | 0; f = (d + 16) | 0; @@ -125454,7 +125512,7 @@ ((((c[d >> 2] & 3) | 0) == 2 ? d : (d + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; f = c[f >> 2] | 0; o = c[(f + 92) >> 2] | 0; @@ -125478,9 +125536,9 @@ ((((c[d >> 2] & 3) | 0) == 3 ? d : h) + 40) >> 2 - ] | 0 - ) | 0 - ) | 0 + ] | 0, + ) | 0, + ) | 0, ) | 0) == 0; @@ -126854,7 +126912,7 @@ if ((h | 0) < 2) { XO( m, - c[((((c[b >> 2] & 3) | 0) == 2 ? b : d) + 40) >> 2] | 0 + c[((((c[b >> 2] & 3) | 0) == 2 ? b : d) + 40) >> 2] | 0, ); a = c[i >> 2] | 0; } @@ -127120,7 +127178,7 @@ c[(((g | 0) == 3 ? b : j) + 40) >> 2] | 0, c[(((g | 0) == 2 ? b : m) + 40) >> 2] | 0, f, - 1 + 1, ) | 0; i = c[b >> 2] & 3; if ( @@ -127128,7 +127186,7 @@ c[(((i | 0) == 2 ? b : m) + 40) >> 2] | 0, c[(((i | 0) == 3 ? b : j) + 40) >> 2] | 0, f, - 0 + 0, ) | 0) == (g | 0) @@ -127441,7 +127499,7 @@ 40) >> 2 ] | 0, - b + b, ); a = c[f >> 2] | 0; } @@ -127457,7 +127515,7 @@ 40) >> 2 ] | 0, - b + b, ); a = c[f >> 2] | 0; } @@ -127628,7 +127686,7 @@ 2 ] | 0, e, - d + d, ) | 0; a = c[g >> 2] | 0; } @@ -127646,7 +127704,7 @@ 2 ] | 0, e, - d + d, ) | 0; a = c[g >> 2] | 0; } @@ -128003,7 +128061,7 @@ 40) >> 2 ] | 0, - e + e, ); d = (d + 1) | 0; } @@ -128017,7 +128075,7 @@ 40) >> 2 ] | 0, - d + d, ); a = c[f >> 2] | 0; } @@ -128635,7 +128693,7 @@ a = ((tL( c[((((c[g >> 2] & 3) | 0) == 3 ? g : j) + 40) >> 2] | 0, - b + b, ) | 0) + a) | @@ -128687,7 +128745,7 @@ a = ((tL( c[((((c[f >> 2] & 3) | 0) == 2 ? f : n) + 40) >> 2] | 0, - b + b, ) | 0) + a) | @@ -128819,7 +128877,7 @@ 40) >> 2 ] | 0, - j + j, ); DL( e, @@ -128830,7 +128888,7 @@ 40) >> 2 ] | 0, - f + f, ); EL(e, g); f = 0; @@ -128865,12 +128923,12 @@ : (m + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; AL( e, 102604, - CL(u, c[c[((c[p >> 2] | 0) + 96) >> 2] >> 2] | 0) | 0 + CL(u, c[c[((c[p >> 2] | 0) + 96) >> 2] >> 2] | 0) | 0, ); u = ((c[((c[p >> 2] | 0) + 96) >> 2] | 0) + 56) | 0; c[q >> 2] = c[u >> 2]; @@ -130838,7 +130896,7 @@ ((((c[b >> 2] & 3) | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; r = KB( @@ -130846,7 +130904,7 @@ ((((c[b >> 2] & 3) | 0) == 2 ? b : (b + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; c[e >> 2] = q; c[(e + 4) >> 2] = r; @@ -132520,7 +132578,7 @@ ((((c[C >> 2] & 3) | 0) == 3 ? C : z) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; N = KB( @@ -132528,7 +132586,7 @@ ((((c[C >> 2] & 3) | 0) == 2 ? C : A) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; c[K >> 2] = M; c[(K + 4) >> 2] = N; @@ -132799,8 +132857,8 @@ ? r : s : w - ? q - : p; + ? q + : p; w = D; continue; } else { @@ -132811,8 +132869,8 @@ ? g : f : w - ? m - : n; + ? m + : n; w = D; continue; } @@ -133349,7 +133407,7 @@ ] | 0, c[B >> 2] | 0, c[v >> 2] | 0, - g + g, ); } else dO( @@ -133359,7 +133417,7 @@ ] | 0, H, 4, - g + g, ); c[z >> 2] = c[I >> 2]; c[(z + 4) >> 2] = c[(I + 4) >> 2]; @@ -133390,7 +133448,7 @@ c[((((c[j >> 2] & 3) | 0) == 2 ? j : k) + 40) >> 2] | 0, G, 4, - g + g, ); c[z >> 2] = c[I >> 2]; c[(z + 4) >> 2] = c[(I + 4) >> 2]; @@ -136019,7 +136077,7 @@ m = +h[(g + 8) >> 3]; f = +K( +(m - +h[(g + (j << 4) + 8) >> 3]), - +(k - +h[(g + (j << 4)) >> 3]) + +(k - +h[(g + (j << 4)) >> 3]), ); j = 0; c: while (1) { @@ -136195,10 +136253,10 @@ (f == 1797693134862315708145274.0e284) ? 0.05 : f > 0.0 - ? f > 0.0003 - ? f - : 0.0003 - : f; + ? f > 0.0003 + ? f + : 0.0003 + : f; i = c[k >> 2] | 0; h[(i + 40) >> 3] = f; h[(i + 32) >> 3] = f; @@ -136492,7 +136550,7 @@ a[z >> 0] | 0 ? 2 : 0, +h[w >> 3], c[x >> 2] | 0, - c[u >> 2] | 0 + c[u >> 2] | 0, ) | 0; a[(k + 64) >> 0] = 1; n = f; @@ -137395,7 +137453,7 @@ i, c[(M + 228) >> 2] | 0, c[(M + 244) >> 2] | 0, - c[(M + 212) >> 2] | 0 + c[(M + 212) >> 2] | 0, ); J = 1; } else J = 1; @@ -137646,7 +137704,7 @@ c[N >> 2] | 0, c[(M + 228) >> 2] | 0, c[(M + 244) >> 2] | 0, - c[(M + 212) >> 2] | 0 + c[(M + 212) >> 2] | 0, ); dS(e); } @@ -138266,7 +138324,7 @@ h[(e + 8) >> 3] = +(~~(k + (k >= 0.0 ? 0.5 : -0.5)) | 0); h[(e + 16) >> 3] = +GN( j, - c[((c[((xC(b) | 0) + 16) >> 2] | 0) + 116) >> 2] & 3 + c[((c[((xC(b) | 0) + 16) >> 2] | 0) + 116) >> 2] & 3, ); k = +h[C >> 3]; j = +h[F >> 3]; @@ -138367,7 +138425,7 @@ 2 ] | 0, j, - 1 + 1, ); if (f) { c[g >> 2] = c[j >> 2]; @@ -138547,7 +138605,7 @@ g, c[(i + 228) >> 2] | 0, c[(i + 244) >> 2] | 0, - c[(i + 212) >> 2] | 0 + c[(i + 212) >> 2] | 0, ); d = c[e >> 2] | 0; } @@ -138573,7 +138631,7 @@ c[j >> 2] | 0, c[(i + 228) >> 2] | 0, c[(i + 244) >> 2] | 0, - c[(i + 212) >> 2] | 0 + c[(i + 212) >> 2] | 0, ); dS(a); } @@ -138837,7 +138895,7 @@ f, c[(t + 228) >> 2] | 0, c[(t + 244) >> 2] | 0, - c[(t + 212) >> 2] | 0 + c[(t + 212) >> 2] | 0, ); f = sN(d, e) | 0; uN(d, e) | 0; @@ -138898,7 +138956,7 @@ c[u >> 2] | 0, c[(t + 228) >> 2] | 0, c[(t + 244) >> 2] | 0, - c[(t + 212) >> 2] | 0 + c[(t + 212) >> 2] | 0, ); dS(d); } @@ -139294,7 +139352,7 @@ o = O( ((c[((c[47293] | 0) + 4) >> 2] | 0) + -1) | 0, - c[47295] | 0 + c[47295] | 0, ) | 0; c[47296] = (o | 0) > 0 ? o : 0; c[47292] = k; @@ -139525,7 +139583,7 @@ g, c[(r + 228) >> 2] | 0, c[(r + 244) >> 2] | 0, - c[(r + 212) >> 2] | 0 + c[(r + 212) >> 2] | 0, ); q = 1; } else q = 1; @@ -139614,7 +139672,7 @@ c[s >> 2] | 0, c[(r + 228) >> 2] | 0, c[(r + 244) >> 2] | 0, - c[(r + 212) >> 2] | 0 + c[(r + 212) >> 2] | 0, ); dS(e); } @@ -139842,7 +139900,7 @@ f, c[(((e | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2] | 0, c[(((e | 0) == 2 ? b : (b + -48) | 0) + 40) >> 2] | 0, - d + d, ); e = f; f = (d + 40) | 0; @@ -139860,7 +139918,7 @@ h, c[(((e | 0) == 2 ? b : (b + -48) | 0) + 40) >> 2] | 0, c[(((e | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2] | 0, - d + d, ); e = h; f = (d + 40) | 0; @@ -140110,7 +140168,7 @@ a, c[((c[((c[(g + 8) >> 2] | 0) + 4) >> 2] | 0) + 12) >> 2] | 0, m, - e + e, ); b = 0; while (1) { @@ -140419,7 +140477,7 @@ if ( ((kb[c[k >> 2] & 63]( c[((((c[b >> 2] & 3) | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2] | - 0 + 0, ) | 0) << 24) >> @@ -141081,7 +141139,7 @@ 3 ] ), - +(k - f / +(g | 0)) + +(k - f / +(g | 0)), ); j = c[e >> 2] | 0; return +( @@ -141105,7 +141163,7 @@ 3 ] - l ), - +(b / +(a | 0) - k) + +(b / +(a | 0) - k), ) + f) * 0.5 @@ -141681,7 +141739,7 @@ : (b + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0) + 16) >> @@ -141900,7 +141958,7 @@ : (e + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0) + 16) >> @@ -141931,7 +141989,7 @@ ] | 0, L, 7, - j + j, ); e = q; r = (r + 1) | 0; @@ -142135,7 +142193,7 @@ : (e + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0) + 16) >> @@ -142166,7 +142224,7 @@ ] | 0, M, 7, - j + j, ); e = r; t = (t + 1) | 0; @@ -142322,7 +142380,7 @@ : (e + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0) + 16) >> @@ -142353,7 +142411,7 @@ ] | 0, L, 7, - j + j, ); e = q; r = (r + 1) | 0; @@ -142500,7 +142558,7 @@ : (p + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0) + 16) >> @@ -142531,7 +142589,7 @@ ] | 0, L, 7, - j + j, ); e = r; q = (q + 1) | 0; @@ -142590,7 +142648,7 @@ ((((c[b >> 2] & 3) | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; zP(e, c[((c[d >> 2] | 0) + 100) >> 2] | 0); } @@ -142602,7 +142660,7 @@ ((((c[b >> 2] & 3) | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; zP(e, c[((c[d >> 2] | 0) + 104) >> 2] | 0); } @@ -142760,7 +142818,7 @@ ((((c[b >> 2] & 3) | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; d = KB( @@ -142768,7 +142826,7 @@ ((((c[b >> 2] & 3) | 0) == 2 ? b : (b + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; c[f >> 2] = g; c[(f + 4) >> 2] = d; @@ -143072,11 +143130,11 @@ y = (K + (n << 6) + 56) | 0; k = +CO( +h[(K + (L << 6) + 8) >> 3] - l, - +h[(K + (L << 6)) >> 3] - m + +h[(K + (L << 6)) >> 3] - m, ); q = +CO( +h[(K + (b << 6) + 8) >> 3] - l, - +h[(K + (b << 6)) >> 3] - m + +h[(K + (b << 6)) >> 3] - m, ); r = +hb[d & 7](t, B, e); b = (n | 0) == 0; @@ -144407,7 +144465,7 @@ 2 ] | 0, e, - d + d, ); d = s; e = (f + 40) | 0; @@ -144445,7 +144503,7 @@ 2 ] | 0, e, - d + d, ); d = u; e = (f + 40) | 0; @@ -147014,7 +147072,7 @@ 2 ] | 0, h, - g + g, ); i = c[g >> 2] | 0; if ((i | 0) <= (e | 0)) { @@ -147635,7 +147693,7 @@ : (d + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; b = KB( @@ -147645,7 +147703,7 @@ : (d + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; c[A >> 2] = D; c[(A + 4) >> 2] = b; @@ -147663,7 +147721,7 @@ : (d + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; D = KB( @@ -147673,7 +147731,7 @@ : (d + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; c[z >> 2] = C; c[(z + 4) >> 2] = D; @@ -148361,7 +148419,7 @@ a, GA(a, 0, 89279, 0) | 0, 0.0, - -1797693134862315708145274.0e284 + -1797693134862315708145274.0e284, ); if (q) { f = @@ -148375,7 +148433,7 @@ f, e, c[47136] | 0, - m + m, ) | 0; g = 12; } else g = 14; @@ -148390,7 +148448,7 @@ c[47137] | 0, f, e, - c[47136] | 0 + c[47136] | 0, ) | 0; g = 12; } @@ -149066,7 +149124,7 @@ : (g + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; zP(f, c[((c[h >> 2] | 0) + 96) >> 2] | 0); } @@ -149085,7 +149143,7 @@ ((((c[e >> 2] & 3) | 0) == 3 ? e : (e + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; zP(j, c[((c[h >> 2] | 0) + 96) >> 2] | 0); } @@ -149803,7 +149861,7 @@ ((((c[b >> 2] & 3) | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; v = KB( @@ -149811,7 +149869,7 @@ ((((c[b >> 2] & 3) | 0) == 2 ? b : (b + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; c[n >> 2] = t; c[(n + 4) >> 2] = v; @@ -149825,12 +149883,12 @@ ((((c[b >> 2] & 3) | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; f = (b + -48) | 0; v = KB( - c[((((c[b >> 2] & 3) | 0) == 2 ? b : f) + 40) >> 2] | 0 + c[((((c[b >> 2] & 3) | 0) == 2 ? b : f) + 40) >> 2] | 0, ) | 0; c[o >> 2] = n; c[(o + 4) >> 2] = v; @@ -149845,7 +149903,7 @@ c[((((c[g >> 2] & 3) | 0) == 2 ? b : f) + 40) >> 2] | 0, c[s >> 2] | 0, c[(s + 4) >> 2] | 0, - 17792 + 17792, ); U1(h); c[q >> 2] = c[t >> 2]; @@ -150316,7 +150374,7 @@ ((c[((c[q >> 2] | 0) + 16) >> 2] | 0) + 248) >> 2 - ] | 0 + ] | 0, ); break; } @@ -150408,7 +150466,7 @@ ((((c[b >> 2] & 3) | 0) == 3 ? b : (b + 48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ) | 0; e = (b + -48) | 0; n = KB(c[((((c[b >> 2] & 3) | 0) == 2 ? b : e) + 40) >> 2] | 0) | 0; @@ -150426,7 +150484,7 @@ c[((((c[e >> 2] & 3) | 0) == 2 ? b : d) + 40) >> 2] | 0, c[h >> 2] | 0, c[(h + 4) >> 2] | 0, - 17792 + 17792, ); c[f >> 2] = c[i >> 2]; c[(f + 4) >> 2] = c[(i + 4) >> 2]; @@ -151563,7 +151621,9 @@ while (1) { if ((e | 0) == (b | 0)) break; x = +B( - +(+h[((c[m >> 2] | 0) + (e << 3)) >> 3]) + +(+h[ + ((c[m >> 2] | 0) + (e << 3)) >> 3 + ]), ); r = x > r ? x : r; e = (e + 1) | 0; @@ -151748,13 +151808,13 @@ $t( t, +g[((c[A >> 2] | 0) + (u << 2)) >> 2], - p + p, ); Wt( t, p, -1.0, - ((c[A >> 2] | 0) + (u << 2) + 4) | 0 + ((c[A >> 2] | 0) + (u << 2) + 4) | 0, ); bu(t, p); Vt(t, p, o, o); @@ -151834,7 +151894,7 @@ k, b, c[(H + (q << 2)) >> 2] | 0, - c[(z + (q << 2)) >> 2] | 0 + c[(z + (q << 2)) >> 2] | 0, ); q = (q + 1) | 0; } @@ -151844,7 +151904,7 @@ +Zt( b, c[(H + (q << 2)) >> 2] | 0, - c[(z + (q << 2)) >> 2] | 0 + c[(z + (q << 2)) >> 2] | 0, ) + v; q = (q + 1) | 0; } @@ -151874,7 +151934,7 @@ c[(z + (a << 2)) >> 2] | 0, b, 0.001, - b + b, ) | 0) < 0 @@ -151896,7 +151956,7 @@ c[(z + (a << 2)) >> 2] | 0, b, 0.001, - b + b, ) | 0) < 0 @@ -153101,7 +153161,7 @@ c[(d + (m << 2)) >> 2] | 0, c[(d + (e << 2)) >> 2] | 0, 0, - 0 + 0, ) | 0; if (d) g = +h[((c[(d + 16) >> 2] | 0) + 128) >> 3] * g; h[((c[(k + (e << 2)) >> 2] | 0) + (m << 3)) >> 3] = g; @@ -153156,7 +153216,7 @@ 132) >> 2 ] | 0, - n + n, ); f = c[47137] | 0; g = 1.0 / g; @@ -153635,9 +153695,8 @@ a = 0; while (1) { if ((a | 0) >= (e | 0)) break; - h[ - ((c[((c[f >> 2] | 0) + (d << 2)) >> 2] | 0) + (a << 3)) >> 3 - ] = 0.0; + h[((c[((c[f >> 2] | 0) + (d << 2)) >> 2] | 0) + (a << 3)) >> 3] = + 0.0; a = (a + 1) | 0; } f = (g + 16) | 0; @@ -153663,7 +153722,7 @@ 132) >> 2 ] | 0, - k + k, ); g = c[47137] | 0; i = 1.0 / i; @@ -156364,8 +156423,8 @@ (a | 0) ? q : (m | 0) == 2 - ? n - : (n + -48) | 0) + + ? n + : (n + -48) | 0) + 40) >> 2 ]; @@ -157695,7 +157754,7 @@ ((((c[f >> 2] & 3) | 0) == 2 ? f : (f + -48) | 0) + 40) >> 2 - ] | 0 + ] | 0, ); f = ZA(b, f) | 0; } @@ -158344,7 +158403,7 @@ a, GA(a, 0, 91269, 0) | 0, 0.0, - -1797693134862315708145274.0e284 + -1797693134862315708145274.0e284, ); d = ZO(a, GA(a, 0, 91278, 0) | 0, 0, 0) | 0; a = (b + 160) | 0; @@ -158421,7 +158480,7 @@ e, c[g >> 2] | 0, c[p >> 2] | 0, - j + j, ); break; } @@ -159165,7 +159224,7 @@ ow(x); bq( X, - +h[K >> 3] * 0.85 + +h[Y >> 3] + +h[L >> 3] * 3.3 + +h[K >> 3] * 0.85 + +h[Y >> 3] + +h[L >> 3] * 3.3, ); } v = +jq(U, v, q, z, T); @@ -159910,7 +159969,7 @@ (f + (((O( c[p >> 2] | 0, - a + a, ) | 0) + j) << @@ -160019,7 +160078,7 @@ ea, fa, U, - g + g, ); u = +h[U >> 3] + u; y = c[_ >> 2] | 0; @@ -160369,7 +160428,7 @@ d >> 2 ] | 0, - a + a, ) | 0) + m) << @@ -160401,7 +160460,7 @@ (((O( c[d >> 2] | 0, - a + a, ) | 0) + m) << @@ -160436,7 +160495,7 @@ (((O( c[d >> 2] | 0, - a + a, ) | 0) + m) << @@ -160465,7 +160524,7 @@ (g + (((O( c[d >> 2] | 0, - a + a, ) | 0) + m) << @@ -160489,7 +160548,7 @@ (g + (((O( c[d >> 2] | 0, - a + a, ) | 0) + m) << @@ -160607,7 +160666,7 @@ ea, fa, X, - j + j, ); d = c[ba >> 2] | 0; if (c[j >> 2] | 0) break b; @@ -161146,7 +161205,7 @@ da, ea, V, - i + i, ); d = c[$ >> 2] | 0; if (c[i >> 2] | 0) break b; @@ -161586,7 +161645,7 @@ +h[(e + 144) >> 3], c[G >> 2] | 0, j, - k + k, ); qw(L); U1(d); @@ -161624,7 +161683,7 @@ e, c[(q + 24) >> 2] | 0, r, - m + m, ); break a; } @@ -161658,7 +161717,7 @@ e, c[(q + 24) >> 2] | 0, r, - m + m, ); break a; } else if ((t | 0) == 30) { @@ -161685,7 +161744,7 @@ c[t >> 2] | 0, r, n, - m + m, ); if (u) { c[x >> 2] = 0; @@ -161699,7 +161758,7 @@ c[t >> 2] | 0, r, n * 0.5, - m + m, ); pq( a, @@ -161709,7 +161768,7 @@ c[t >> 2] | 0, r, n * 0.125, - m + m, ); pq( a, @@ -161719,7 +161778,7 @@ c[t >> 2] | 0, r, n * 0.03125, - m + m, ); } break; @@ -161749,7 +161808,7 @@ r, s, p, - +h[w >> 3] * 0.001 + +h[w >> 3] * 0.001, ); U1(r); c[x >> 2] = 0; @@ -161784,7 +161843,7 @@ +h[(e + 144) >> 3], c[G >> 2] | 0, j, - k + k, ); } W6(e | 0, K | 0, 168) | 0; @@ -162075,7 +162134,7 @@ (f + (((O( c[((c[g >> 2] | 0) + (i << 2)) >> 2] | 0, - a + a, ) | 0) + j) << @@ -163718,7 +163777,7 @@ (f - e) * +h[b >> 3] + (d - f) * +h[a >> 3] + +h[c >> 3] * (e - d) - ) + ), ) * 0.5 ); } @@ -164694,7 +164753,7 @@ ((c[r >> 2] | 0) + (o << 3)) >> 3 - ]) + ]), ); q = A > q ? A : q; o = (o + 1) | 0; @@ -164852,7 +164911,7 @@ b, c[J >> 2] | 0, c[K >> 2] | 0, - c[H >> 2] | 0 + c[H >> 2] | 0, ) | 0; y = (k + 4) | 0; r = 0; @@ -164889,13 +164948,14 @@ ((c[H >> 2] | 0) + (w << 2)) >> 2 ], - z + z, ); Wt( x, z, -1.0, - ((c[H >> 2] | 0) + (w << 2) + 4) | 0 + ((c[H >> 2] | 0) + (w << 2) + 4) | + 0, ); bu(x, z); Vt(x, z, f, f); @@ -164959,7 +165019,7 @@ j, b, c[(D + (r << 2)) >> 2] | 0, - c[(k + (r << 2)) >> 2] | 0 + c[(k + (r << 2)) >> 2] | 0, ); r = (r + 1) | 0; } @@ -164969,7 +165029,7 @@ +Zt( b, c[(D + (r << 2)) >> 2] | 0, - c[(k + (r << 2)) >> 2] | 0 + c[(k + (r << 2)) >> 2] | 0, ) + q; r = (r + 1) | 0; } @@ -164998,7 +165058,7 @@ c[(k + (s << 2)) >> 2] | 0, b, 0.001, - b + b, ) | 0 ) { o = -1; @@ -165013,7 +165073,7 @@ 1, 15, I, - n + n, ) | 0; s = (s + 1) | 0; } @@ -165744,9 +165804,8 @@ c[i >> 2] = j + 1; c[(h + (j << 2)) >> 2] = k; j = c[n >> 2] | 0; - c[ - ((c[(j + 180) >> 2] | 0) + (c[(j + 184) >> 2] << 2)) >> 2 - ] = 0; + c[((c[(j + 180) >> 2] | 0) + (c[(j + 184) >> 2] << 2)) >> 2] = + 0; j = (k + -48) | 0; h = c[ @@ -165788,9 +165847,8 @@ 16) >> 2 ] | 0; - c[ - ((c[(t + 172) >> 2] | 0) + (c[(t + 176) >> 2] << 2)) >> 2 - ] = 0; + c[((c[(t + 172) >> 2] | 0) + (c[(t + 176) >> 2] << 2)) >> 2] = + 0; k = ZA(r, k) | 0; } m = c[m >> 2] | 0; @@ -166504,7 +166562,7 @@ c[((c[j >> 2] | 0) + (f << 2)) >> 2] | 0, (a + ~~+g[((c[h >> 2] | 0) + (f << 2)) >> 2]) | 0, c[46849] | 0, - e + e, ); f = (f + 1) | 0; } @@ -166710,7 +166768,7 @@ c[((c[j >> 2] | 0) + (f << 2)) >> 2] | 0, +g[((c[h >> 2] | 0) + (f << 2)) >> 2] + k, o, - e + e, ); f = (f + 1) | 0; } @@ -170393,7 +170451,7 @@ +( O( c[((c[n >> 2] | 0) + (g << 2)) >> 2] | 0, - c[((c[l >> 2] | 0) + (g << 2)) >> 2] | 0 + c[((c[l >> 2] | 0) + (g << 2)) >> 2] | 0, ) | 0 ); g = (g + 1) | 0; @@ -173073,9 +173131,8 @@ function cv(a, b) { a = a | 0; b = b | 0; - h[ - ((c[((c[(b + 16) >> 2] | 0) + 112) >> 2] | 0) + 40) >> 3 - ] = 6.283185307179586; + h[((c[((c[(b + 16) >> 2] | 0) + 112) >> 2] | 0) + 40) >> 3] = + 6.283185307179586; hv(a, b); return; } @@ -174016,7 +174073,7 @@ 0.0, 0.0, 1.0, - y + y, ); } while (0); @@ -174867,7 +174924,7 @@ j, k, l, - m + m, ); n = (n + 1) | 0; } @@ -175094,7 +175151,7 @@ f, g, i, - j + j, ); k = (k + 1) | 0; } @@ -175115,7 +175172,7 @@ f, g, i, - j + j, ); k = (k + 1) | 0; } @@ -175134,7 +175191,7 @@ f, g, i, - j + j, ); k = (k + 1) | 0; } @@ -175153,7 +175210,7 @@ f, g, i, - j + j, ); k = (k + 1) | 0; } @@ -175172,7 +175229,7 @@ f, g, i, - j + j, ); k = (k + 1) | 0; } @@ -175550,7 +175607,7 @@ i, +h[(a + 24) >> 3] * 0.5, s, - j + j, ) | 0; c[((c[q >> 2] | 0) + (j << 2)) >> 2] = g; g = c[((c[q >> 2] | 0) + (j << 2)) >> 2] | 0; @@ -176367,25 +176424,25 @@ c[e >> 2] | 0, c[(a + 16) >> 2] | 0, c[f >> 2] | 0, - c[(a + 32) >> 2] | 0 + c[(a + 32) >> 2] | 0, ) | 0; d = (a + 20) | 0; W6( c[(b + 20) >> 2] | 0, c[d >> 2] | 0, - ((c[a >> 2] << 2) + 4) | 0 + ((c[a >> 2] << 2) + 4) | 0, ) | 0; W6( c[(b + 24) >> 2] | 0, c[(a + 24) >> 2] | 0, - (c[((c[d >> 2] | 0) + (c[a >> 2] << 2)) >> 2] << 2) | 0 + (c[((c[d >> 2] | 0) + (c[a >> 2] << 2)) >> 2] << 2) | 0, ) | 0; d = c[(a + 28) >> 2] | 0; if (d | 0) W6( c[(b + 28) >> 2] | 0, d | 0, - O(c[e >> 2] | 0, c[f >> 2] | 0) | 0 + O(c[e >> 2] | 0, c[f >> 2] | 0) | 0, ) | 0; c[(b + 36) >> 2] = c[(a + 36) >> 2]; c[(b + 8) >> 2] = c[e >> 2]; @@ -176434,7 +176491,7 @@ f, ((c[(b + 8) >> 2] | 0) + (c[(a + 8) >> 2] | 0)) | 0, d, - 1 + 1, ) | 0), u | 0) : 0 @@ -176819,7 +176876,7 @@ c[(a + 24) >> 2] | 0, c[(a + 28) >> 2] | 0, c[(a + 16) >> 2] | 0, - c[(a + 40) >> 2] | 0 + c[(a + 40) >> 2] | 0, ) | 0 ); else qa(95100, 94943, 803, 95126); @@ -177739,8 +177796,8 @@ (b << 24) >> 24 == 108 ? 60 : (b << 24) >> 24 == 103 - ? 62 - : 0; + ? 62 + : 0; } else e = 14; break; } @@ -180781,8 +180838,8 @@ ((b & 128) | 0) == 0 ? 1 : (d << 24) >> 24 == -19 - ? (c & 255) > 159 - : ((b & 192) | 0) == 192; + ? (c & 255) > 159 + : ((b & 192) | 0) == 192; } while (0); return (b & 1) | 0; @@ -180810,8 +180867,8 @@ ((c & 128) | 0) == 0 ? 1 : (e << 24) >> 24 == -12 - ? (b & 255) > 143 - : ((c & 192) | 0) == 192; + ? (b & 255) > 143 + : ((c & 192) | 0) == 192; break; } } else b = 1; @@ -185979,7 +186036,7 @@ i, f, g, - h + h, ) | 0; break a; } @@ -191203,7 +191260,7 @@ d, f, (e + (0 - (c[u >> 2] | 0))) | 0, - 107657 + 107657, ) | 0 ) ) { @@ -191213,7 +191270,7 @@ d, f, (e + (0 - (c[u >> 2] | 0))) | 0, - 107649 + 107649, ) | 0 ) ) { @@ -191529,7 +191586,7 @@ f, (d + (c[(f + 64) >> 2] << 1)) | 0, e, - 144368 + 144368, ) | 0 ) ) @@ -191579,7 +191636,7 @@ f, (d + (c[(f + 64) >> 2] << 1)) | 0, e, - 144368 + 144368, ) | 0 ) ) @@ -191710,7 +191767,7 @@ f, (d + (c[h >> 2] << 1)) | 0, e, - 144390 + 144390, ) | 0 ) { c[a >> 2] = 24; @@ -191722,7 +191779,7 @@ f, (d + (c[h >> 2] << 1)) | 0, e, - 144397 + 144397, ) | 0 ) { c[a >> 2] = 25; @@ -191734,7 +191791,7 @@ f, (d + (c[h >> 2] << 1)) | 0, e, - 144405 + 144405, ) | 0 ) { c[a >> 2] = 26; @@ -191747,7 +191804,7 @@ f, (d + (c[h >> 2] << 1)) | 0, e, - 144413 + 144413, ) | 0 ) ) @@ -192301,7 +192358,7 @@ f, (d + (c[(f + 64) >> 2] | 0)) | 0, e, - 144447 + 144447, ) | 0 ) ) @@ -192594,7 +192651,7 @@ f, d, e, - c[(62304 + (g << 2)) >> 2] | 0 + c[(62304 + (g << 2)) >> 2] | 0, ) | 0 ) { j = 6; @@ -192649,7 +192706,7 @@ f, (d + (c[h >> 2] | 0)) | 0, e, - 144454 + 144454, ) | 0 ) { c[a >> 2] = 32; @@ -192661,7 +192718,7 @@ f, (d + (c[h >> 2] | 0)) | 0, e, - 144462 + 144462, ) | 0 ) { c[a >> 2] = 32; @@ -192674,7 +192731,7 @@ f, (d + (c[h >> 2] | 0)) | 0, e, - 144471 + 144471, ) | 0 ) ) @@ -197108,7 +197165,12 @@ a = d * +C( - +(+h[p >> 3] + +h[n >> 3] + +h[o >> 3] + +h[m >> 3]) + +( + +h[p >> 3] + + +h[n >> 3] + + +h[o >> 3] + + +h[m >> 3] + ), ); } } else a = k; @@ -199548,9 +199610,9 @@ ? 153593 : 153452 : G - ? 153444 - : 153448, - 3 + ? 153444 + : 153448, + 3, ); R3(b, 32, f, j, h ^ 8192); } else { @@ -199759,8 +199821,8 @@ q >>> 0 < x >>> 0 ? 0.5 : n & ((q | 0) == (x | 0)) - ? 1.0 - : 1.5; + ? 1.0 + : 1.5; if (C) { x = (a[B >> 0] | 0) == 45; e = x ? -e : e; @@ -200941,7 +201003,7 @@ k, (f + -1) | 0, 1, - j + j, ); s4(k, 1); b = c[k >> 2] | 1; @@ -202183,7 +202245,7 @@ y, u, t, - (((t | 0) < 0) << 31) >> 31 + (((t | 0) < 0) << 31) >> 31, ); n = t; break c; @@ -202254,7 +202316,7 @@ Y6( I | 0, (n & 1) | 0, - 256 + 256, ) | 0; a[K >> 0] = 0; n = a[m >> 0] | 0; @@ -202437,7 +202499,7 @@ } else n = Y2( - b + b, ) | 0; if ( @@ -202459,7 +202521,7 @@ L, M, 1, - J + J, ) | 0 ) { case -1: { @@ -202566,7 +202628,7 @@ } else n = Y2( - b + b, ) | 0; if ( @@ -202744,9 +202806,8 @@ while (0); if (!v) { if (r | 0) - c[ - (r + (n << 2)) >> 2 - ] = 0; + c[(r + (n << 2)) >> 2] = + 0; if (!k) { k = 0; break f; @@ -204379,7 +204440,7 @@ ((e[(c + 2) >> 1] | 0) << 16) | (e[c >> 1] | 0) | 0, e[(c + 4) >> 1] | 0 | 0, ((e[g >> 1] | 0) << 16) | (e[a >> 1] | 0) | 0, - e[f >> 1] | 0 | 0 + e[f >> 1] | 0 | 0, ) | 0; c = J6(d | 0, z | 0, e[(c + 6) >> 1] | 0 | 0, 0) | 0; d = z; @@ -204690,7 +204751,7 @@ Y6( p | 0, 0, - (e << 2) | 0 + (e << 2) | 0, ) | 0; O5(s, 0, p, 0) | 0; f = c[r >> 2] | 0; @@ -204727,9 +204788,8 @@ c[o >> 2] = m; b = O5(s, n, p, q) | 0; if (!b) { - c[ - (g + 20) >> 2 - ] = 0; + c[(g + 20) >> 2] = + 0; f = (s + 24) | 0; b = c[f >> 2] | 0; e = 0; @@ -204839,7 +204899,7 @@ 2; h = T1( - f + f, ) | 0; c[ @@ -204870,7 +204930,7 @@ ] | 0, f | - 0 + 0, ) | 0; } @@ -205210,7 +205270,7 @@ c[n >> 2] | 0, c[p >> 2] | 0, d, - 0 + 0, ) | 0; } c[n >> 2] = d; @@ -205514,9 +205574,8 @@ : 0 ) { if (y) { - c[ - (q + 20) >> 2 - ] = 1; + c[(q + 20) >> 2] = + 1; g = 0; } else { g = d6(b, q, m) | 0; @@ -205610,7 +205669,7 @@ ] | 0) + t) | - 0 + 0, ) | 0; if (!g) { if ( @@ -205647,7 +205706,7 @@ g = T5( d, - g + g, ) | 0; if ( @@ -205656,7 +205715,7 @@ g = T5( d, - 4 + 4, ) | 0; if ( @@ -205665,7 +205724,7 @@ g = S5( d, - k + k, ) | 0; if ( @@ -205676,7 +205735,7 @@ g = T5( d, - 0 + 0, ) | 0; o = @@ -205756,7 +205815,7 @@ a[ (i + 12) >> 0 - ] & 1 + ] & 1, ) | 0; if (g | 0) { p = t; @@ -205772,7 +205831,7 @@ S5( d, c[i >> 2] | - 0 + 0, ) | 0; if (!g) { g = @@ -205807,7 +205866,7 @@ d6( b, q, - m + m, ) | 0; c[ @@ -205896,7 +205955,7 @@ e6( e, f, - m + m, ); } c[e >> 2] = @@ -205952,19 +206011,19 @@ ] | 0) >>> 31) ^ - 1 + 1, ) | 0; if (!g) { g = S5( d, - q + q, ) | 0; if (!g) { g = S5( d, - j + j, ) | 0; if ( @@ -205973,7 +206032,7 @@ g = S5( d, - k + k, ) | 0; if ( @@ -205982,7 +206041,7 @@ g = T5( d, - 3 + 3, ) | 0; if ( @@ -205991,7 +206050,7 @@ g = S5( d, - j + j, ) | 0; if ( @@ -206000,7 +206059,7 @@ g = T5( d, - 0 + 0, ) | 0; if ( @@ -206009,7 +206068,7 @@ g = T5( d, - 2 + 2, ) | 0; if ( @@ -206025,7 +206084,7 @@ g = S5( d, - k + k, ) | 0; if ( @@ -206041,7 +206100,7 @@ g = T5( d, - 0 + 0, ) | 0; if ( @@ -206071,7 +206130,7 @@ d6( b, q, - m + m, ) | 0; c[ @@ -206152,7 +206211,7 @@ e6( e, f, - m + m, ); } c[ @@ -206514,7 +206573,7 @@ d = S5( b, - c[(h + 4) >> 2] | 0 + c[(h + 4) >> 2] | 0, ) | 0; if (d | 0) break b; d = T5(b, 0) | 0; @@ -206530,7 +206589,7 @@ d = S5( b, - c[(h + 4) >> 2] | 0 + c[(h + 4) >> 2] | 0, ) | 0; if (d | 0) break b; d = T5(b, 0) | 0; @@ -206613,7 +206672,7 @@ t, f, v, - x + x, ) | 0; if (!d) { h = 0; @@ -206656,7 +206715,7 @@ t, f, v, - x + x, ) | 0; if (d | 0) { s = 34; @@ -206680,7 +206739,7 @@ t, 0, u, - x + x, ) | 0; if (d | 0) break c; j = @@ -206689,7 +206748,7 @@ c[u >> 2] | 0, 0, -1, - 0 + 0, ) | 0; c[u >> 2] = j; if (!j) { @@ -206710,7 +206769,7 @@ t, 0, v, - x + x, ) | 0; if (d | 0) { s = 49; @@ -206725,7 +206784,7 @@ a, c[v >> 2] | 0, - j + j, ) | 0; c[u >> 2] = d; } @@ -206895,7 +206954,7 @@ 1114111, 0, 0, - -1 + -1, ) | 0; c[(i + 24) >> 2] = h; if (!h) { @@ -206910,7 +206969,7 @@ 1114111, 0, 0, - c[(g + 4) >> 2] | 0 + c[(g + 4) >> 2] | 0, ) | 0; c[(i + 28) >> 2] = h; if (!h) { @@ -206945,7 +207004,7 @@ c[e >> 2] | 0, 0, 0, - -1 + -1, ) | 0; c[(i + 24) >> 2] = h; if (!h) { @@ -206962,7 +207021,7 @@ 0, c[(g + 16) >> 2] | 0, - -1 + -1, ) | 0; c[(i + 28) >> 2] = h; if (!h) { @@ -206984,7 +207043,7 @@ ((c[e >> 2] | 0) + 4) >> 2 - ] | 0 + ] | 0, ) | 0; if (d | 0) break a; d = T5(b, 0) | 0; @@ -206992,7 +207051,7 @@ d = S5( b, - c[c[e >> 2] >> 2] | 0 + c[c[e >> 2] >> 2] | 0, ) | 0; if (d | 0) break a; d = T5(b, 0) | 0; @@ -207012,7 +207071,7 @@ ((c[e >> 2] | 0) + 4) >> 2 - ] | 0 + ] | 0, ) | 0; if (d | 0) break a; d = T5(b, 0) | 0; @@ -207020,7 +207079,7 @@ d = S5( b, - c[c[e >> 2] >> 2] | 0 + c[c[e >> 2] >> 2] | 0, ) | 0; if (d | 0) break a; d = T5(b, 0) | 0; @@ -207037,7 +207096,7 @@ b, c[ c[(i + 4) >> 2] >> 2 - ] | 0 + ] | 0, ) | 0; if (d | 0) break a; d = T5(b, 0) | 0; @@ -207062,7 +207121,7 @@ c[(e + 24) >> 2] | 0, c[(g + 24) >> 2] | 0, 0, - 0 + 0, ) | 0; c[(i + 24) >> 2] = h; if (!h) { @@ -207077,7 +207136,7 @@ c[((c[f >> 2] | 0) + 28) >> 2] | 0, 0, - 0 + 0, ) | 0; c[(i + 28) >> 2] = h; if (!h) { @@ -207149,7 +207208,7 @@ 2 ] | 0, e, - c[k >> 2] | 0 + c[k >> 2] | 0, ) | 0; U1(e); if (!(c[h >> 2] | 0)) { @@ -207195,7 +207254,7 @@ 2 ] | 0, e, - c[k >> 2] | 0 + c[k >> 2] | 0, ) | 0; U1(e); if (!(c[i >> 2] | 0)) { @@ -207244,7 +207303,7 @@ c[(h + 24) >> 2] | 0, b, d, - e + e, ) | 0), (h | 0) != 0) : 0 @@ -207270,7 +207329,7 @@ c[((c[g >> 2] | 0) + 24) >> 2] | 0, b, d, - e + e, ) | 0; if (a | 0) break a; a = O5(c[f >> 2] | 0, b, d, e) | 0; @@ -208121,7 +208180,7 @@ c[f >> 2] | 0, c[(f + 4) >> 2] | 0, c[(f + 8) >> 2] | 0, - a[(f + 12) >> 0] & 1 + a[(f + 12) >> 0] & 1, ) | 0; c[n >> 2] = e; if (!e) { @@ -209581,7 +209640,7 @@ 0, 0, 0, - c[Q >> 2] << 2 + c[Q >> 2] << 2, ) | 0; c[(o + 20) >> 2] = E; if (!E) { @@ -209716,7 +209775,7 @@ c[S >> 2] & -9, a, j, - s + s, ); E = c[(m + (o << 3)) >> 2] | 0; o = @@ -209979,7 +210038,7 @@ 24) >> 2 ] | - 0 + 0, ) | 0 ) ) @@ -210010,7 +210069,7 @@ c[ e >> 2 - ] | 0 + ] | 0, ) | 0 ) break; @@ -210023,7 +210082,7 @@ e >> 2 ] | - 0 + 0, ) | 0 ) ) @@ -210039,7 +210098,7 @@ 2 ] | 0, q, - c[S >> 2] & 2 + c[S >> 2] & 2, ) | 0 ) break f; @@ -210066,7 +210125,7 @@ 0, 0, 0, - c[Q >> 2] << 2 + c[Q >> 2] << 2, ) | 0; c[(e + 20) >> 2] = x; if (!x) { @@ -210147,9 +210206,8 @@ if (!r) break; t = c[(p + 8) >> 2] | 0; if ((c[(t + 20) >> 2] & 256) | 0) - c[ - (k + (c[(p + 12) >> 2] << 2)) >> 2 - ] = 0; + c[(k + (c[(p + 12) >> 2] << 2)) >> 2] = + 0; s = c[p >> 2] | 0; u = c[(p + 4) >> 2] | 0; c[T >> 2] = c[(p + 16) >> 2]; @@ -210531,7 +210589,7 @@ Y6( c[(g + 4) >> 2] | 0, -1, - P | 0 + P | 0, ) | 0; k = c[(a + 16) >> 2] | 0; if ( @@ -210908,7 +210966,7 @@ 24) >> 2 ] | - 0 + 0, ) | 0 ) ) { @@ -210941,7 +210999,7 @@ c[ a >> 2 - ] | 0 + ] | 0, ) | 0 ) break; @@ -210954,7 +211012,7 @@ a >> 2 ] | - 0 + 0, ) | 0 ) ) { @@ -210972,7 +211030,7 @@ 2 ] | 0, h, - c[R >> 2] & 2 + c[R >> 2] & 2, ) | 0 ) { a = o; @@ -211038,7 +211096,7 @@ N, c[J >> 2] | 0, o, - b + b, ) | 0 ) ) { @@ -211822,7 +211880,7 @@ (((((p | 0) < 0 ? -1 : 0) >> 31) | (((p | 0) < 0 ? -1 : 0) << 1)) & l) | - 0 + 0, ) | 0; b = z; h = (h - 1) | 0; @@ -212195,7 +212253,7 @@ h | 0, i | 0, j | 0, - k | 0 + k | 0, ) | 0 ); } @@ -212278,7 +212336,7 @@ g | 0, h | 0, i | 0, - j | 0 + j | 0, ); } function t7() { @@ -213388,7 +213446,7 @@ // EMSCRIPTEN_END_ASM Module.asmGlobalArg, Module.asmLibraryArg, - buffer + buffer, ); var ___errno_location = (Module["___errno_location"] = asm["___errno_location"]); @@ -213484,7 +213542,7 @@ throw ( "could not load memory initializer " + memoryInitializer ); - } + }, ); } var memoryInitializerBytes = tryParseAsDataURI(memoryInitializer); @@ -213496,7 +213554,7 @@ var response = request.response; if (request.status !== 200 && request.status !== 0) { var data = tryParseAsDataURI( - Module["memoryInitializerRequestURL"] + Module["memoryInitializerRequestURL"], ); if (data) { response = data.buffer; @@ -213505,7 +213563,7 @@ "a problem seems to have happened with Module.memoryInitializerRequest, status: " + request.status + ", retrying " + - memoryInitializer + memoryInitializer, ); doBrowserLoad(); return; @@ -213518,7 +213576,7 @@ } else { Module["memoryInitializerRequest"].addEventListener( "load", - useRequest + useRequest, ); } } else { @@ -213640,7 +213698,7 @@ if (format == "png-image-element") { return Viz.svgXmlToPngImageElement( render(src, "svg", engine, totalMemory, files), - scale + scale, ); } else { return render(src, format, engine, totalMemory, files); @@ -213656,7 +213714,7 @@ "vizCreateFile", "number", ["string", "string"], - [files[i].path, files[i].data] + [files[i].path, files[i].data], ); } @@ -213664,7 +213722,7 @@ "vizRenderFromString", "number", ["string", "string", "string"], - [src, format, engine] + [src, format, engine], ); var resultString = graphviz["Pointer_stringify"](resultPointer); @@ -213672,7 +213730,7 @@ "vizLastErrorMessage", "number", [], - [] + [], ); var errorMessageString = graphviz["Pointer_stringify"](errorMessagePointer); @@ -213688,7 +213746,7 @@ return btoa( encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) { return String.fromCharCode("0x" + p1); - }) + }), ); } @@ -213811,8 +213869,8 @@ "object" == typeof exports && "undefined" != typeof module ? n(exports) : "function" == typeof define && define.amd - ? define(["exports"], n) - : n((t.d3 = t.d3 || {})); + ? define(["exports"], n) + : n((t.d3 = t.d3 || {})); })(this, function (t) { "use strict"; function n(t, n) { @@ -213885,7 +213943,12 @@ if (null != (e = t[a]) && e >= e) for (r = i = e; ++a < o; ) null != (e = t[a]) && (r > e && (r = e), i < e && (i = e)); - } else for (; ++a < o; ) if (null != (e = n(t[a], a, t)) && e >= e) for (r = i = e; ++a < o; ) null != (e = n(t[a], a, t)) && (r > e && (r = e), i < e && (i = e)); + } else + for (; ++a < o; ) + if (null != (e = n(t[a], a, t)) && e >= e) + for (r = i = e; ++a < o; ) + null != (e = n(t[a], a, t)) && + (r > e && (r = e), i < e && (i = e)); return [r, i]; } var l = Array.prototype, @@ -213985,7 +214048,11 @@ for (; ++o < i; ) if (null != (e = t[o]) && e >= e) for (r = e; ++o < i; ) null != (e = t[o]) && e > r && (r = e); - } else for (; ++o < i; ) if (null != (e = n(t[o], o, t)) && e >= e) for (r = e; ++o < i; ) null != (e = n(t[o], o, t)) && e > r && (r = e); + } else + for (; ++o < i; ) + if (null != (e = n(t[o], o, t)) && e >= e) + for (r = e; ++o < i; ) + null != (e = n(t[o], o, t)) && e > r && (r = e); return r; } function T(t) { @@ -214003,7 +214070,11 @@ for (; ++o < i; ) if (null != (e = t[o]) && e >= e) for (r = e; ++o < i; ) null != (e = t[o]) && r > e && (r = e); - } else for (; ++o < i; ) if (null != (e = n(t[o], o, t)) && e >= e) for (r = e; ++o < i; ) null != (e = n(t[o], o, t)) && r > e && (r = e); + } else + for (; ++o < i; ) + if (null != (e = n(t[o], o, t)) && e >= e) + for (r = e; ++o < i; ) + null != (e = n(t[o], o, t)) && r > e && (r = e); return r; } function k(t) { @@ -214079,19 +214150,19 @@ .enter() .insert("path", ".tick") .attr("class", "domain") - .attr("stroke", "currentColor") + .attr("stroke", "currentColor"), )), (x = x.merge(M)), (N = N.merge( M.append("line") .attr("stroke", "currentColor") - .attr(f + "2", c * o) + .attr(f + "2", c * o), )), (A = A.merge( M.append("text") .attr("fill", "currentColor") .attr(f, c * p) - .attr("dy", t === z ? "0em" : t === D ? "0.71em" : "0.32em") + .attr("dy", t === z ? "0em" : t === D ? "0.71em" : "0.32em"), )), l !== b && ((m = m.transition(l)), @@ -214118,8 +214189,8 @@ ? "M" + c * a + "," + g + "H0.5V" + y + "H" + c * a : "M0.5," + g + "V" + y : a - ? "M" + g + "," + c * a + "V0.5H" + y + "V" + c * a - : "M" + g + ",0.5H" + y + ? "M" + g + "," + c * a + "V0.5H" + y + "V" + c * a + : "M" + g + ",0.5H" + y, ), x.attr("opacity", 1).attr("transform", function (t) { return s(_(t)); @@ -214133,7 +214204,7 @@ .attr("font-family", "sans-serif") .attr( "text-anchor", - t === R ? "start" : t === q ? "end" : "middle" + t === R ? "start" : t === q ? "end" : "middle", ), b.each(function () { this.__axis = _; @@ -214484,7 +214555,7 @@ this.addEventListener( u.type, (u.listener = f), - (u.capture = e) + (u.capture = e), ), void (u.value = n) ); @@ -214739,34 +214810,34 @@ }; } : "function" == typeof n - ? e.local + ? e.local + ? function (t, n) { + return function () { + var e = n.apply(this, arguments); + null == e + ? this.removeAttributeNS(t.space, t.local) + : this.setAttributeNS(t.space, t.local, e); + }; + } + : function (t, n) { + return function () { + var e = n.apply(this, arguments); + null == e + ? this.removeAttribute(t) + : this.setAttribute(t, e); + }; + } + : e.local ? function (t, n) { return function () { - var e = n.apply(this, arguments); - null == e - ? this.removeAttributeNS(t.space, t.local) - : this.setAttributeNS(t.space, t.local, e); + this.setAttributeNS(t.space, t.local, n); }; } : function (t, n) { return function () { - var e = n.apply(this, arguments); - null == e - ? this.removeAttribute(t) - : this.setAttribute(t, e); + this.setAttribute(t, n); }; - } - : e.local - ? function (t, n) { - return function () { - this.setAttributeNS(t.space, t.local, n); - }; - } - : function (t, n) { - return function () { - this.setAttribute(t, n); - }; - })(e, n) + })(e, n), ); }, style: function (t, n, e) { @@ -214779,19 +214850,19 @@ }; } : "function" == typeof n - ? function (t, n, e) { - return function () { - var r = n.apply(this, arguments); - null == r - ? this.style.removeProperty(t) - : this.style.setProperty(t, r, e); - }; - } - : function (t, n, e) { - return function () { - this.style.setProperty(t, n, e); - }; - })(t, n, null == e ? "" : e) + ? function (t, n, e) { + return function () { + var r = n.apply(this, arguments); + null == r + ? this.style.removeProperty(t) + : this.style.setProperty(t, r, e); + }; + } + : function (t, n, e) { + return function () { + this.style.setProperty(t, n, e); + }; + })(t, n, null == e ? "" : e), ) : ct(this.node(), t); }, @@ -214805,17 +214876,17 @@ }; } : "function" == typeof n - ? function (t, n) { - return function () { - var e = n.apply(this, arguments); - null == e ? delete this[t] : (this[t] = e); - }; - } - : function (t, n) { - return function () { - this[t] = n; - }; - })(t, n) + ? function (t, n) { + return function () { + var e = n.apply(this, arguments); + null == e ? delete this[t] : (this[t] = e); + }; + } + : function (t, n) { + return function () { + this[t] = n; + }; + })(t, n), ) : this.node()[t]; }, @@ -214834,16 +214905,16 @@ }; } : n - ? function (t) { - return function () { - ht(this, t); - }; - } - : function (t) { - return function () { - dt(this, t); - }; - })(e, n) + ? function (t) { + return function () { + ht(this, t); + }; + } + : function (t) { + return function () { + dt(this, t); + }; + })(e, n), ); }, text: function (t) { @@ -214862,7 +214933,7 @@ return function () { this.textContent = t; }; - })(t) + })(t), ) : this.node().textContent; }, @@ -214882,7 +214953,7 @@ return function () { this.innerHTML = t; }; - })(t) + })(t), ) : this.node().innerHTML; }, @@ -214904,7 +214975,7 @@ return this.select(function () { return this.insertBefore( e.apply(this, arguments), - r.apply(this, arguments) || null + r.apply(this, arguments) || null, ); }); }, @@ -214960,7 +215031,7 @@ return function () { return kt(this, t, n); }; - })(t, n) + })(t, n), ); }, }; @@ -215253,27 +215324,37 @@ (((n = parseInt(n[1], 16)) >> 8) & 15) | ((n >> 4) & 240), ((n >> 4) & 15) | (240 & n), ((15 & n) << 4) | (15 & n), - 1 + 1, ) : (n = rn.exec(t)) - ? dn(parseInt(n[1], 16)) - : (n = on.exec(t)) - ? new yn(n[1], n[2], n[3], 1) - : (n = an.exec(t)) - ? new yn((255 * n[1]) / 100, (255 * n[2]) / 100, (255 * n[3]) / 100, 1) - : (n = un.exec(t)) - ? pn(n[1], n[2], n[3], n[4]) - : (n = cn.exec(t)) - ? pn((255 * n[1]) / 100, (255 * n[2]) / 100, (255 * n[3]) / 100, n[4]) - : (n = fn.exec(t)) - ? bn(n[1], n[2] / 100, n[3] / 100, 1) - : (n = sn.exec(t)) - ? bn(n[1], n[2] / 100, n[3] / 100, n[4]) - : ln.hasOwnProperty(t) - ? dn(ln[t]) - : "transparent" === t - ? new yn(NaN, NaN, NaN, 0) - : null + ? dn(parseInt(n[1], 16)) + : (n = on.exec(t)) + ? new yn(n[1], n[2], n[3], 1) + : (n = an.exec(t)) + ? new yn( + (255 * n[1]) / 100, + (255 * n[2]) / 100, + (255 * n[3]) / 100, + 1, + ) + : (n = un.exec(t)) + ? pn(n[1], n[2], n[3], n[4]) + : (n = cn.exec(t)) + ? pn( + (255 * n[1]) / 100, + (255 * n[2]) / 100, + (255 * n[3]) / 100, + n[4], + ) + : (n = fn.exec(t)) + ? bn(n[1], n[2] / 100, n[3] / 100, 1) + : (n = sn.exec(t)) + ? bn(n[1], n[2] / 100, n[3] / 100, n[4]) + : ln.hasOwnProperty(t) + ? dn(ln[t]) + : "transparent" === t + ? new yn(NaN, NaN, NaN, 0) + : null ); } function dn(t) { @@ -215305,8 +215386,8 @@ r <= 0 ? (t = n = e = NaN) : e <= 0 || e >= 1 - ? (t = n = NaN) - : n <= 0 && (t = NaN), + ? (t = n = NaN) + : n <= 0 && (t = NaN), new xn(t, n, e, r) ); } @@ -215330,8 +215411,8 @@ n === o ? (e - r) / u + 6 * (e < r) : e === o - ? (r - n) / u + 2 - : (n - e) / u + 4), + ? (r - n) / u + 2 + : (n - e) / u + 4), (u /= c < 0.5 ? o + i : 2 - o - i), (a *= 60)) : (u = c > 0 && c < 1 ? 0 : a), @@ -215349,10 +215430,10 @@ (t < 60 ? n + ((e - n) * t) / 60 : t < 180 - ? e - : t < 240 - ? n + ((e - n) * (240 - t)) / 60 - : n) + ? e + : t < 240 + ? n + ((e - n) * (240 - t)) / 60 + : n) ); } Zt(Jt, hn, { @@ -215414,7 +215495,7 @@ (1 === t ? ")" : ", " + t + ")") ); }, - }) + }), ), Zt( xn, @@ -215442,7 +215523,7 @@ wn(t >= 240 ? t - 240 : t + 120, i, r), wn(t, i, r), wn(t < 120 ? t + 240 : t - 120, i, r), - this.opacity + this.opacity, ); }, displayable: function () { @@ -215454,7 +215535,7 @@ this.opacity <= 1 ); }, - }) + }), ); var Mn = Math.PI / 180, Nn = 180 / Math.PI, @@ -215516,7 +215597,7 @@ n < 0 ? n + 360 : n, Math.sqrt(t.a * t.a + t.b * t.b), t.l, - t.opacity + t.opacity, ); } function Yn(t, n, e, r) { @@ -215534,7 +215615,7 @@ this.l + 18 * (null == t ? 1 : t), this.a, this.b, - this.opacity + this.opacity, ); }, darker: function (t) { @@ -215542,7 +215623,7 @@ this.l - 18 * (null == t ? 1 : t), this.a, this.b, - this.opacity + this.opacity, ); }, rgb: function () { @@ -215553,14 +215634,14 @@ Un( 3.1338561 * (n = An * Ln(n)) - 1.6168667 * (t = Tn * Ln(t)) - - 0.4906146 * (e = Sn * Ln(e)) + 0.4906146 * (e = Sn * Ln(e)), ), Un(-0.9787684 * n + 1.9161415 * t + 0.033454 * e), Un(0.0719453 * n - 0.2289914 * t + 1.4052427 * e), - this.opacity + this.opacity, ); }, - }) + }), ), Zt( Fn, @@ -215571,7 +215652,7 @@ this.h, this.c, this.l + 18 * (null == t ? 1 : t), - this.opacity + this.opacity, ); }, darker: function (t) { @@ -215579,13 +215660,13 @@ this.h, this.c, this.l - 18 * (null == t ? 1 : t), - this.opacity + this.opacity, ); }, rgb: function () { return zn(this).rgb(); }, - }) + }), ); var In = -0.14861, jn = 1.78277, @@ -215712,10 +215793,10 @@ 255 * (n + e * (In * r + jn * i)), 255 * (n + e * (Hn * r + Xn * i)), 255 * (n + e * (Gn * r)), - this.opacity + this.opacity, ); }, - }) + }), ); var ae = (function t(n) { var e = ie(n); @@ -215846,20 +215927,20 @@ : ("number" === r ? he : "string" === r - ? (e = hn(n)) - ? ((n = e), ae) - : ge - : n instanceof hn - ? ae - : n instanceof Date - ? le - : Array.isArray(n) - ? se - : ("function" != typeof n.valueOf && - "function" != typeof n.toString) || - isNaN(n) - ? de - : he)(t, n); + ? (e = hn(n)) + ? ((n = e), ae) + : ge + : n instanceof hn + ? ae + : n instanceof Date + ? le + : Array.isArray(n) + ? se + : ("function" != typeof n.valueOf && + "function" != typeof n.toString) || + isNaN(n) + ? de + : he)(t, n); } function _e(t, n) { return ( @@ -215958,12 +216039,12 @@ +t[2], +t[3], +t[4], - +t[5] + +t[5], )); }, "px, ", "px)", - "deg)" + "deg)", ), ke = Te( function (t) { @@ -215972,7 +216053,7 @@ : (we || (we = document.createElementNS( "http://www.w3.org/2000/svg", - "g" + "g", )), we.setAttribute("transform", t), (t = we.transform.baseVal.consolidate()) @@ -215981,7 +216062,7 @@ }, ", ", ")", - ")" + ")", ), Ee = Math.SQRT2, Ce = 2, @@ -216164,7 +216245,7 @@ r.stop(), t(e + n); }, n, - e + e, ), r ); @@ -216269,7 +216350,7 @@ e.delay <= t && o(t - e.delay); }, 0, - e.time + e.time, )); })(t, e, { name: n, @@ -216317,7 +216398,7 @@ t, t.__data__, e.index, - e.group + e.group, ), delete o[i]) : (a = !1); @@ -216342,10 +216423,10 @@ "number" == typeof n ? he : n instanceof hn - ? ae - : (e = hn(n)) - ? ((n = e), ae) - : ge + ? ae + : (e = hn(n)) + ? ((n = e), ae) + : ge )(t, n); } var Tr = Pt.prototype.constructor; @@ -216512,7 +216593,7 @@ u = a.on; u !== r && (i = (r = u).copy()).on(n, e), (a.on = i); }; - })(e, t, n) + })(e, t, n), ); }, attr: function (t, n) { @@ -216531,12 +216612,12 @@ if (null != c) return (a = this.getAttributeNS( t.space, - t.local + t.local, )) === (u = c + "") ? null : a === r && u === i - ? o - : ((i = u), (o = n((r = a), c))); + ? o + : ((i = u), (o = n((r = a), c))); this.removeAttributeNS(t.space, t.local); }; } @@ -216551,50 +216632,50 @@ (u = c + "") ? null : a === r && u === i - ? o - : ((i = u), (o = n((r = a), c))); + ? o + : ((i = u), (o = n((r = a), c))); this.removeAttribute(t); }; })(e, r, Nr(this, "attr." + t, n)) : null == n - ? (e.local - ? function (t) { - return function () { - this.removeAttributeNS(t.space, t.local); - }; - } - : function (t) { - return function () { - this.removeAttribute(t); - }; - })(e) - : (e.local - ? function (t, n, e) { - var r, - i, - o = e + ""; - return function () { - var a = this.getAttributeNS(t.space, t.local); - return a === o - ? null - : a === r - ? i - : (i = n((r = a), e)); - }; - } - : function (t, n, e) { - var r, - i, - o = e + ""; - return function () { - var a = this.getAttribute(t); - return a === o - ? null - : a === r - ? i - : (i = n((r = a), e)); - }; - })(e, r, n) + ? (e.local + ? function (t) { + return function () { + this.removeAttributeNS(t.space, t.local); + }; + } + : function (t) { + return function () { + this.removeAttribute(t); + }; + })(e) + : (e.local + ? function (t, n, e) { + var r, + i, + o = e + ""; + return function () { + var a = this.getAttributeNS(t.space, t.local); + return a === o + ? null + : a === r + ? i + : (i = n((r = a), e)); + }; + } + : function (t, n, e) { + var r, + i, + o = e + ""; + return function () { + var a = this.getAttribute(t); + return a === o + ? null + : a === r + ? i + : (i = n((r = a), e)); + }; + })(e, r, n), ); }, attrTween: function (t, n) { @@ -216619,7 +216700,7 @@ this.setAttributeNS( t.space, t.local, - n(e) + n(e), ); }; })(t, i)), @@ -216645,7 +216726,7 @@ ); } return (i._value = n), i; - })(r, n) + })(r, n), ); }, style: function (t, n, e) { @@ -216661,63 +216742,67 @@ return o === a ? null : o === e && a === r - ? i - : (i = n((e = o), (r = a))); + ? i + : (i = n((e = o), (r = a))); }; - })(t, r) + })(t, r), ).on("end.style." + t, Sr(t)) : "function" == typeof n - ? this.styleTween( - t, - (function (t, n, e) { - var r, i, o; - return function () { - var a = ct(this, t), - u = e(this), - c = u + ""; - return ( - null == u && - (this.style.removeProperty(t), - (c = u = ct(this, t))), - a === c - ? null - : a === r && c === i - ? o - : ((i = c), (o = n((r = a), u))) - ); - }; - })(t, r, Nr(this, "style." + t, n)) - ).each( - (function (t, n) { - var e, - r, - i, - o, - a = "style." + n, - u = "end." + a; - return function () { - var c = xr(this, t), - f = c.on, - s = null == c.value[a] ? o || (o = Sr(n)) : void 0; - (f === e && i === s) || - (r = (e = f).copy()).on(u, (i = s)), - (c.on = r); - }; - })(this._id, t) - ) - : this.styleTween( - t, - (function (t, n, e) { - var r, - i, - o = e + ""; - return function () { - var a = ct(this, t); - return a === o ? null : a === r ? i : (i = n((r = a), e)); - }; - })(t, r, n), - e - ).on("end.style." + t, null); + ? this.styleTween( + t, + (function (t, n, e) { + var r, i, o; + return function () { + var a = ct(this, t), + u = e(this), + c = u + ""; + return ( + null == u && + (this.style.removeProperty(t), + (c = u = ct(this, t))), + a === c + ? null + : a === r && c === i + ? o + : ((i = c), (o = n((r = a), u))) + ); + }; + })(t, r, Nr(this, "style." + t, n)), + ).each( + (function (t, n) { + var e, + r, + i, + o, + a = "style." + n, + u = "end." + a; + return function () { + var c = xr(this, t), + f = c.on, + s = null == c.value[a] ? o || (o = Sr(n)) : void 0; + (f === e && i === s) || + (r = (e = f).copy()).on(u, (i = s)), + (c.on = r); + }; + })(this._id, t), + ) + : this.styleTween( + t, + (function (t, n, e) { + var r, + i, + o = e + ""; + return function () { + var a = ct(this, t); + return a === o + ? null + : a === r + ? i + : (i = n((r = a), e)); + }; + })(t, r, n), + e, + ).on("end.style." + t, null); }, styleTween: function (t, n, e) { var r = "style." + (t += ""); @@ -216743,7 +216828,7 @@ ); } return (o._value = n), o; - })(t, n, null == e ? "" : e) + })(t, n, null == e ? "" : e), ); }, text: function (t) { @@ -216760,7 +216845,7 @@ return function () { this.textContent = t; }; - })(null == t ? "" : t + "") + })(null == t ? "" : t + ""), ); }, remove: function () { @@ -216771,7 +216856,7 @@ var n = this.parentNode; for (var e in this.__transition) if (+e !== t) return; n && n.removeChild(this); - }) + }), ); var t; }, @@ -216823,7 +216908,7 @@ } o.tween = i; }; - })(e, t, n) + })(e, t, n), ); }, delay: function (t) { @@ -216843,7 +216928,7 @@ mr(this, t).delay = n; } ); - })(n, t) + })(n, t), ) : wr(this.node(), n).delay; }, @@ -216864,7 +216949,7 @@ xr(this, t).duration = n; } ); - })(n, t) + })(n, t), ) : wr(this.node(), n).duration; }, @@ -216877,7 +216962,7 @@ return function () { xr(this, t).ease = n; }; - })(n, t) + })(n, t), ) : wr(this.node(), n).ease; }, @@ -216956,10 +217041,10 @@ return (t = +t) < jr ? Jr * t * t : t < Xr - ? Jr * (t -= Hr) * t + Gr - : t < $r - ? Jr * (t -= Vr) * t + Wr - : Jr * (t -= Zr) * t + Qr; + ? Jr * (t -= Hr) * t + Gr + : t < $r + ? Jr * (t -= Vr) * t + Wr + : Jr * (t -= Zr) * t + Qr; } var ti = (function t(n) { function e(t) { @@ -217301,8 +217386,8 @@ "selection" === (t.event.metaKey ? (m = "overlay") : m) ? pi : t.event.altKey - ? yi - : gi, + ? yi + : gi, w = n === bi ? null : Ni[m], M = n === _i ? null : Ai[m], N = Ei(b), @@ -217333,7 +217418,7 @@ q.on("touchmove.brush", O, !0).on( "touchend.brush touchcancel.brush", Y, - !0 + !0, ); else { var U = zt(t.event.view) @@ -217364,7 +217449,7 @@ } di(); }, - !0 + !0, ) .on( "keyup.brush", @@ -217401,7 +217486,7 @@ } di(); }, - !0 + !0, ) .on("mousemove.brush", O, !0) .on("mouseup.brush", Y, !0); @@ -217502,7 +217587,12 @@ e = null; }, 500)), q.on("touchmove.brush touchend.brush touchcancel.brush", null); - } else jt(t.event.view, g), U.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null); + } else + jt(t.event.view, g), + U.on( + "keydown.brush keyup.brush mousemove.brush mouseup.brush", + null, + ); q.attr("pointer-events", "all"), L.attr("cursor", xi.overlay), N.selection && (T = N.selection), @@ -217533,7 +217623,7 @@ "function" == typeof e ? e.apply(this, arguments) : e, - r.extent + r.extent, ), u = ye(o, a); function s(n) { @@ -217548,7 +217638,7 @@ r = this.__brush, i = n.input( "function" == typeof e ? e.apply(this, t) : e, - r.extent + r.extent, ), o = f(this, t).beforestart(); Mr(this), @@ -218118,21 +218208,21 @@ n.ring.push(c), (d[(n.end = s)] = n)) : (n = h[s]) - ? (e = d[f]) - ? (delete h[n.start], - delete d[e.end], - n === e - ? (n.ring.push(c), i(n.ring)) - : (h[e.start] = d[n.end] = - { - start: e.start, - end: n.end, - ring: e.ring.concat(n.ring), - })) - : (delete h[n.start], - n.ring.unshift(r), - (h[(n.start = f)] = n)) - : (h[f] = d[s] = {start: f, end: s, ring: [r, c]}); + ? (e = d[f]) + ? (delete h[n.start], + delete d[e.end], + n === e + ? (n.ring.push(c), i(n.ring)) + : (h[e.start] = d[n.end] = + { + start: e.start, + end: n.end, + ring: e.ring.concat(n.ring), + })) + : (delete h[n.start], + n.ring.unshift(r), + (h[(n.start = f)] = n)) + : (h[f] = d[s] = {start: f, end: s, ring: [r, c]}); } ho[s << 3].forEach(p); })(e, i, function (t) { @@ -218195,8 +218285,8 @@ "function" == typeof t ? t : Array.isArray(t) - ? uo(oo.call(t)) - : uo(t)), + ? uo(oo.call(t)) + : uo(t)), i) : e; }), @@ -218247,7 +218337,7 @@ return JSON.stringify(t) + ": d[" + n + "]"; }) .join(",") + - "}" + "}", ); } function To(t) { @@ -218276,8 +218366,8 @@ : ((n = t.getUTCFullYear()) < 0 ? "-" + So(-n, 6) : n > 9999 - ? "+" + So(n, 6) - : So(n, 4)) + + ? "+" + So(n, 6) + : So(n, 4)) + "-" + So(t.getUTCMonth() + 1, 2) + "-" + @@ -218293,10 +218383,10 @@ So(o, 3) + "Z" : i - ? "T" + So(e, 2) + ":" + So(r, 2) + ":" + So(i, 2) + "Z" - : r || e - ? "T" + So(e, 2) + ":" + So(r, 2) + "Z" - : ""); + ? "T" + So(e, 2) + ":" + So(r, 2) + ":" + So(i, 2) + "Z" + : r || e + ? "T" + So(e, 2) + ":" + So(r, 2) + "Z" + : ""); } function Eo(t) { var n = new RegExp('["' + t + "\n\r]"), @@ -218325,8 +218415,8 @@ (n = a) >= o ? (c = !0) : (r = t.charCodeAt(a++)) === Mo - ? (f = !0) - : r === No && ((f = !0), t.charCodeAt(a) === Mo && ++a), + ? (f = !0) + : r === No && ((f = !0), t.charCodeAt(a) === Mo && ++a), t.slice(i + 1, n - 1).replace(/""/g, '"') ); } @@ -218364,10 +218454,10 @@ return null == t ? "" : t instanceof Date - ? ko(t) - : n.test((t += "")) - ? '"' + t.replace(/"/g, '""') + '"' - : t; + ? ko(t) + : n.test((t += "")) + ? '"' + t.replace(/"/g, '""') + '"' + : t; } return { parse: function (t, n) { @@ -218649,11 +218739,11 @@ return arguments.length ? this.cover(+t[0][0], +t[0][1]).cover(+t[1][0], +t[1][1]) : isNaN(this._x0) - ? void 0 - : [ - [this._x0, this._y0], - [this._x1, this._y1], - ]; + ? void 0 + : [ + [this._x0, this._y0], + [this._x1, this._y1], + ]; }), (fa.find = function (t, n, e) { var r, @@ -218697,7 +218787,7 @@ new ra(v[3], g, y, a, u), new ra(v[2], i, y, g, u), new ra(v[1], g, o, a, y), - new ra(v[0], i, o, g, y) + new ra(v[0], i, o, g, y), ), (f = ((n >= y) << 1) | (t >= g)) && ((c = p[p.length - 1]), @@ -218761,13 +218851,13 @@ r ? (i ? (r.next = i) : delete r.next, this) : n - ? (i ? (n[l] = i) : delete n[l], - (d = n[0] || n[1] || n[2] || n[3]) && - d === (n[3] || n[2] || n[1] || n[0]) && - !d.length && - (e ? (e[h] = d) : (this._root = d)), - this) - : ((this._root = i), this) + ? (i ? (n[l] = i) : delete n[l], + (d = n[0] || n[1] || n[2] || n[3]) && + d === (n[3] || n[2] || n[1] || n[0]) && + !d.length && + (e ? (e[h] = d) : (this._root = d)), + this) + : ((this._root = i), this) ); }), (fa.removeAll = function (t) { @@ -218890,8 +218980,8 @@ return i < 0 ? "0." + new Array(-i).join("0") + r : r.length > i + 1 - ? r.slice(0, i + 1) + "." + r.slice(i + 1) - : r + new Array(i - r.length + 2).join("0"); + ? r.slice(0, i + 1) + "." + r.slice(i + 1) + : r + new Array(i - r.length + 2).join("0"); } (wa.prototype = Ma.prototype), (Ma.prototype.toString = function () { @@ -218947,10 +219037,12 @@ return o === a ? r : o > a - ? r + new Array(o - a + 1).join("0") - : o > 0 - ? r.slice(0, o) + "." + r.slice(o) - : "0." + new Array(1 - o).join("0") + _a(t, Math.max(0, n + o - 1))[0]; + ? r + new Array(o - a + 1).join("0") + : o > 0 + ? r.slice(0, o) + "." + r.slice(o) + : "0." + + new Array(1 - o).join("0") + + _a(t, Math.max(0, n + o - 1))[0]; }, X: function (t) { return Math.round(t).toString(16).toUpperCase(); @@ -219034,8 +219126,8 @@ "$" === f ? i[0] : "#" === f && /[boxX]/.test(v) - ? "0" + v.toLowerCase() - : "", + ? "0" + v.toLowerCase() + : "", y = "$" === f ? i[1] : /[%p]/.test(v) ? u : "", _ = Aa[v], b = /[defgprs%]/.test(v); @@ -219075,8 +219167,8 @@ ? c : "-" : "-" === c || "(" === c - ? "" - : c) + m), + ? "" + : c) + m), (x = ("s" === v ? ka[8 + ma / 3] : "") + x + @@ -219118,8 +219210,8 @@ null == d ? 6 : /[gprs]/.test(v) - ? Math.max(1, Math.min(21, d)) - : Math.max(0, Math.min(20, d))), + ? Math.max(1, Math.min(21, d)) + : Math.max(0, Math.min(20, d))), (m.toString = function () { return t + ""; }), @@ -219150,7 +219242,7 @@ function za(t, n) { return Math.max( 0, - 3 * Math.max(-8, Math.min(8, Math.floor(ba(n) / 3))) - ba(Math.abs(t)) + 3 * Math.max(-8, Math.min(8, Math.floor(ba(n) / 3))) - ba(Math.abs(t)), ); } function Ra(t, n) { @@ -219398,8 +219490,8 @@ bu < 0 ? ((Ru = -(qu = 180)), (Du = -(Lu = 90))) : oc > Oa - ? (Lu = 90) - : oc < -Oa && (Du = -90), + ? (Lu = 90) + : oc < -Oa && (Du = -90), (Iu[0] = Ru), (Iu[1] = qu); }, @@ -219421,17 +219513,17 @@ f ^ (u * Uu < c && c < u * t) ? (o = i[1] * Ha) > Lu && (Lu = o) : f ^ (u * Uu < (c = ((c + 360) % 360) - 180) && c < u * t) - ? (o = -i[1] * Ha) < Du && (Du = o) - : (n < Du && (Du = n), n > Lu && (Lu = n)), + ? (o = -i[1] * Ha) < Du && (Du = o) + : (n < Du && (Du = n), n > Lu && (Lu = n)), f ? t < Uu ? pc(Ru, t) > pc(Ru, qu) && (qu = t) : pc(t, qu) > pc(Ru, qu) && (Ru = t) : qu >= Ru - ? (t < Ru && (Ru = t), t > qu && (qu = t)) - : t > Uu - ? pc(Ru, t) > pc(Ru, qu) && (qu = t) - : pc(t, qu) > pc(Ru, qu) && (Ru = t); + ? (t < Ru && (Ru = t), t > qu && (qu = t)) + : t > Uu + ? pc(Ru, t) > pc(Ru, qu) && (qu = t) + : pc(t, qu) > pc(Ru, qu) && (Ru = t); } else Fu.push((Iu = [(Ru = t), (qu = t)])); n < Du && (Du = n), n > Lu && (Lu = n), (Yu = e), (Uu = t); } @@ -219510,9 +219602,9 @@ eu( (a = rc * o - ic * i) * a + (a = ic * r - ec * o) * a + - (a = ec * i - rc * r) * a + (a = ec * i - rc * r) * a, ), - ec * r + rc * i + ic * o + ec * r + rc * i + ic * o, ); (Hu += a), ($u += a * (ec + (ec = r))), @@ -219582,8 +219674,8 @@ ? Ec(Rc(t), Dc(n, e)) : Rc(t) : n || e - ? Dc(n, e) - : Cc; + ? Dc(n, e) + : Cc; } function zc(t) { return function (n, e) { @@ -219919,7 +220011,7 @@ ? Va( (tu(n) * (o = Wa(r)) * tu(e) - tu(r) * (i = Wa(n)) * tu(t)) / - (i * o * a) + (i * o * a), ) : (n + r) / 2; })(e, r, o, a)), @@ -219957,7 +220049,7 @@ (i = (e * o) / 2), r.point(-o, i), r.point(0, i), r.point(o, i); } else r.point(n[0], n[1]); }, - [-Ya, -Fa] + [-Ya, -Fa], ); function Wc(t) { var n = Wa(t), @@ -220034,8 +220126,8 @@ ? 0 : u(l, h) : v - ? u(l + (l < 0 ? Ya : -Ya), h) - : 0; + ? u(l + (l < 0 ? Ya : -Ya), h) + : 0; if ( (!n && (f = c = v) && t.lineStart(), v !== c && @@ -220081,7 +220173,7 @@ function (n, r, i, o) { Lc(o, t, e, i, n, r); }, - r ? [0, -t] : [-Ya, t - Ya] + r ? [0, -t] : [-Ya, t - Ya], ); } var Zc = 1e9, @@ -220105,16 +220197,16 @@ ? 0 : 3 : Ga(r[0] - e) < Oa - ? i > 0 - ? 2 - : 1 - : Ga(r[1] - n) < Oa - ? i > 0 - ? 1 - : 0 - : i > 0 - ? 3 - : 2; + ? i > 0 + ? 2 + : 1 + : Ga(r[1] - n) < Oa + ? i > 0 + ? 1 + : 0 + : i > 0 + ? 3 + : 2; } function u(t, n) { return c(t.x, n.x); @@ -220125,12 +220217,12 @@ return e !== r ? e - r : 0 === e - ? n[1] - t[1] - : 1 === e - ? t[0] - n[0] - : 2 === e - ? t[1] - n[1] - : n[0] - t[0]; + ? n[1] - t[1] + : 1 === e + ? t[0] - n[0] + : 2 === e + ? t[1] - n[1] + : n[0] - t[0]; } return function (a) { var c, @@ -220434,14 +220526,14 @@ .filter(function (t) { return Ga(t % p) > Oa; }) - .map(c) + .map(c), ) .concat( g(Za(o / d) * d, i, d) .filter(function (t) { return Ga(t % v) > Oa; }) - .map(f) + .map(f), ); } return ( @@ -220457,7 +220549,7 @@ s(r).concat( l(a).slice(1), s(e).reverse().slice(1), - l(u).reverse().slice(1) + l(u).reverse().slice(1), ), ], }; @@ -220609,10 +220701,10 @@ var t = Jf ? [Zf / Jf, Qf / Jf] : Wf - ? [Vf / Wf, $f / Wf] - : Gf - ? [Hf / Gf, Xf / Gf] - : [NaN, NaN]; + ? [Vf / Wf, $f / Wf] + : Gf + ? [Hf / Gf, Xf / Gf] + : [NaN, NaN]; return (Hf = Xf = Gf = Vf = $f = Wf = Zf = Qf = Jf = 0), t; }, }; @@ -220776,7 +220868,7 @@ u = +n[0][1] + (i - o * (e[1][1] + e[0][1])) / 2; t.scale(150 * o).translate([a, u]); }, - e + e, ); } function As(t, n, e) { @@ -220792,7 +220884,7 @@ a = -i * e[0][1]; t.scale(150 * i).translate([o, a]); }, - e + e, ); } function Ss(t, n, e) { @@ -220805,7 +220897,7 @@ a = (r - i * (e[1][1] + e[0][1])) / 2; t.scale(150 * i).translate([o, a]); }, - e + e, ); } (bs.prototype = { @@ -220914,7 +221006,7 @@ (x /= M), w, v, - g + g, ), g.point(S, k), e(S, k, A, m, x, w, f, s, l, h, d, p, v, g)); @@ -220967,7 +221059,7 @@ (d = o[1]), (p = o[2]), ks, - n + n, ), n.point(s, l); } @@ -221098,7 +221190,7 @@ return this.stream.point(r[0], r[1]); }, }); - })(e)(x(a(M((s = t))))) + })(e)(x(a(M((s = t))))), )); }), (A.preclip = function (t) { @@ -221121,15 +221213,15 @@ (w = +t[0][0]), (r = +t[0][1]), (i = +t[1][0]), - (o = +t[1][1]) + (o = +t[1][1]), )), k()) : null == w - ? null - : [ - [w, r], - [i, o], - ]; + ? null + : [ + [w, r], + [i, o], + ]; }), (A.scale = function (t) { return arguments.length ? ((l = +t), S()) : l; @@ -221280,14 +221372,14 @@ [u[0] + o, u[1] + o], ] : t === js - ? [ - [Math.max(u[0] - o, f), n], - [Math.min(u[0] + o, e), r], - ] - : [ - [f, Math.max(u[1] - o, n)], - [e, Math.min(u[1] + o, r)], - ] + ? [ + [Math.max(u[0] - o, f), n], + [Math.min(u[0] + o, e), r], + ] + : [ + [f, Math.max(u[1] - o, n)], + [e, Math.min(u[1] + o, r)], + ], ); } return ( @@ -221310,11 +221402,11 @@ (r = +t[1][1])), s()) : null == f - ? null - : [ - [f, n], - [e, r], - ]; + ? null + : [ + [f, n], + [e, r], + ]; }), s() ); @@ -222413,13 +222505,13 @@ ? (r = x( (u = Math.floor(u / r) * r), (c = Math.ceil(c / r) * r), - e + e, )) : r < 0 && (r = x( (u = Math.ceil(u * r) / r), (c = Math.floor(c * r) / r), - e + e, )), r > 0 ? ((i[o] = Math.floor(u / r) * r), @@ -222490,10 +222582,10 @@ return 10 === t ? qh : t === Math.E - ? Math.exp - : function (n) { - return Math.pow(t, n); - }; + ? Math.exp + : function (n) { + return Math.pow(t, n); + }; })(a)), o()[0] < 0 ? ((e = Lh(e)), (r = Lh(r)), n(Rh, Dh)) : n(Ph, zh), i @@ -222560,7 +222652,7 @@ ceil: function (t) { return r(Math.ceil(e(t))); }, - }) + }), ); }), i @@ -222606,8 +222698,8 @@ ? 1 === (e = +n) ? t(mh, mh) : 0.5 === e - ? t(Ih, jh) - : t(Fh(e), Fh(1 / e)) + ? t(Ih, jh) + : t(Fh(e), Fh(1 / e)) : e; }), Eh(n) @@ -222665,7 +222757,7 @@ if (t >= t) if (r < 0) for (; ++r <= 0; ) for (; n(t, -1), !e(t); ); else for (; --r >= 0; ) for (; n(t, 1), !e(t); ); - } + }, ); }), e && @@ -222690,7 +222782,7 @@ } : function (n) { return i.count(0, n) % t == 0; - } + }, ) : i : null @@ -222706,7 +222798,7 @@ }, function (t, n) { return n - t; - } + }, ); Wh.every = function (t) { return ( @@ -222722,7 +222814,7 @@ }, function (n, e) { return (e - n) / t; - } + }, ) : Wh : null @@ -222743,7 +222835,7 @@ }, function (t) { return t.getUTCSeconds(); - } + }, ), td = Kh.range, nd = $h( @@ -222758,13 +222850,16 @@ }, function (t) { return t.getMinutes(); - } + }, ), ed = nd.range, rd = $h( function (t) { t.setTime( - t - t.getMilliseconds() - 1e3 * t.getSeconds() - t.getMinutes() * Qh + t - + t.getMilliseconds() - + 1e3 * t.getSeconds() - + t.getMinutes() * Qh, ); }, function (t, n) { @@ -222775,7 +222870,7 @@ }, function (t) { return t.getHours(); - } + }, ), id = rd.range, od = $h( @@ -222793,7 +222888,7 @@ }, function (t) { return t.getDate() - 1; - } + }, ), ad = od.range; function ud(t) { @@ -222809,7 +222904,7 @@ return ( (n - t - (n.getTimezoneOffset() - t.getTimezoneOffset()) * Qh) / Jh ); - } + }, ); } var cd = ud(0), @@ -222842,7 +222937,7 @@ }, function (t) { return t.getMonth(); - } + }, ), Md = wd.range, Nd = $h( @@ -222857,7 +222952,7 @@ }, function (t) { return t.getFullYear(); - } + }, ); Nd.every = function (t) { return isFinite((t = Math.floor(t))) && t > 0 @@ -222869,7 +222964,7 @@ }, function (n, e) { n.setFullYear(n.getFullYear() + e * t); - } + }, ) : null; }; @@ -222886,7 +222981,7 @@ }, function (t) { return t.getUTCMinutes(); - } + }, ), Sd = Td.range, kd = $h( @@ -222901,7 +222996,7 @@ }, function (t) { return t.getUTCHours(); - } + }, ), Ed = kd.range, Cd = $h( @@ -222916,7 +223011,7 @@ }, function (t) { return t.getUTCDate() - 1; - } + }, ), Pd = Cd.range; function zd(t) { @@ -222930,7 +223025,7 @@ }, function (t, n) { return (n - t) / Jh; - } + }, ); } var Rd = zd(0), @@ -222963,7 +223058,7 @@ }, function (t) { return t.getUTCMonth(); - } + }, ), $d = Vd.range, Wd = $h( @@ -222978,7 +223073,7 @@ }, function (t) { return t.getUTCFullYear(); - } + }, ); Wd.every = function (t) { return isFinite((t = Math.floor(t))) && t > 0 @@ -222990,7 +223085,7 @@ }, function (n, e) { n.setUTCFullYear(n.getUTCFullYear() + e * t); - } + }, ) : null; }; @@ -223209,7 +223304,15 @@ (o.y = r.getFullYear()), (o.m = r.getMonth()), (o.d = r.getDate() + ((o.w + 6) % 7))); - } else ("W" in o || "U" in o) && ("w" in o || (o.w = "u" in o ? o.u % 7 : "W" in o ? 1 : 0), (i = "Z" in o ? Jd(Kd(o.y)).getUTCDay() : n(Kd(o.y)).getDay()), (o.m = 0), (o.d = "W" in o ? ((o.w + 6) % 7) + 7 * o.W - ((i + 5) % 7) : o.w + 7 * o.U - ((i + 6) % 7))); + } else + ("W" in o || "U" in o) && + ("w" in o || (o.w = "u" in o ? o.u % 7 : "W" in o ? 1 : 0), + (i = "Z" in o ? Jd(Kd(o.y)).getUTCDay() : n(Kd(o.y)).getDay()), + (o.m = 0), + (o.d = + "W" in o + ? ((o.w + 6) % 7) + 7 * o.W - ((i + 5) % 7) + : o.w + 7 * o.U - ((i + 6) % 7))); return "Z" in o ? ((o.H += (o.Z / 100) | 0), (o.M += o.Z % 100), Jd(o)) : n(o); @@ -223613,18 +223716,18 @@ u(e) < e ? d : a(e) < e - ? p - : o(e) < e - ? v - : i(e) < e - ? g - : n(e) < e - ? r(e) < e - ? y - : _ - : t(e) < e - ? b - : m + ? p + : o(e) < e + ? v + : i(e) < e + ? g + : n(e) < e + ? r(e) < e + ? y + : _ + : t(e) < e + ? b + : m )(e); } function N(n, r, i, o) { @@ -223636,9 +223739,9 @@ u === x.length ? ((o = w(r / xv, i / xv, n)), (n = t)) : u - ? ((o = (u = x[a / x[u - 1][2] < x[u][2] / a ? u - 1 : u])[1]), - (n = u[0])) - : ((o = Math.max(w(r, i, n), 1)), (n = c)); + ? ((o = (u = x[a / x[u - 1][2] < x[u][2] / a ? u - 1 : u])[1]), + (n = u[0])) + : ((o = Math.max(w(r, i, n), 1)), (n = c)); } return null == o ? n : n.every(o); } @@ -223690,7 +223793,7 @@ : u( 0 === e ? 0.5 - : ((n = (r(n) - t) * e), c ? Math.max(0, Math.min(1, n)) : n) + : ((n = (r(n) - t) * e), c ? Math.max(0, Math.min(1, n)) : n), ); } return ( @@ -223804,14 +223907,14 @@ zv = Cv("7fc97fbeaed4fdc086ffff99386cb0f0027fbf5b17666666"), Rv = Cv("1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666"), Dv = Cv( - "a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928" + "a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928", ), qv = Cv("fbb4aeb3cde3ccebc5decbe4fed9a6ffffcce5d8bdfddaecf2f2f2"), Lv = Cv("b3e2cdfdcdaccbd5e8f4cae4e6f5c9fff2aef1e2cccccccc"), Uv = Cv("e41a1c377eb84daf4a984ea3ff7f00ffff33a65628f781bf999999"), Ov = Cv("66c2a5fc8d628da0cbe78ac3a6d854ffd92fe5c494b3b3b3"), Bv = Cv( - "8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f" + "8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f", ); function Yv(t) { return ce(t[t.length - 1]); @@ -223826,7 +223929,7 @@ "8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e", "8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e", "5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30", - "5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30" + "5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30", ) .map(Cv), Iv = Yv(Fv), @@ -223840,7 +223943,7 @@ "762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837", "762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837", "40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b", - "40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b" + "40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b", ) .map(Cv), Hv = Yv(jv), @@ -223854,7 +223957,7 @@ "c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221", "c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221", "8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419", - "8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419" + "8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419", ) .map(Cv), Gv = Yv(Xv), @@ -223868,7 +223971,7 @@ "5427888073acb2abd2d8daebfee0b6fdb863e08214b35806", "5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806", "2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08", - "2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08" + "2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08", ) .map(Cv), $v = Yv(Vv), @@ -223882,7 +223985,7 @@ "b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac", "b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac", "67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061", - "67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061" + "67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061", ) .map(Cv), Zv = Yv(Wv), @@ -223896,7 +223999,7 @@ "b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d", "b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d", "67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a", - "67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a" + "67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a", ) .map(Cv), Jv = Yv(Qv), @@ -223910,7 +224013,7 @@ "d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4", "d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4", "a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695", - "a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695" + "a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695", ) .map(Cv), tg = Yv(Kv), @@ -223924,7 +224027,7 @@ "d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850", "d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850", "a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837", - "a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837" + "a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837", ) .map(Cv), eg = Yv(ng), @@ -223938,7 +224041,7 @@ "d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd", "d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd", "9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2", - "9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2" + "9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2", ) .map(Cv), ig = Yv(rg), @@ -223950,7 +224053,7 @@ "edf8fbccece699d8c966c2a42ca25f006d2c", "edf8fbccece699d8c966c2a441ae76238b45005824", "f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824", - "f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b" + "f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b", ) .map(Cv), ag = Yv(og), @@ -223962,7 +224065,7 @@ "edf8fbbfd3e69ebcda8c96c68856a7810f7c", "edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b", "f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b", - "f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b" + "f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b", ) .map(Cv), cg = Yv(ug), @@ -223974,7 +224077,7 @@ "f0f9e8ccebc5a8ddb57bccc443a2ca0868ac", "f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e", "f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e", - "f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081" + "f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081", ) .map(Cv), sg = Yv(fg), @@ -223986,7 +224089,7 @@ "fef0d9fdd49efdbb84fc8d59e34a33b30000", "fef0d9fdd49efdbb84fc8d59ef6548d7301f990000", "fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000", - "fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000" + "fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000", ) .map(Cv), hg = Yv(lg), @@ -223998,7 +224101,7 @@ "f6eff7d0d1e6a6bddb67a9cf1c9099016c59", "f6eff7d0d1e6a6bddb67a9cf3690c002818a016450", "fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450", - "fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636" + "fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636", ) .map(Cv), pg = Yv(dg), @@ -224010,7 +224113,7 @@ "f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d", "f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b", "fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b", - "fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858" + "fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858", ) .map(Cv), gg = Yv(vg), @@ -224022,7 +224125,7 @@ "f1eef6d4b9dac994c7df65b0dd1c77980043", "f1eef6d4b9dac994c7df65b0e7298ace125691003f", "f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f", - "f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f" + "f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f", ) .map(Cv), _g = Yv(yg), @@ -224034,7 +224137,7 @@ "feebe2fcc5c0fa9fb5f768a1c51b8a7a0177", "feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177", "fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177", - "fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a" + "fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a", ) .map(Cv), mg = Yv(bg), @@ -224046,7 +224149,7 @@ "ffffccc7e9b47fcdbb41b6c42c7fb8253494", "ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84", "ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84", - "ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58" + "ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58", ) .map(Cv), wg = Yv(xg), @@ -224058,7 +224161,7 @@ "ffffccd9f0a3addd8e78c67931a354006837", "ffffccd9f0a3addd8e78c67941ab5d238443005a32", "ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32", - "ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529" + "ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529", ) .map(Cv), Ng = Yv(Mg), @@ -224070,7 +224173,7 @@ "ffffd4fee391fec44ffe9929d95f0e993404", "ffffd4fee391fec44ffe9929ec7014cc4c028c2d04", "ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04", - "ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506" + "ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506", ) .map(Cv), Tg = Yv(Ag), @@ -224082,7 +224185,7 @@ "ffffb2fed976feb24cfd8d3cf03b20bd0026", "ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026", "ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026", - "ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026" + "ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026", ) .map(Cv), kg = Yv(Sg), @@ -224094,7 +224197,7 @@ "eff3ffc6dbef9ecae16baed63182bd08519c", "eff3ffc6dbef9ecae16baed64292c62171b5084594", "f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594", - "f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b" + "f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b", ) .map(Cv), Cg = Yv(Eg), @@ -224106,7 +224209,7 @@ "edf8e9c7e9c0a1d99b74c47631a354006d2c", "edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32", "f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32", - "f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b" + "f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b", ) .map(Cv), zg = Yv(Pg), @@ -224118,7 +224221,7 @@ "f7f7f7d9d9d9bdbdbd969696636363252525", "f7f7f7d9d9d9bdbdbd969696737373525252252525", "fffffff0f0f0d9d9d9bdbdbd969696737373525252252525", - "fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000" + "fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000", ) .map(Cv), Dg = Yv(Rg), @@ -224130,7 +224233,7 @@ "f2f0f7dadaebbcbddc9e9ac8756bb154278f", "f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486", "fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486", - "fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d" + "fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d", ) .map(Cv), Lg = Yv(qg), @@ -224142,7 +224245,7 @@ "fee5d9fcbba1fc9272fb6a4ade2d26a50f15", "fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d", "fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d", - "fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d" + "fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d", ) .map(Cv), Og = Yv(Ug), @@ -224154,7 +224257,7 @@ "feeddefdd0a2fdae6bfd8d3ce6550da63603", "feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04", "fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04", - "fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704" + "fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704", ) .map(Cv), Yg = Yv(Bg), @@ -224173,23 +224276,23 @@ } var Wg = $g( Cv( - "44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725" - ) + "44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725", + ), ), Zg = $g( Cv( - "00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf" - ) + "00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf", + ), ), Qg = $g( Cv( - "00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4" - ) + "00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4", + ), ), Jg = $g( Cv( - "0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921" - ) + "0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921", + ), ); function Kg(t) { return function () { @@ -224557,7 +224660,7 @@ +r.apply(this, ((u[0] = c), u)), +i.apply(this, u), +r.apply(this, ((u[0] = f), u)), - +i.apply(this, u) + +i.apply(this, u), ), a) ) @@ -224704,7 +224807,7 @@ (t._x0 + 2 * t._x1) / 3, (t._y0 + 2 * t._y1) / 3, (t._x0 + 4 * t._x1 + n) / 6, - (t._y0 + 4 * t._y1 + e) / 6 + (t._y0 + 4 * t._y1 + e) / 6, ); } function o_(t) { @@ -224755,7 +224858,7 @@ (this._point = 3), this._context.lineTo( (5 * this._x0 + this._x1) / 6, - (5 * this._y0 + this._y1) / 6 + (5 * this._y0 + this._y1) / 6, ); default: i_(this, t, n); @@ -224792,11 +224895,11 @@ case 2: this._context.moveTo( (this._x2 + 2 * this._x3) / 3, - (this._y2 + 2 * this._y3) / 3 + (this._y2 + 2 * this._y3) / 3, ), this._context.lineTo( (this._x3 + 2 * this._x2) / 3, - (this._y3 + 2 * this._y2) / 3 + (this._y3 + 2 * this._y2) / 3, ), this._context.closePath(); break; @@ -224820,7 +224923,7 @@ (this._y4 = n), this._context.moveTo( (this._x0 + 4 * this._x1 + t) / 6, - (this._y0 + 4 * this._y1 + n) / 6 + (this._y0 + 4 * this._y1 + n) / 6, ); break; default: @@ -224891,7 +224994,7 @@ (r = c / e), this._basis.point( this._beta * t[c] + (1 - this._beta) * (i + r * a), - this._beta * n[c] + (1 - this._beta) * (o + r * u) + this._beta * n[c] + (1 - this._beta) * (o + r * u), ); (this._x = this._y = null), this._basis.lineEnd(); }, @@ -224917,7 +225020,7 @@ t._x2 + t._k * (t._x1 - n), t._y2 + t._k * (t._y1 - e), t._x2, - t._y2 + t._y2, ); } function l_(t, n) { @@ -225165,7 +225268,7 @@ var e = this._x2 - t, r = this._y2 - n; this._l23_a = Math.sqrt( - (this._l23_2a = Math.pow(e * e + r * r, this._alpha)) + (this._l23_2a = Math.pow(e * e + r * r, this._alpha)), ); } switch (this._point) { @@ -225254,7 +225357,7 @@ var e = this._x2 - t, r = this._y2 - n; this._l23_a = Math.sqrt( - (this._l23_2a = Math.pow(e * e + r * r, this._alpha)) + (this._l23_2a = Math.pow(e * e + r * r, this._alpha)), ); } switch (this._point) { @@ -225325,7 +225428,7 @@ var e = this._x2 - t, r = this._y2 - n; this._l23_a = Math.sqrt( - (this._l23_2a = Math.pow(e * e + r * r, this._alpha)) + (this._l23_2a = Math.pow(e * e + r * r, this._alpha)), ); } switch (this._point) { @@ -225772,7 +225875,7 @@ r[1][o], i[1][o], t[a], - n[a] + n[a], ); (this._line || (0 !== this._line && 1 === e)) && this._context.closePath(), @@ -225853,11 +225956,11 @@ (r.C = !0), $_(this, r)) : (i = r.L) && i.C - ? ((e.C = i.C = !1), (r.C = !0), (t = r)) - : (t === e.L && ($_(this, e), (e = (t = e).U)), - (e.C = !1), - (r.C = !0), - V_(this, r)), + ? ((e.C = i.C = !1), (r.C = !0), (t = r)) + : (t === e.L && ($_(this, e), (e = (t = e).U)), + (e.C = !1), + (r.C = !0), + V_(this, r)), (e = t.U); this._.C = !1; }, @@ -226034,8 +226137,8 @@ r > -xb ? ((n = u.P), (e = u)) : i > -xb - ? ((n = u), (e = u.N)) - : (n = e = u); + ? ((n = u), (e = u.N)) + : (n = e = u); break; } if (!u.R) { @@ -226098,7 +226201,7 @@ return l ? (-h + Math.sqrt( - h * h - 2 * l * ((s * s) / (-2 * f) - c + f / 2 + i - o / 2) + h * h - 2 * l * ((s * s) / (-2 * f) - c + f / 2 + i - o / 2), )) / l + r @@ -226205,32 +226308,32 @@ : r, ] : Math.abs(v - r) < xb && - e - p > xb - ? [ - Math.abs(h - r) < xb - ? l - : e, - r, - ] - : Math.abs(p - e) < xb && - v - n > xb - ? [ - e, - Math.abs(l - e) < xb - ? h - : n, - ] - : Math.abs(v - n) < xb && - p - t > xb - ? [ - Math.abs(h - n) < xb - ? l - : t, - n, - ] - : null - ) - ) - 1 + e - p > xb + ? [ + Math.abs(h - r) < xb + ? l + : e, + r, + ] + : Math.abs(p - e) < xb && + v - n > xb + ? [ + e, + Math.abs(l - e) < xb + ? h + : n, + ] + : Math.abs(v - n) < xb && + p - t > xb + ? [ + Math.abs(h - n) < xb + ? l + : t, + n, + ] + : null, + ), + ) - 1, ), ++f); f && (y = !1); @@ -226255,7 +226358,7 @@ mb.push(Q_((a = y.site), w, M)) - 1, mb.push(Q_(a, M, N)) - 1, mb.push(Q_(a, N, A)) - 1, - mb.push(Q_(a, A, w)) - 1 + mb.push(Q_(a, A, w)) - 1, ); } } @@ -226445,7 +226548,7 @@ a = t.invertY(n[1][1]) - e[1][1]; return t.translate( i > r ? (r + i) / 2 : Math.min(0, r) || Math.max(0, i), - a > o ? (o + a) / 2 : Math.min(0, o) || Math.max(0, a) + a > o ? (o + a) / 2 : Math.min(0, o) || Math.max(0, a), ); } (Eb.prototype = Sb.prototype), @@ -226516,8 +226619,8 @@ "function" == typeof t ? t : Array.isArray(t) - ? p(h.call(t)) - : p(t)), + ? p(h.call(t)) + : p(t)), r) : e; }), @@ -226529,7 +226632,7 @@ (t = d.call(t, u).sort(n)), Math.ceil( (r - e) / - (2 * (N(t, 0.75) - N(t, 0.25)) * Math.pow(t.length, -1 / 3)) + (2 * (N(t, 0.75) - N(t, 0.25)) * Math.pow(t.length, -1 / 3)), ) ); }), @@ -226706,7 +226809,7 @@ v.push( N.value < A.value ? {source: A, target: N} - : {source: N, target: A} + : {source: N, target: A}, ); } return r ? v.sort(r) : v; @@ -226729,7 +226832,7 @@ (r = function (t, e) { return n( t.source.value + t.target.value, - e.source.value + e.target.value + e.source.value + e.target.value, ); }))._ = t), i) @@ -226916,32 +227019,32 @@ vo( {width: c, height: f, data: i}, {width: c, height: f, data: l}, - o >> a + o >> a, ), go( {width: c, height: f, data: l}, {width: c, height: f, data: i}, - o >> a + o >> a, ), vo( {width: c, height: f, data: i}, {width: c, height: f, data: l}, - o >> a + o >> a, ), go( {width: c, height: f, data: l}, {width: c, height: f, data: i}, - o >> a + o >> a, ), vo( {width: c, height: f, data: i}, {width: c, height: f, data: l}, - o >> a + o >> a, ), go( {width: c, height: f, data: l}, {width: c, height: f, data: i}, - o >> a + o >> a, ); var d = s(i); if (!Array.isArray(d)) { @@ -226999,8 +227102,8 @@ "function" == typeof t ? t : Array.isArray(t) - ? uo(oo.call(t)) - : uo(t)), + ? uo(oo.call(t)) + : uo(t)), l) : s; }), @@ -227113,7 +227216,7 @@ null != (t.event.subject = a = u.apply(i, o)) && ((c = a.x - p[0] || 0), (h = a.y - p[1] || 0), !0) ); - } + }, ) ) return function t(u) { @@ -227139,11 +227242,11 @@ p[1] + h, p[0] - g[0], p[1] - g[1], - v + v, ), v.apply, v, - [u, i, o] + [u, i, o], ); }; } @@ -227202,7 +227305,7 @@ else if (isNaN((e = +r))) { if ( !/^([-+]\d{2})?\d{4}(-\d{2}(-\d{2})?)?(T\d{2}:\d{2}(:\d{2}(\.\d{3})?)?(Z|[-+]\d{2}:\d{2})?)?$/.test( - r + r, ) ) continue; @@ -228055,8 +228158,8 @@ i >= 0.12 && i < 0.234 && r >= -0.425 && r < -0.214 ? u : i >= 0.166 && i < 0.234 && r >= -0.214 && r < -0.115 - ? c - : a + ? c + : a ).invert(t); }), (s.stream = function (e) { @@ -228210,15 +228313,15 @@ (h = +r[0][0]), (t = +r[0][1]), (n = +r[1][0]), - (e = +r[1][1]) + (e = +r[1][1]), )), p()) : null == h - ? null - : [ - [h, t], - [n, e], - ]; + ? null + : [ + [h, t], + [n, e], + ]; }, scale: function (t) { return arguments.length @@ -228333,7 +228436,7 @@ : function (t) { (t.x = ((t.x - f) / (s - f)) * n), (t.y = (1 - (i.y ? t.y / i.y : 1)) * e); - } + }, ); } return ( @@ -228344,15 +228447,15 @@ return arguments.length ? ((r = !1), (n = +t[0]), (e = +t[1]), i) : r - ? null - : [n, e]; + ? null + : [n, e]; }), (i.nodeSize = function (t) { return arguments.length ? ((r = !0), (n = +t[0]), (e = +t[1]), i) : r - ? [n, e] - : null; + ? [n, e] + : null; }), i ); @@ -228419,7 +228522,7 @@ r.x0, (t * (r.depth + 1)) / n, r.x1, - (t * (r.depth + 2)) / n + (t * (r.depth + 2)) / n, ); var i = r.x0, o = r.y0, @@ -228432,7 +228535,7 @@ (r.x1 = a), (r.y1 = u); }; - })(n, o) + })(n, o), ), r && i.eachBefore(Ul), i @@ -228603,15 +228706,15 @@ return arguments.length ? ((r = !1), (n = +t[0]), (e = +t[1]), i) : r - ? null - : [n, e]; + ? null + : [n, e]; }), (i.nodeSize = function (t) { return arguments.length ? ((r = !0), (n = +t[0]), (e = +t[1]), i) : r - ? [n, e] - : null; + ? [n, e] + : null; }), i ); @@ -229032,10 +229135,10 @@ return n < 0 ? [NaN, NaN] : n < 1 - ? [e, a[0]] - : n >= o - ? [a[o - 1], r] - : [a[n - 1], a[n]]; + ? [e, a[0]] + : n >= o + ? [a[o - 1], r] + : [a[n - 1], a[n]]; }), (c.unknown = function (t) { return arguments.length ? ((n = t), c) : c; @@ -229087,7 +229190,7 @@ new Date(2e3, 0, 1), new Date(2e3, 0, 2), ]), - arguments + arguments, ); }), (t.scaleUtc = function () { @@ -229096,7 +229199,7 @@ Date.UTC(2e3, 0, 1), Date.UTC(2e3, 0, 2), ]), - arguments + arguments, ); }), (t.scaleSequential = function t() { @@ -229396,8 +229499,8 @@ ay(j * j + H * H))) > 1 ? 0 : s < -1 - ? cy - : Math.acos(s)) / 2 + ? cy + : Math.acos(s)) / 2, ), G = ay(L[0] * L[0] + L[1] * L[1]); (k = iy(S, (l - G) / (X - 1))), @@ -229416,7 +229519,7 @@ E, ny(y.y01, y.x01), ny(_.y01, _.x01), - !g + !g, ) : (u.arc( y.cx, @@ -229424,7 +229527,7 @@ E, ny(y.y01, y.x01), ny(y.y11, y.x11), - !g + !g, ), u.arc( 0, @@ -229432,7 +229535,7 @@ h, ny(y.cy + y.y11, y.cx + y.x11), ny(_.cy + _.y11, _.cx + _.x11), - !g + !g, ), u.arc( _.cx, @@ -229440,7 +229543,7 @@ E, ny(_.y11, _.x11), ny(_.y01, _.x01), - !g + !g, ))) : (u.moveTo(z, R), u.arc(0, 0, h, b, m, !g)) : u.moveTo(z, R), @@ -229456,7 +229559,7 @@ k, ny(y.y01, y.x01), ny(_.y01, _.x01), - !g + !g, ) : (u.arc( y.cx, @@ -229464,7 +229567,7 @@ k, ny(y.y01, y.x01), ny(y.y11, y.x11), - !g + !g, ), u.arc( 0, @@ -229472,7 +229575,7 @@ l, ny(y.cy + y.y11, y.cx + y.x11), ny(_.cy + _.y11, _.cx + _.x11), - g + g, ), u.arc( _.cx, @@ -229480,7 +229583,7 @@ k, ny(_.y11, _.x11), ny(_.y01, _.x01), - !g + !g, ))) : u.arc(0, 0, l, w, x, g) : u.lineTo(D, q); @@ -229748,8 +229851,8 @@ null == t ? L_ : "function" == typeof t - ? t - : Kg(Ry.call(t))), + ? t + : Kg(Ry.call(t))), i) : n; }), @@ -229775,8 +229878,8 @@ (i = (r = t[n[e]][c])[1] - r[0]) >= 0 ? ((r[0] = o), (r[1] = o += i)) : i < 0 - ? ((r[1] = a), (r[0] = a += i)) - : (r[0] = o); + ? ((r[1] = a), (r[0] = a += i)) + : (r[0] = o); }), (t.stackOffsetNone = q_), (t.stackOffsetSilhouette = function (t, n) { @@ -229914,7 +230017,7 @@ (a += i), r.restart(o, (i += n), e), t(a); }, n, - e + e, ), r); }), @@ -229943,7 +230046,7 @@ ]; return (o.index = i), (o.data = e), o; }), - e + e, ); } return ( @@ -230058,7 +230161,7 @@ f = "function" == typeof n ? n.apply(this, t) : n, s = l( c.invert(a).concat(u / c.k), - f.invert(a).concat(u / f.k) + f.invert(a).concat(u / f.k), ); return function (t) { if (1 === t) t = f; @@ -230089,7 +230192,7 @@ n = this.__zoom, e = Math.max( c[0], - Math.min(c[1], n.k * Math.pow(2, a.apply(this, arguments))) + Math.min(c[1], n.k * Math.pow(2, a.apply(this, arguments))), ), i = Ot(this); if (t.wheel) @@ -230106,7 +230209,7 @@ }, v)), t.zoom( "mouse", - o(b(_(n, e), t.mouse[0], t.mouse[1]), t.extent, f) + o(b(_(n, e), t.mouse[0], t.mouse[1]), t.extent, f), ); } } @@ -230128,14 +230231,14 @@ b( n.that.__zoom, (n.mouse[0] = Ot(n.that)), - n.mouse[1] + n.mouse[1], ), n.extent, - f - ) + f, + ), ); }, - !0 + !0, ) .on( "mouseup.zoom", @@ -230145,7 +230248,7 @@ Pb(), n.end(); }, - !0 + !0, ), a = Ot(this), u = t.event.clientX, @@ -230275,7 +230378,7 @@ null, "function" == typeof n ? n.apply(this, arguments) - : n + : n, ) .end(); }); @@ -230303,10 +230406,10 @@ return o( this.__zoom.translate( "function" == typeof n ? n.apply(this, arguments) : n, - "function" == typeof e ? e.apply(this, arguments) : e + "function" == typeof e ? e.apply(this, arguments) : e, ), i.apply(this, arguments), - f + f, ); }); }), @@ -230325,10 +230428,10 @@ : -n, "function" == typeof e ? -e.apply(this, arguments) - : -e + : -e, ), t, - f + f, ); }); }), @@ -230454,36 +230557,36 @@ require("d3-zoom"), require("viz.js/viz"), require("d3-format"), - require("d3-path") + require("d3-path"), ) : "function" == typeof define && define.amd - ? define( - [ - "exports", - "d3-selection", - "d3-dispatch", - "d3-transition", - "d3-timer", - "d3-interpolate", - "d3-zoom", - "viz.js/viz", - "d3-format", - "d3-path", - ], - e - ) - : e( - (t["d3-graphviz"] = {}), - t.d3, - t.d3, - t.d3, - t.d3, - t.d3, - t.d3, - t.Viz, - t.d3, - t.d3 - ); + ? define( + [ + "exports", + "d3-selection", + "d3-dispatch", + "d3-transition", + "d3-timer", + "d3-interpolate", + "d3-zoom", + "viz.js/viz", + "d3-format", + "d3-path", + ], + e, + ) + : e( + (t["d3-graphviz"] = {}), + t.d3, + t.d3, + t.d3, + t.d3, + t.d3, + t.d3, + t.Viz, + t.d3, + t.d3, + ); })(this, function (t, e, n, r, i, a, o, s, l, h) { "use strict"; function c(t) { @@ -230572,8 +230675,8 @@ return "#text" == t.tag ? document.createTextNode("") : "#comment" == t.tag - ? document.createComment(t.comment) - : document.createElementNS("http://www.w3.org/2000/svg", t.tag); + ? document.createComment(t.comment) + : document.createElementNS("http://www.w3.org/2000/svg", t.tag); } function f(t) { var n = d(t), @@ -230612,7 +230715,7 @@ }, function () { return t.node(); - } + }, ); return t.remove(), a; } @@ -230674,7 +230777,7 @@ }, function (t) { return t.tag + "-" + e; - } + }, )) .enter() .append(function (t) { @@ -230711,7 +230814,7 @@ .on("zoom", function () { e.select(n.node().querySelector("g")).attr( "transform", - e.event.transform + e.event.transform, ); }); this._zoomBehavior = r; @@ -230798,7 +230901,7 @@ "0 0 " + (3 * m) / 4 / y.scale + " " + - (3 * _) / 4 / y.scale + (3 * _) / 4 / y.scale, ), (t.attributes.viewBox = "0 0 " + @@ -230875,7 +230978,7 @@ .attr("stroke-dashoffset", I) .attr( "transform", - "translate(" + t.offset.x + "," + t.offset.y + ")" + "translate(" + t.offset.x + "," + t.offset.y + ")", ), (N["stroke-dashoffset"] = 0), (N.transform = "translate(0,0)"), @@ -230961,11 +231064,11 @@ a.interpolateTransformSvg( o .zoomTransform( - f._zoomSelection.node() + f._zoomSelection.node(), ) .toString(), - w.call(f, v).toString() - )(e) + w.call(f, v).toString(), + )(e), ); }; }); @@ -231002,7 +231105,7 @@ }, function (t) { return t.key; - } + }, )) .enter() .append(function (t) { @@ -231326,7 +231429,16 @@ if (0 != s.size()) { var h = s.node().getBBox(); (h.cx = h.x + h.width / 2), (h.cy = h.y + h.height / 2); - } else 0 != l.size() && (h = {x: +l.attr("x"), y: +l.attr("y"), width: 0, height: 0, cx: +l.attr("x"), cy: +l.attr("y")}); + } else + 0 != l.size() && + (h = { + x: +l.attr("x"), + y: +l.attr("y"), + width: 0, + height: 0, + cx: +l.attr("x"), + cy: +l.attr("y"), + }); return ( s.each(function (t, i) { var a = e.select(this); @@ -231354,7 +231466,7 @@ .replace(/-0\./g, "-.") .replace(/ 0\./g, " .")) ); - })(o, n - h.cx, r - h.cy) + })(o, n - h.cx, r - h.cy), ); } else { var s = a.attr("d"); @@ -231384,7 +231496,7 @@ .join("")) .replace(/-0\./g, "-.") .replace(/ 0\./g, " .")); - })(s, n - h.cx, r - h.cy) + })(s, n - h.cx, r - h.cy), ); } }), @@ -231497,13 +231609,13 @@ }); 0 == d.size() ? (console.warn( - 'No script tag of type "javascript/worker" was found and "useWorker" is true. Not using web worker.' + 'No script tag of type "javascript/worker" was found and "useWorker" is true. Not using web worker.', ), (u = !1)) : ((this._vizURL = d.attr("src")), this._vizURL || (console.warn( - 'No "src" attribute of was found on the "javascript/worker" script tag and "useWorker" is true. Not using web worker.' + 'No "src" attribute of was found on the "javascript/worker" script tag and "useWorker" is true. Not using web worker.', ), (u = !1))); } @@ -231634,11 +231746,11 @@ ? i.timeout( function () { (this._transition = r.transition( - this._transitionFactory() + this._transitionFactory(), )), z.call(this, t); }.bind(this), - 0 + 0, ) : z.call(this, t), this); @@ -231814,7 +231926,7 @@ var c = l.children[d].children.findIndex( function (t, e) { return "a" == t.tag; - } + }, ); l = l.children[d].children[c]; } @@ -231824,7 +231936,7 @@ ((c = h.children[d].children.findIndex( function (t, e) { return "a" == t.tag; - } + }, )), (h = h.children[d].children[c])); for ( @@ -231897,7 +232009,7 @@ .append("div"), i = new window.DOMParser().parseFromString( t, - "image/svg+xml" + "image/svg+xml", ); r.append(function () { return i.documentElement; @@ -232002,8 +232114,8 @@ D( i.map(function (t) { return t.length; - }) - ) + }), + ), ), o = function (o) { var s = i[o]; @@ -232024,13 +232136,13 @@ (i += l.format(" >5")(t - n) + " "), "initEnd" != s && (i += l.format(" >5")( - t - r.start[e] + t - r.start[e], )), "dataProcessEnd" == s && (i += " prepare " + l.format(" >5")( - t - r.layoutEnd[e] + t - r.layoutEnd[e], )), "renderEnd" == s && h._transition && @@ -232038,7 +232150,8 @@ " transition start margin " + l.format(" >5")( h._transition.delay() - - (t - r.renderStart[e]) + (t - + r.renderStart[e]), )), (c = h._transition.delay()), (u = h._transition.duration())), @@ -232048,7 +232161,7 @@ (i += " transition delay " + l.format(" >5")( - t - r.renderStart[e] + t - r.renderStart[e], )), (i += " expected " + @@ -232071,7 +232184,7 @@ } console.log(i), (n = t); } - : null + : null, ); }; for (var s in i) { @@ -232166,7 +232279,7 @@ removeDrawnEdge: O, }), "removeDrawnEdge", - O + O, ), S(k, "drawnEdgeSelection", L), S(k, "drawnEdgeSelection", L), @@ -232232,7 +232345,7 @@ .selectWithoutDataPropagation("a"), i = (r.selectWithoutDataPropagation( - "ellipse,polygon,path,polyline" + "ellipse,polygon,path,polyline", ), r.selectWithoutDataPropagation("text")); else @@ -232293,7 +232406,7 @@ (s = a.value), (l = new showdown.Converter({extensions: ["table"]})), (document.getElementById("preview").innerHTML = l.makeHtml( - s.markdownTemplate + s.markdownTemplate, )), (function (e, a) { window.chartColors; @@ -232308,7 +232421,7 @@ new Set([ ...Object.keys(e), ...Object.keys(a), - ]) + ]), ), }; return ( @@ -232353,7 +232466,7 @@ keys: Object.keys(t.totals.nsloc).filter( (t) => "total" !== t && - "commentToSourceRatio" !== t + "commentToSourceRatio" !== t, ), }; return ( @@ -232433,7 +232546,7 @@ !t.startsWith("AssemblyCall:Name:") ); }, - !0 + !0, )), i = (new Chart("chart-num-bar-ast", { @@ -232472,7 +232585,7 @@ function (t) { return t.startsWith("FunctionCall:Name:"); }, - !0 + !0, )), r = (new Chart("chart-num-bar-ast-funccalls", { @@ -232509,7 +232622,7 @@ function (t) { return t.startsWith("AssemblyCall:Name:"); }, - !0 + !0, )); new Chart("chart-num-bar-ast-asmcalls", { type: "bar", @@ -232558,7 +232671,7 @@ } var s, l; }, - !1 + !1, ), (window.onload = function () { "function" == typeof acquireVsCodeApi && From 14b1321a84887727b24cc6c60c339aa1dbda4003 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 21 Nov 2024 14:22:50 +0500 Subject: [PATCH 131/160] Add missing addresses for Arbitrum and Optimism (from OlympusDAO/olympus-docs@feaa76853fe649e6bd979e50569ab456b382525d) and Base addresses --- src/scripts/env.json | 60 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/src/scripts/env.json b/src/scripts/env.json index 551a252e9..d6f95a388 100644 --- a/src/scripts/env.json +++ b/src/scripts/env.json @@ -135,28 +135,29 @@ } }, "olympus": { - "Kernel": "0x0000000000000000000000000000000000000000", + "Kernel": "0xeac3eC0CC130f4826715187805d1B50e861F2DaC", "modules": { "OlympusPrice": "0x0000000000000000000000000000000000000000", "OlympusRange": "0x0000000000000000000000000000000000000000", - "OlympusRoles": "0x0000000000000000000000000000000000000000", - "OlympusTreasury": "0x0000000000000000000000000000000000000000", - "OlympusMinter": "0x0000000000000000000000000000000000000000", + "OlympusRoles": "0xFF5F09D5efE13A9a424F30EC2e1af89D867834d6", + "OlympusTreasury": "0x56db53e9801a6EA080569261b63925E0f1f3C81A", + "OlympusMinter": "0x8f6406eDbFA393e327822D4A08BcF15503570D87", "OlympusInstructions": "0x0000000000000000000000000000000000000000", - "OlympusVotes": "0x0000000000000000000000000000000000000000" + "OlympusVotes": "0x0000000000000000000000000000000000000000", + "OlympusLender": "0x868C3ae18Fdea85bBb7a303e379c5B7e23b30F03" }, "policies": { - "RolesAdmin": "0x0000000000000000000000000000000000000000", + "RolesAdmin": "0x69168c08AcF66f002fd02E1B169f38C022c93b70", "TreasuryCustodian": "0x0000000000000000000000000000000000000000", - "CrossChainBridge": "0x0000000000000000000000000000000000000000" + "CrossChainBridge": "0x20B3834091f038Ce04D8686FAC99CA44A0FB285c" }, "legacy": { - "OHM": "0x0000000000000000000000000000000000000000", + "OHM": "0xf0cb2dc0db5e6c66B9a70Ac27B06b878da017028", "sOHM": "0x0000000000000000000000000000000000000000", - "gOHM": "0x0000000000000000000000000000000000000000", + "gOHM": "0x8D9bA570D6cb60C7e3e0F31343Efe75AB8E65FB1", "Staking": "0x0000000000000000000000000000000000000000", "Treasury": "0x0000000000000000000000000000000000000000", - "OlympusAuthority": "0x0000000000000000000000000000000000000000" + "OlympusAuthority": "0x78f84998c73655ac2Da0Aa1e1270F6Cb985a343e" } } }, @@ -266,6 +267,45 @@ "OldPOLY": "0x0000000000000000000000000000000000000000" } } + }, + "optimism": { + "external": { + }, + "olympus": { + "Kernel": "0x18878Df23e2a36f81e820e4b47b4A40576D3159C", + "modules": { + "OlympusMinter": "0x623164A9Ee2556D524b08f34F1d2389d7B4e1A1C", + "OlympusRoles": "0xbC9eE0D911739cBc72cd094ADA26F56E0C49EeAE" + }, + "policies": { + "RolesAdmin": "0xb1fA0Ac44d399b778B14af0AAF4bCF8af3437ad1", + "CrossChainBridge": "0x22AE99D07584A2AE1af748De573c83f1B9Cdb4c0" + }, + "legacy": { + "OHM": "0x060cb087a9730E13aa191f31A6d86bFF8DfcdCC0", + "gOHM": "0x0b5740c6b4a97f90eF2F0220651Cca420B868FfB", + "OlympusAuthority": "0x13DFEff85779118136bB9826DcAD8f3bd25153a3" + } + } + }, + "base": { + "external": { + }, + "olympus": { + "Kernel": "0x18878Df23e2a36f81e820e4b47b4A40576D3159C", + "modules": { + "OlympusMinter": "0x623164A9Ee2556D524b08f34F1d2389d7B4e1A1C", + "OlympusRoles": "0xbC9eE0D911739cBc72cd094ADA26F56E0C49EeAE" + }, + "policies": { + "RolesAdmin": "0xb1fA0Ac44d399b778B14af0AAF4bCF8af3437ad1", + "CrossChainBridge": "0x6CA1a916e883c7ce2BFBcF59dc70F2c1EF9dac6e" + }, + "legacy": { + "OHM": "0x060cb087a9730E13aa191f31A6d86bFF8DfcdCC0", + "OlympusAuthority": "0x13DFEff85779118136bB9826DcAD8f3bd25153a3" + } + } } }, "last": { From b76f1eaf5f923df08a5d4ed56ebbc71ab56e9912 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 11:57:37 +0400 Subject: [PATCH 132/160] Restore github workflow for OCG proposals --- .github/workflows/OCG.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/OCG.yml diff --git a/.github/workflows/OCG.yml b/.github/workflows/OCG.yml new file mode 100644 index 000000000..090021ebe --- /dev/null +++ b/.github/workflows/OCG.yml @@ -0,0 +1,37 @@ +name: OCG Proposals +on: + push: + branches: + - master + pull_request: + +jobs: + run-ci: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - uses: pnpm/action-setup@v2 + with: + version: 9 + + - name: Install Node dependencies + run: pnpm install + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Install Foundry dependencies + run: pnpm run build + + - name: Run proposal simulation tests + run: pnpm run test:proposal + env: + FORK_TEST_RPC_URL: ${{ secrets.FORK_TEST_RPC_URL }} From 10623a1b2d5bd25b8af90fb1d179c411ea6fa411 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 12:26:06 +0400 Subject: [PATCH 133/160] Restore proposal simulation workflow --- .github/workflows/OCG.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/OCG.yml diff --git a/.github/workflows/OCG.yml b/.github/workflows/OCG.yml new file mode 100644 index 000000000..090021ebe --- /dev/null +++ b/.github/workflows/OCG.yml @@ -0,0 +1,37 @@ +name: OCG Proposals +on: + push: + branches: + - master + pull_request: + +jobs: + run-ci: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - uses: pnpm/action-setup@v2 + with: + version: 9 + + - name: Install Node dependencies + run: pnpm install + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Install Foundry dependencies + run: pnpm run build + + - name: Run proposal simulation tests + run: pnpm run test:proposal + env: + FORK_TEST_RPC_URL: ${{ secrets.FORK_TEST_RPC_URL }} From 44dba710d488d7e8356419494556564d63bcffd4 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 12:35:58 +0400 Subject: [PATCH 134/160] Fix fork blocks, proposal ids, assertions for older proposals --- src/proposals/OIP_166.sol | 6 +-- src/proposals/OIP_XXX.sol | 51 ++++++++++++------- .../proposals/EmissionManagerProposal.t.sol | 11 ++-- src/test/proposals/OIP_166.t.sol | 8 +-- src/test/proposals/OIP_168.t.sol | 8 +-- src/test/proposals/OIP_XXX.t.sol | 36 ++++++++++--- 6 files changed, 77 insertions(+), 43 deletions(-) diff --git a/src/proposals/OIP_166.sol b/src/proposals/OIP_166.sol index c7ef403b8..9f892fff0 100644 --- a/src/proposals/OIP_166.sol +++ b/src/proposals/OIP_166.sol @@ -19,8 +19,8 @@ contract OIP_166 is GovernorBravoProposal { Kernel internal _kernel; // Returns the id of the proposal. - function id() public view override returns (uint256) { - return 166; + function id() public pure override returns (uint256) { + return 1; } // Returns the name of the proposal. @@ -178,7 +178,7 @@ contract OIP_166 is GovernorBravoProposal { } // Validates the post-execution state. - function _validate(Addresses addresses, address) internal override { + function _validate(Addresses addresses, address) internal view override { // Load the contract addresses ROLESv1 roles = ROLESv1(addresses.getAddress("olympus-module-roles")); RolesAdmin rolesAdmin = RolesAdmin(addresses.getAddress("olympus-policy-roles-admin")); diff --git a/src/proposals/OIP_XXX.sol b/src/proposals/OIP_XXX.sol index ddda9d050..eecfd6004 100644 --- a/src/proposals/OIP_XXX.sol +++ b/src/proposals/OIP_XXX.sol @@ -143,34 +143,47 @@ contract OIP_XXX is GovernorBravoProposal { IERC20 dai = IERC20(addresses.getAddress("external-tokens-dai")); IERC4626 sdai = IERC4626(addresses.getAddress("external-tokens-sdai")); // Validate token balances - assertEq(dai.balanceOf(clearinghouseV0), 0); - assertEq(sdai.balanceOf(clearinghouseV0), 0); - assertEq(sdai.maxRedeem(clearinghouseV1), 0); // Should be 0 DAI since rebalance wasn't called - assertEq(dai.balanceOf(TRSRY), cacheTRSRY.daiBalance + cacheCH0.daiBalance); + assertEq(dai.balanceOf(clearinghouseV0), 0, "DAI balance of clearinghouse v1 should be 0"); + assertEq( + sdai.balanceOf(clearinghouseV0), + 0, + "sDAI balance of clearinghouse v1 should be 0" + ); + assertEq(sdai.maxRedeem(clearinghouseV1), 0, "Max redeem should be 0"); // Should be 0 DAI since rebalance wasn't called + assertEq( + dai.balanceOf(TRSRY), + cacheTRSRY.daiBalance + cacheCH0.daiBalance, + "DAI balance of treasury should be correct" + ); assertEq( sdai.balanceOf(TRSRY), - cacheTRSRY.sdaiBalance + cacheCH0.sdaiBalance - sdai.balanceOf(clearinghouseV1) + cacheTRSRY.sdaiBalance + cacheCH0.sdaiBalance - sdai.balanceOf(clearinghouseV1), + "sDAI balance of treasury should be correct" ); // Validate Clearinghouse state Clearinghouse CHv0 = Clearinghouse(clearinghouseV0); - assertEq(CHv0.active(), false); + assertEq(CHv0.active(), false, "Clearinghouse v1 should be shutdown"); // Validate Clearinghouse parameters Clearinghouse CHv1 = Clearinghouse(clearinghouseV1); - assertEq(CHv1.active(), true); - assertEq(CHv1.INTEREST_RATE(), 5e15); - assertEq(CHv1.LOAN_TO_COLLATERAL(), 289292e16); - assertEq(CHv1.DURATION(), 121 days); - assertEq(CHv1.FUND_CADENCE(), 7 days); - assertEq(CHv1.FUND_AMOUNT(), 18_000_000e18); - assertEq(CHv1.MAX_REWARD(), 1e17); + assertEq(CHv1.active(), true, "Clearinghouse v1.1 should be active"); + assertEq(CHv1.INTEREST_RATE(), 5e15, "Interest rate should be correct"); + assertEq(CHv1.LOAN_TO_COLLATERAL(), 289292e16, "Loan to collateral should be correct"); + assertEq(CHv1.DURATION(), 121 days, "Duration should be correct"); + assertEq(CHv1.FUND_CADENCE(), 7 days, "Fund cadence should be correct"); + assertEq(CHv1.FUND_AMOUNT(), 18_000_000e18, "Fund amount should be correct"); + assertEq(CHv1.MAX_REWARD(), 1e17, "Max reward should be correct"); // Validate Clearinghouse Registry state // The V0 Clearinghouse's emergencyShutdown function does NOT remove it from the registry. CHREGv1 CHRegistry = CHREGv1(CHREG); - assertEq(CHRegistry.activeCount(), 2); - assertEq(CHRegistry.active(0), clearinghouseV0); - assertEq(CHRegistry.active(1), clearinghouseV1); - assertEq(CHRegistry.registryCount(), 2); - assertEq(CHRegistry.registry(1), clearinghouseV0); - assertEq(CHRegistry.registry(2), clearinghouseV1); + assertEq(CHRegistry.activeCount(), 2, "Active count should be correct"); + assertEq(CHRegistry.active(0), clearinghouseV0, "Clearinghouse v0 should be active"); + assertEq(CHRegistry.active(1), clearinghouseV1, "Clearinghouse v1.1 should be active"); + assertEq(CHRegistry.registryCount(), 2, "Registry count should be correct"); + assertEq(CHRegistry.registry(1), clearinghouseV0, "Clearinghouse v0 should be in registry"); + assertEq( + CHRegistry.registry(2), + clearinghouseV1, + "Clearinghouse v1.1 should be in registry" + ); } } diff --git a/src/test/proposals/EmissionManagerProposal.t.sol b/src/test/proposals/EmissionManagerProposal.t.sol index c66083dc1..47cd88bc3 100644 --- a/src/test/proposals/EmissionManagerProposal.t.sol +++ b/src/test/proposals/EmissionManagerProposal.t.sol @@ -32,15 +32,14 @@ contract EmissionManagerProposalTest is Test { /// @notice Creates a sandboxed environment from a mainnet fork. function setUp() public virtual { // Mainnet Fork at a fixed block - // Prior to actual deployment of the proposal (otherwise it will fail) - 21071000 - // TODO: Update the block number once the proposal has been submitted on-chain. - vm.createSelectFork(RPC_URL, 21071000); + // Prior to actual deployment of the proposal (otherwise it will fail) - 21224026 + vm.createSelectFork(RPC_URL, 21224026 - 1); /// @dev Deploy your proposal EmissionManagerProposal proposal = new EmissionManagerProposal(); /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. - hasBeenSubmitted = false; + hasBeenSubmitted = true; /// [DO NOT DELETE] /// @notice This section is used to simulate the proposal on the mainnet fork. @@ -68,7 +67,7 @@ contract EmissionManagerProposalTest is Test { address governor = addresses.getAddress("olympus-governor"); bool[] memory matches = suite.checkProposalCalldatas(governor); for (uint256 i; i < matches.length; i++) { - assertTrue(matches[i]); + assertTrue(matches[i], "Calldata should match"); } } else { console.log("\n\n------- Calldata check (simulation vs mainnet) -------\n"); @@ -79,6 +78,6 @@ contract EmissionManagerProposalTest is Test { // [DO NOT DELETE] Dummy test to ensure `setUp` is executed and the proposal simulated. function testProposal_simulate() public { - assertTrue(true); + assertTrue(true, "Proposal should be simulated"); } } diff --git a/src/test/proposals/OIP_166.t.sol b/src/test/proposals/OIP_166.t.sol index 414999fa3..064a36c72 100644 --- a/src/test/proposals/OIP_166.t.sol +++ b/src/test/proposals/OIP_166.t.sol @@ -33,13 +33,13 @@ contract OIP_166_OCGProposalTest is Test { function setUp() public virtual { // Mainnet Fork at a fixed block // Prior to actual deployment of the proposal (otherwise it will fail) - 20872023 - vm.createSelectFork(RPC_URL, 20872022); + vm.createSelectFork(RPC_URL, 20872023 - 1); /// @dev Deploy your proposal OIP_166 proposal = new OIP_166(); /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. - hasBeenSubmitted = false; + hasBeenSubmitted = true; /// [DO NOT DELETE] /// @notice This section is used to simulate the proposal on the mainnet fork. @@ -77,7 +77,7 @@ contract OIP_166_OCGProposalTest is Test { address governor = addresses.getAddress("olympus-governor"); bool[] memory matches = suite.checkProposalCalldatas(governor); for (uint256 i; i < matches.length; i++) { - assertTrue(matches[i]); + assertTrue(matches[i], "Calldata should match"); } } else { console.log("\n\n------- Calldata check (simulation vs mainnet) -------\n"); @@ -88,6 +88,6 @@ contract OIP_166_OCGProposalTest is Test { // [DO NOT DELETE] Dummy test to ensure `setUp` is executed and the proposal simulated. function testProposal_simulate() public { - assertTrue(true); + assertTrue(true, "Proposal should be simulated"); } } diff --git a/src/test/proposals/OIP_168.t.sol b/src/test/proposals/OIP_168.t.sol index 7241819e8..22f7349ed 100644 --- a/src/test/proposals/OIP_168.t.sol +++ b/src/test/proposals/OIP_168.t.sol @@ -32,8 +32,8 @@ contract OIP_168_OCGProposalTest is Test { /// @notice Creates a sandboxed environment from a mainnet fork. function setUp() public virtual { // Mainnet Fork at a fixed block - // Prior to actual deployment of the proposal (otherwise it will fail) - 21071000 - vm.createSelectFork(RPC_URL, 21071000); + // Prior to actual deployment of the proposal (otherwise it will fail) - 21218711 + vm.createSelectFork(RPC_URL, 21218711 - 1); /// @dev Deploy your proposal OIP_168 proposal = new OIP_168(); @@ -67,7 +67,7 @@ contract OIP_168_OCGProposalTest is Test { address governor = addresses.getAddress("olympus-governor"); bool[] memory matches = suite.checkProposalCalldatas(governor); for (uint256 i; i < matches.length; i++) { - assertTrue(matches[i]); + assertTrue(matches[i], "Calldata should match"); } } else { console.log("\n\n------- Calldata check (simulation vs mainnet) -------\n"); @@ -78,6 +78,6 @@ contract OIP_168_OCGProposalTest is Test { // [DO NOT DELETE] Dummy test to ensure `setUp` is executed and the proposal simulated. function testProposal_simulate() public { - assertTrue(true); + assertTrue(true, "Proposal should be simulated"); } } diff --git a/src/test/proposals/OIP_XXX.t.sol b/src/test/proposals/OIP_XXX.t.sol index 80ba6b3d8..b724aeb3a 100644 --- a/src/test/proposals/OIP_XXX.t.sol +++ b/src/test/proposals/OIP_XXX.t.sol @@ -32,12 +32,18 @@ contract OCGProposalTest is Test { // If true, the framework will check that calldatas match. bool public hasBeenSubmitted; + string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); + // Clearinghouse Expected events event Defund(address token, uint256 amount); event Deactivate(); /// @notice Creates a sandboxed environment from a mainnet fork. function setUp() public virtual { + // Mainnet Fork at a fixed block + // Prior to actual deployment of the proposal (otherwise it will fail) and Clearinghouse v2 - 21216656 + vm.createSelectFork(RPC_URL, 21216656 - 1); + /// @dev Deploy your proposal OIP_XXX proposal = new OIP_XXX(); @@ -69,7 +75,7 @@ contract OCGProposalTest is Test { address governor = addresses.getAddress("olympus-governor"); bool[] memory matches = suite.checkProposalCalldatas(governor); for (uint256 i; i < matches.length; i++) { - assertTrue(matches[i]); + assertTrue(matches[i], "Calldata should match"); } } else { console.log("\n\n------- Calldata check (simulation vs mainnet) -------\n"); @@ -80,7 +86,7 @@ contract OCGProposalTest is Test { // [DO NOT DELETE] Dummy test to ensure `setUp` is executed and the proposal simulated. function testProposal_simulate() public { - assertTrue(true); + assertTrue(true, "Proposal should be simulated"); } /// -- OPTIONAL INTEGRATION TESTS ---------------------------------------------------- @@ -122,12 +128,28 @@ contract OCGProposalTest is Test { clearinghouseV1.emergencyShutdown(); // Check that the system is shutdown and logged in the CHREG - assertFalse(clearinghouseV1.active()); + assertFalse(clearinghouseV1.active(), "Clearinghouse should be shutdown"); // assertEq(CHREGv1(CHREG).activeCount(), 0); // Check the token balances - assertEq(dai.balanceOf(address(clearinghouseV1)), 0); - assertEq(sdai.balanceOf(address(clearinghouseV1)), 0); - assertEq(dai.balanceOf(TRSRY), cacheCH.daiBalance + cacheTRSRY.daiBalance); - assertEq(sdai.balanceOf(TRSRY), cacheCH.sdaiBalance + cacheTRSRY.sdaiBalance); + assertEq( + dai.balanceOf(address(clearinghouseV1)), + 0, + "DAI balance of clearinghouse should be 0" + ); + assertEq( + sdai.balanceOf(address(clearinghouseV1)), + 0, + "sDAI balance of clearinghouse should be 0" + ); + assertEq( + dai.balanceOf(TRSRY), + cacheCH.daiBalance + cacheTRSRY.daiBalance, + "DAI balance of treasury should be correct" + ); + assertEq( + sdai.balanceOf(TRSRY), + cacheCH.sdaiBalance + cacheTRSRY.sdaiBalance, + "sDAI balance of treasury should be correct" + ); } } From 555be4af7b60405a3352e7b09acec44321aa559e Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 13:04:05 +0400 Subject: [PATCH 135/160] Add ProposalTest contract to simplify simulation tests. Linting. --- src/proposals/OIP_166.sol | 33 ++------ src/proposals/OIP_168.sol | 2 +- src/proposals/OIP_XXX.sol | 7 +- .../proposals/EmissionManagerProposal.t.sol | 67 +--------------- src/test/proposals/OIP_166.t.sol | 59 ++------------ src/test/proposals/OIP_168.t.sol | 67 +--------------- src/test/proposals/OIP_XXX.t.sol | 66 ++-------------- src/test/proposals/ProposalTest.sol | 76 +++++++++++++++++++ 8 files changed, 109 insertions(+), 268 deletions(-) create mode 100644 src/test/proposals/ProposalTest.sol diff --git a/src/proposals/OIP_166.sol b/src/proposals/OIP_166.sol index 9f892fff0..bac8550cf 100644 --- a/src/proposals/OIP_166.sol +++ b/src/proposals/OIP_166.sol @@ -1,7 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.15; + import {console2} from "forge-std/console2.sol"; +import {ProposalScript} from "src/proposals/ProposalScript.sol"; + // OCG Proposal Simulator import {Addresses} from "proposal-sim/addresses/Addresses.sol"; import {GovernorBravoProposal} from "proposal-sim/proposals/OlympusGovernorBravoProposal.sol"; @@ -14,7 +17,8 @@ import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; import {RolesAdmin} from "src/policies/RolesAdmin.sol"; import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; -// OIP_166 is the first step in activating Olympus Onchain Governance. +/// @notice OIP_166 is the first step in activating Olympus Onchain Governance. +// solhint-disable-next-line contract-name-camelcase contract OIP_166 is GovernorBravoProposal { Kernel internal _kernel; @@ -256,28 +260,7 @@ contract OIP_166 is GovernorBravoProposal { } } -import {ScriptSuite} from "proposal-sim/script/ScriptSuite.s.sol"; - -// @notice GovernorBravoScript is a script that runs BRAVO_01 proposal. -// BRAVO_01 proposal deploys a Vault contract and an ERC20 token contract -// Then the proposal transfers ownership of both Vault and ERC20 to the timelock address -// Finally the proposal whitelist the ERC20 token in the Vault contract -// @dev Use this script to simulates or run a single proposal -// Use this as a template to create your own script -// `forge script script/GovernorBravo.s.sol:GovernorBravoScript -vvvv --rpc-url {rpc} --broadcast --verify --etherscan-api-key {key}` -contract OIP_166_Script is ScriptSuite { - string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; - - constructor() ScriptSuite(ADDRESSES_PATH, new OIP_166()) {} - - function run() public override { - // set debug mode to true and run it to build the actions list - proposal.setDebug(true); - - // run the proposal to build it - proposal.run(addresses, address(0)); - - // get the calldata for the proposal, doing so in debug mode prints it to the console - proposal.getCalldata(); - } +// solhint-disable-next-line contract-name-camelcase +contract OIP_166ProposalScript is ProposalScript { + constructor() ProposalScript(new OIP_166()) {} } diff --git a/src/proposals/OIP_168.sol b/src/proposals/OIP_168.sol index 73c28e1b8..d07c9a5e1 100644 --- a/src/proposals/OIP_168.sol +++ b/src/proposals/OIP_168.sol @@ -202,7 +202,7 @@ contract OIP_168 is GovernorBravoProposal { address operator_1_5 = addresses.getAddress("olympus-policy-operator-1_5"); address heart_1_5 = addresses.getAddress("olympus-policy-heart-1_5"); address heart_1_6 = addresses.getAddress("olympus-policy-heart-1_6"); - address clearinghouse = addresses.getAddress("olympus-policy-clearinghouse-1_2"); + // address clearinghouse = addresses.getAddress("olympus-policy-clearinghouse-1_2"); // Validate the new Heart policy has the "heart" role require( diff --git a/src/proposals/OIP_XXX.sol b/src/proposals/OIP_XXX.sol index eecfd6004..ea676a629 100644 --- a/src/proposals/OIP_XXX.sol +++ b/src/proposals/OIP_XXX.sol @@ -14,7 +14,8 @@ import {CHREGv1} from "modules/CHREG/CHREG.v1.sol"; import {TRSRYv1} from "modules/TRSRY/TRSRY.v1.sol"; import {Clearinghouse} from "policies/Clearinghouse.sol"; -// OIP_XX proposal performs all the necessary steps to upgrade the Clearinghouse. +/// @notice OIP_XXX proposal performs all the necessary steps to upgrade the Clearinghouse. +// solhint-disable-next-line contract-name-camelcase contract OIP_XXX is GovernorBravoProposal { // Data struct to cache initial balances and used them in `_validate`. struct Cache { @@ -30,7 +31,7 @@ contract OIP_XXX is GovernorBravoProposal { Kernel internal _kernel; // Returns the id of the proposal. - function id() public view override returns (uint256) { + function id() public pure override returns (uint256) { return 0; } @@ -64,7 +65,7 @@ contract OIP_XXX is GovernorBravoProposal { addresses.addAddress("olympus-policy-clearinghouse-v1.1", address(clearinghouse)); } - function _afterDeploy(Addresses addresses, address deployer) internal override { + function _afterDeploy(Addresses addresses, address) internal override { // Get relevant olympus contracts address TRSRY = address(_kernel.getModuleForKeycode(toKeycode(bytes5("TRSRY")))); address clearinghouseV0 = addresses.getAddress("olympus-policy-clearinghouse"); diff --git a/src/test/proposals/EmissionManagerProposal.t.sol b/src/test/proposals/EmissionManagerProposal.t.sol index 47cd88bc3..c6b1c08de 100644 --- a/src/test/proposals/EmissionManagerProposal.t.sol +++ b/src/test/proposals/EmissionManagerProposal.t.sol @@ -1,35 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -// Proposal test-suite imports -import "forge-std/Test.sol"; -import {TestSuite} from "proposal-sim/test/TestSuite.t.sol"; -import {Addresses} from "proposal-sim/addresses/Addresses.sol"; -import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; -import {RolesAdmin} from "policies/RolesAdmin.sol"; -import {GovernorBravoDelegator} from "src/external/governance/GovernorBravoDelegator.sol"; -import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; -import {Timelock} from "src/external/governance/Timelock.sol"; +import {ProposalTest} from "./ProposalTest.sol"; // EmissionManagerProposal imports import {EmissionManagerProposal} from "src/proposals/EmissionManagerProposal.sol"; -/// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. -/// @dev Update the `setUp` function to deploy your proposal and set the submission -/// flag to `true` once the proposal has been submitted on-chain. -/// Note: this will fail if the OCGPermissions script has not been run yet. -contract EmissionManagerProposalTest is Test { - string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; - TestSuite public suite; - Addresses public addresses; - - // Wether the proposal has been submitted or not. - // If true, the framework will check that calldatas match. - bool public hasBeenSubmitted; - - string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); - - /// @notice Creates a sandboxed environment from a mainnet fork. +contract EmissionManagerProposalTest is ProposalTest { function setUp() public virtual { // Mainnet Fork at a fixed block // Prior to actual deployment of the proposal (otherwise it will fail) - 21224026 @@ -41,43 +18,7 @@ contract EmissionManagerProposalTest is Test { /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. hasBeenSubmitted = true; - /// [DO NOT DELETE] - /// @notice This section is used to simulate the proposal on the mainnet fork. - { - // Populate addresses array - address[] memory proposalsAddresses = new address[](1); - proposalsAddresses[0] = address(proposal); - - // Deploy TestSuite contract - suite = new TestSuite(ADDRESSES_PATH, proposalsAddresses); - - // Set addresses object - addresses = suite.addresses(); - - // Set debug mode - suite.setDebug(true); - // Execute proposals - suite.testProposals(); - - // Proposals execution may change addresses, so we need to update the addresses object. - addresses = suite.addresses(); - - // Check if simulated calldatas match the ones from mainnet. - if (hasBeenSubmitted) { - address governor = addresses.getAddress("olympus-governor"); - bool[] memory matches = suite.checkProposalCalldatas(governor); - for (uint256 i; i < matches.length; i++) { - assertTrue(matches[i], "Calldata should match"); - } - } else { - console.log("\n\n------- Calldata check (simulation vs mainnet) -------\n"); - console.log("Proposal has NOT been submitted on-chain yet.\n"); - } - } - } - - // [DO NOT DELETE] Dummy test to ensure `setUp` is executed and the proposal simulated. - function testProposal_simulate() public { - assertTrue(true, "Proposal should be simulated"); + // Simulate the proposal + _simulateProposal(address(proposal)); } } diff --git a/src/test/proposals/OIP_166.t.sol b/src/test/proposals/OIP_166.t.sol index 064a36c72..d679f1695 100644 --- a/src/test/proposals/OIP_166.t.sol +++ b/src/test/proposals/OIP_166.t.sol @@ -1,35 +1,14 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -// Proposal test-suite imports -import "forge-std/Test.sol"; +import {ProposalTest} from "./ProposalTest.sol"; import {TestSuite} from "proposal-sim/test/TestSuite.t.sol"; -import {Addresses} from "proposal-sim/addresses/Addresses.sol"; -import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; import {RolesAdmin} from "policies/RolesAdmin.sol"; -import {GovernorBravoDelegator} from "src/external/governance/GovernorBravoDelegator.sol"; -import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; -import {Timelock} from "src/external/governance/Timelock.sol"; // OIP_166 imports import {OIP_166} from "src/proposals/OIP_166.sol"; -/// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. -/// @dev Update the `setUp` function to deploy your proposal and set the submission -/// flag to `true` once the proposal has been submitted on-chain. -/// Note: this will fail if the OCGPermissions script has not been run yet. -contract OIP_166_OCGProposalTest is Test { - string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; - TestSuite public suite; - Addresses public addresses; - - // Wether the proposal has been submitted or not. - // If true, the framework will check that calldatas match. - bool public hasBeenSubmitted; - - string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); - - /// @notice Creates a sandboxed environment from a mainnet fork. +contract OIP166Test is ProposalTest { function setUp() public virtual { // Mainnet Fork at a fixed block // Prior to actual deployment of the proposal (otherwise it will fail) - 20872023 @@ -41,8 +20,9 @@ contract OIP_166_OCGProposalTest is Test { /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. hasBeenSubmitted = true; - /// [DO NOT DELETE] - /// @notice This section is used to simulate the proposal on the mainnet fork. + // NOTE: unique to OIP 166 + // In prepation for this particular proposal, we need to: + // Push the roles admin "admin" permission to the Timelock from the DAO MS (multisig) { // Populate addresses array address[] memory proposalsAddresses = new address[](1); @@ -54,40 +34,15 @@ contract OIP_166_OCGProposalTest is Test { // Set addresses object addresses = suite.addresses(); - // NOTE: unique to OIP 166 - // In prepation for this particular proposal, we need to: - // Push the roles admin "admin" permission to the Timelock from the DAO MS (multisig) address daoMS = addresses.getAddress("olympus-multisig-dao"); address timelock = addresses.getAddress("olympus-timelock"); RolesAdmin rolesAdmin = RolesAdmin(addresses.getAddress("olympus-policy-roles-admin")); vm.prank(daoMS); rolesAdmin.pushNewAdmin(timelock); - - // Set debug mode - suite.setDebug(true); - // Execute proposals - suite.testProposals(); - - // Proposals execution may change addresses, so we need to update the addresses object. - addresses = suite.addresses(); - - // Check if simulated calldatas match the ones from mainnet. - if (hasBeenSubmitted) { - address governor = addresses.getAddress("olympus-governor"); - bool[] memory matches = suite.checkProposalCalldatas(governor); - for (uint256 i; i < matches.length; i++) { - assertTrue(matches[i], "Calldata should match"); - } - } else { - console.log("\n\n------- Calldata check (simulation vs mainnet) -------\n"); - console.log("Proposal has NOT been submitted on-chain yet.\n"); - } } - } - // [DO NOT DELETE] Dummy test to ensure `setUp` is executed and the proposal simulated. - function testProposal_simulate() public { - assertTrue(true, "Proposal should be simulated"); + // Simulate the proposal + _simulateProposal(address(proposal)); } } diff --git a/src/test/proposals/OIP_168.t.sol b/src/test/proposals/OIP_168.t.sol index 22f7349ed..69322edc5 100644 --- a/src/test/proposals/OIP_168.t.sol +++ b/src/test/proposals/OIP_168.t.sol @@ -1,35 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -// Proposal test-suite imports -import "forge-std/Test.sol"; -import {TestSuite} from "proposal-sim/test/TestSuite.t.sol"; -import {Addresses} from "proposal-sim/addresses/Addresses.sol"; -import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; -import {RolesAdmin} from "policies/RolesAdmin.sol"; -import {GovernorBravoDelegator} from "src/external/governance/GovernorBravoDelegator.sol"; -import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; -import {Timelock} from "src/external/governance/Timelock.sol"; +import {ProposalTest} from "./ProposalTest.sol"; // OIP_168 imports import {OIP_168} from "src/proposals/OIP_168.sol"; -/// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. -/// @dev Update the `setUp` function to deploy your proposal and set the submission -/// flag to `true` once the proposal has been submitted on-chain. -/// Note: this will fail if the OCGPermissions script has not been run yet. -contract OIP_168_OCGProposalTest is Test { - string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; - TestSuite public suite; - Addresses public addresses; - - // Wether the proposal has been submitted or not. - // If true, the framework will check that calldatas match. - bool public hasBeenSubmitted; - - string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); - - /// @notice Creates a sandboxed environment from a mainnet fork. +contract OIP168Test is ProposalTest { function setUp() public virtual { // Mainnet Fork at a fixed block // Prior to actual deployment of the proposal (otherwise it will fail) - 21218711 @@ -41,43 +18,7 @@ contract OIP_168_OCGProposalTest is Test { /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. hasBeenSubmitted = true; - /// [DO NOT DELETE] - /// @notice This section is used to simulate the proposal on the mainnet fork. - { - // Populate addresses array - address[] memory proposalsAddresses = new address[](1); - proposalsAddresses[0] = address(proposal); - - // Deploy TestSuite contract - suite = new TestSuite(ADDRESSES_PATH, proposalsAddresses); - - // Set addresses object - addresses = suite.addresses(); - - // Set debug mode - suite.setDebug(true); - // Execute proposals - suite.testProposals(); - - // Proposals execution may change addresses, so we need to update the addresses object. - addresses = suite.addresses(); - - // Check if simulated calldatas match the ones from mainnet. - if (hasBeenSubmitted) { - address governor = addresses.getAddress("olympus-governor"); - bool[] memory matches = suite.checkProposalCalldatas(governor); - for (uint256 i; i < matches.length; i++) { - assertTrue(matches[i], "Calldata should match"); - } - } else { - console.log("\n\n------- Calldata check (simulation vs mainnet) -------\n"); - console.log("Proposal has NOT been submitted on-chain yet.\n"); - } - } - } - - // [DO NOT DELETE] Dummy test to ensure `setUp` is executed and the proposal simulated. - function testProposal_simulate() public { - assertTrue(true, "Proposal should be simulated"); + // Simulate the proposal + _simulateProposal(address(proposal)); } } diff --git a/src/test/proposals/OIP_XXX.t.sol b/src/test/proposals/OIP_XXX.t.sol index b724aeb3a..7780ef0da 100644 --- a/src/test/proposals/OIP_XXX.t.sol +++ b/src/test/proposals/OIP_XXX.t.sol @@ -1,44 +1,23 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -// Proposal test-suite imports -import "forge-std/Test.sol"; -import {TestSuite} from "proposal-sim/test/TestSuite.t.sol"; -import {Addresses} from "proposal-sim/addresses/Addresses.sol"; +import {ProposalTest} from "./ProposalTest.sol"; import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; -import {RolesAdmin} from "policies/RolesAdmin.sol"; -import {GovernorBravoDelegator} from "src/external/governance/GovernorBravoDelegator.sol"; -import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; -import {Timelock} from "src/external/governance/Timelock.sol"; // OIP_XXX imports import {OIP_XXX, Clearinghouse, CHREGv1, IERC20, IERC4626} from "src/proposals/OIP_XXX.sol"; -/// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. -/// @dev Update the `setUp` function to deploy your proposal and set the submission -/// flag to `true` once the proposal has been submitted on-chain. -contract OCGProposalTest is Test { - string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; - TestSuite public suite; - Addresses public addresses; - +contract OIPXXXTest is ProposalTest { // Data struct to cache initial balances. struct Cache { uint256 daiBalance; uint256 sdaiBalance; } - // Wether the proposal has been submitted or not. - // If true, the framework will check that calldatas match. - bool public hasBeenSubmitted; - - string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); - // Clearinghouse Expected events event Defund(address token, uint256 amount); event Deactivate(); - /// @notice Creates a sandboxed environment from a mainnet fork. function setUp() public virtual { // Mainnet Fork at a fixed block // Prior to actual deployment of the proposal (otherwise it will fail) and Clearinghouse v2 - 21216656 @@ -50,43 +29,8 @@ contract OCGProposalTest is Test { /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. hasBeenSubmitted = false; - /// [DO NOT DELETE] - /// @notice This section is used to simulate the proposal on the mainnet fork. - { - // Populate addresses array - address[] memory proposalsAddresses = new address[](1); - proposalsAddresses[0] = address(proposal); - - // Deploy TestSuite contract - suite = new TestSuite(ADDRESSES_PATH, proposalsAddresses); - - // Set addresses object - addresses = suite.addresses(); - - suite.setDebug(true); - // Execute proposals - suite.testProposals(); - - // Proposals execution may change addresses, so we need to update the addresses object. - addresses = suite.addresses(); - - // Check if simulated calldatas match the ones from mainnet. - if (hasBeenSubmitted) { - address governor = addresses.getAddress("olympus-governor"); - bool[] memory matches = suite.checkProposalCalldatas(governor); - for (uint256 i; i < matches.length; i++) { - assertTrue(matches[i], "Calldata should match"); - } - } else { - console.log("\n\n------- Calldata check (simulation vs mainnet) -------\n"); - console.log("Proposal has NOT been submitted on-chain yet.\n"); - } - } - } - - // [DO NOT DELETE] Dummy test to ensure `setUp` is executed and the proposal simulated. - function testProposal_simulate() public { - assertTrue(true, "Proposal should be simulated"); + // Simulate the proposal + _simulateProposal(address(proposal)); } /// -- OPTIONAL INTEGRATION TESTS ---------------------------------------------------- @@ -129,7 +73,7 @@ contract OCGProposalTest is Test { // Check that the system is shutdown and logged in the CHREG assertFalse(clearinghouseV1.active(), "Clearinghouse should be shutdown"); - // assertEq(CHREGv1(CHREG).activeCount(), 0); + assertEq(CHREGv1(CHREG).activeCount(), 1, "CHREG should have 1 active policy"); // Check the token balances assertEq( dai.balanceOf(address(clearinghouseV1)), diff --git a/src/test/proposals/ProposalTest.sol b/src/test/proposals/ProposalTest.sol new file mode 100644 index 000000000..e72e33e58 --- /dev/null +++ b/src/test/proposals/ProposalTest.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +// Proposal test-suite imports +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; +import {TestSuite} from "proposal-sim/test/TestSuite.t.sol"; +import {Addresses} from "proposal-sim/addresses/Addresses.sol"; +import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; +import {RolesAdmin} from "policies/RolesAdmin.sol"; +import {GovernorBravoDelegator} from "src/external/governance/GovernorBravoDelegator.sol"; +import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; +import {Timelock} from "src/external/governance/Timelock.sol"; + +/// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. +/// @dev Update the `setUp` function to deploy your proposal and set the submission +/// flag to `true` once the proposal has been submitted on-chain. +/// Note: this will fail if the OCGPermissions script has not been run yet. +abstract contract ProposalTest is Test { + string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; + TestSuite public suite; + Addresses public addresses; + + // Wether the proposal has been submitted or not. + // If true, the framework will check that calldatas match. + bool public hasBeenSubmitted; + + string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); + + /// @notice This function simulates the proposal with the given address. + /// @dev This function assumes the following: + /// - A mainnet fork has been created using `vm.createSelectFork` with a block number prior to the proposal deployment. + /// - If the proposal has been submitted on-chain, the `hasBeenSubmitted` flag has been set to `true`. + /// - The proposal contract has been deployed within the test contract and passed as an argument. + /// + /// @param proposal_ The address of the proposal contract. + function _simulateProposal(address proposal_) internal virtual { + /// @notice This section is used to simulate the proposal on the mainnet fork. + { + // Populate addresses array + address[] memory proposalsAddresses = new address[](1); + proposalsAddresses[0] = address(proposal_); + + // Deploy TestSuite contract + suite = new TestSuite(ADDRESSES_PATH, proposalsAddresses); + + // Set addresses object + addresses = suite.addresses(); + + // Set debug mode + suite.setDebug(true); + // Execute proposals + suite.testProposals(); + + // Proposals execution may change addresses, so we need to update the addresses object. + addresses = suite.addresses(); + + // Check if simulated calldatas match the ones from mainnet. + if (hasBeenSubmitted) { + address governor = addresses.getAddress("olympus-governor"); + bool[] memory matches = suite.checkProposalCalldatas(governor); + for (uint256 i; i < matches.length; i++) { + assertTrue(matches[i], "Calldata should match"); + } + } else { + console2.log("\n\n------- Calldata check (simulation vs mainnet) -------\n"); + console2.log("Proposal has NOT been submitted on-chain yet.\n"); + } + } + } + + /// @dev Dummy test to ensure `setUp` is executed and the proposal simulated. + function testProposal_simulate() public { + assertTrue(true, "Proposal should be simulated"); + } +} From bbcd90f3c96df9732ce36f9220afeab45d613381 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 13:16:07 +0400 Subject: [PATCH 136/160] Shift contract registry and loan consolidator proposal tests to use ProposalTest --- .../proposals/ContractRegistryProposal.t.sol | 73 +------------------ .../proposals/LoanConsolidatorProposal.t.sol | 69 +----------------- 2 files changed, 8 insertions(+), 134 deletions(-) diff --git a/src/test/proposals/ContractRegistryProposal.t.sol b/src/test/proposals/ContractRegistryProposal.t.sol index 0131b21e3..0b4f28967 100644 --- a/src/test/proposals/ContractRegistryProposal.t.sol +++ b/src/test/proposals/ContractRegistryProposal.t.sol @@ -1,88 +1,23 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -// Proposal test-suite imports -import "forge-std/Test.sol"; -import {TestSuite} from "proposal-sim/test/TestSuite.t.sol"; -import {Addresses} from "proposal-sim/addresses/Addresses.sol"; -import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; -import {RolesAdmin} from "policies/RolesAdmin.sol"; -import {GovernorBravoDelegator} from "src/external/governance/GovernorBravoDelegator.sol"; -import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; -import {Timelock} from "src/external/governance/Timelock.sol"; - -// ContractRegistry -import {OlympusContractRegistry} from "src/modules/RGSTY/OlympusContractRegistry.sol"; -import {ContractRegistryAdmin} from "src/policies/ContractRegistryAdmin.sol"; +import {ProposalTest} from "./ProposalTest.sol"; import {ContractRegistryProposal} from "src/proposals/ContractRegistryProposal.sol"; -/// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. -/// @dev Update the `setUp` function to deploy your proposal and set the submission -/// flag to `true` once the proposal has been submitted on-chain. -/// Note: this will fail if the OCGPermissions script has not been run yet. -contract ContractRegistryProposalTest is Test { - string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; - TestSuite public suite; - Addresses public addresses; - - // Wether the proposal has been submitted or not. - // If true, the framework will check that calldatas match. - bool public hasBeenSubmitted; - - string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); - - /// @notice Creates a sandboxed environment from a mainnet fork. +contract ContractRegistryProposalTest is ProposalTest { function setUp() public virtual { // Mainnet Fork at a fixed block // Prior to the proposal deployment (otherwise it will fail) vm.createSelectFork(RPC_URL, 21070000); - // TODO update addresses when RGSTY and ContractRegistryAdmin are deployed - /// @dev Deploy your proposal ContractRegistryProposal proposal = new ContractRegistryProposal(); /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. hasBeenSubmitted = false; - /// [DO NOT DELETE] - /// @notice This section is used to simulate the proposal on the mainnet fork. - { - // Populate addresses array - address[] memory proposalsAddresses = new address[](1); - proposalsAddresses[0] = address(proposal); - - // Deploy TestSuite contract - suite = new TestSuite(ADDRESSES_PATH, proposalsAddresses); - - // Set addresses object - addresses = suite.addresses(); - - // Set debug mode - suite.setDebug(true); - // Execute proposals - suite.testProposals(); - - // Proposals execution may change addresses, so we need to update the addresses object. - addresses = suite.addresses(); - - // Check if simulated calldatas match the ones from mainnet. - if (hasBeenSubmitted) { - address governor = addresses.getAddress("olympus-governor"); - bool[] memory matches = suite.checkProposalCalldatas(governor); - for (uint256 i; i < matches.length; i++) { - assertTrue(matches[i]); - } - } else { - console.log("\n\n------- Calldata check (simulation vs mainnet) -------\n"); - console.log("Proposal has NOT been submitted on-chain yet.\n"); - } - } - } - - // [DO NOT DELETE] Dummy test to ensure `setUp` is executed and the proposal simulated. - function testProposal_simulate() public { - assertTrue(true); + // Simulate the proposal + _simulateProposal(address(proposal)); } } diff --git a/src/test/proposals/LoanConsolidatorProposal.t.sol b/src/test/proposals/LoanConsolidatorProposal.t.sol index efde73939..d2d70e6e7 100644 --- a/src/test/proposals/LoanConsolidatorProposal.t.sol +++ b/src/test/proposals/LoanConsolidatorProposal.t.sol @@ -1,85 +1,24 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -// Proposal test-suite imports -import "forge-std/Test.sol"; -import {TestSuite} from "proposal-sim/test/TestSuite.t.sol"; -import {Addresses} from "proposal-sim/addresses/Addresses.sol"; -import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; -import {RolesAdmin} from "policies/RolesAdmin.sol"; -import {GovernorBravoDelegator} from "src/external/governance/GovernorBravoDelegator.sol"; -import {GovernorBravoDelegate} from "src/external/governance/GovernorBravoDelegate.sol"; -import {Timelock} from "src/external/governance/Timelock.sol"; +import {ProposalTest} from "./ProposalTest.sol"; // Proposal imports import {LoanConsolidatorProposal} from "src/proposals/LoanConsolidatorProposal.sol"; -/// @notice Creates a sandboxed environment from a mainnet fork, to simulate the proposal. -/// @dev Update the `setUp` function to deploy your proposal and set the submission -/// flag to `true` once the proposal has been submitted on-chain. -/// Note: this will fail if the OCGPermissions script has not been run yet. -contract LoanConsolidatorProposalTest is Test { - string public constant ADDRESSES_PATH = "./src/proposals/addresses.json"; - TestSuite public suite; - Addresses public addresses; - - // Wether the proposal has been submitted or not. - // If true, the framework will check that calldatas match. - bool public hasBeenSubmitted; - - string RPC_URL = vm.envString("FORK_TEST_RPC_URL"); - - /// @notice Creates a sandboxed environment from a mainnet fork. +contract LoanConsolidatorProposalTest is ProposalTest { function setUp() public virtual { // Mainnet Fork at a fixed block // Prior to the proposal deployment (otherwise it will fail) vm.createSelectFork(RPC_URL, 21070000); - // TODO update addresses when LoanConsolidator is deployed - /// @dev Deploy your proposal LoanConsolidatorProposal proposal = new LoanConsolidatorProposal(); /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. hasBeenSubmitted = false; - /// [DO NOT DELETE] - /// @notice This section is used to simulate the proposal on the mainnet fork. - { - // Populate addresses array - address[] memory proposalsAddresses = new address[](1); - proposalsAddresses[0] = address(proposal); - - // Deploy TestSuite contract - suite = new TestSuite(ADDRESSES_PATH, proposalsAddresses); - - // Set addresses object - addresses = suite.addresses(); - - // Set debug mode - suite.setDebug(true); - // Execute proposals - suite.testProposals(); - - // Proposals execution may change addresses, so we need to update the addresses object. - addresses = suite.addresses(); - - // Check if simulated calldatas match the ones from mainnet. - if (hasBeenSubmitted) { - address governor = addresses.getAddress("olympus-governor"); - bool[] memory matches = suite.checkProposalCalldatas(governor); - for (uint256 i; i < matches.length; i++) { - assertTrue(matches[i]); - } - } else { - console.log("\n\n------- Calldata check (simulation vs mainnet) -------\n"); - console.log("Proposal has NOT been submitted on-chain yet.\n"); - } - } - } - - // [DO NOT DELETE] Dummy test to ensure `setUp` is executed and the proposal simulated. - function testProposal_simulate() public { - assertTrue(true); + // Simulate the proposal + _simulateProposal(address(proposal)); } } From cfb921f4081690628986eeb463b0702965bd0c31 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 13:35:45 +0400 Subject: [PATCH 137/160] LoanConsolidator: upgrade CHREG to the latest version if v1.0 is detected during fork tests --- src/test/policies/LoanConsolidatorFork.t.sol | 66 +++++++++++++------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index e16fda53f..9d7090e7d 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GLP-3.0 pragma solidity ^0.8.15; -import {Test, console2, stdStorage, StdStorage} from "forge-std/Test.sol"; +import {Test, console2} from "forge-std/Test.sol"; import {MockFlashloanLender} from "src/test/mocks/MockFlashloanLender.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; @@ -19,6 +19,7 @@ import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; import {RolesAdmin} from "src/policies/RolesAdmin.sol"; import {TRSRYv1} from "src/modules/TRSRY/TRSRY.v1.sol"; import {CHREGv1} from "src/modules/CHREG/CHREG.v1.sol"; +import {OlympusClearinghouseRegistry} from "src/modules/CHREG/OlympusClearinghouseRegistry.sol"; import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; import {ClonesWithImmutableArgs} from "clones/ClonesWithImmutableArgs.sol"; @@ -28,7 +29,6 @@ import {ClearinghouseLowerLTC} from "src/test/lib/ClearinghouseLowerLTC.sol"; import {ClearinghouseHigherLTC} from "src/test/lib/ClearinghouseHigherLTC.sol"; contract LoanConsolidatorForkTest is Test { - using stdStorage for StdStorage; using ClonesWithImmutableArgs for address; LoanConsolidator public utils; @@ -107,6 +107,46 @@ contract LoanConsolidatorForkTest is Test { // Determine the kernel executor kernelExecutor = Kernel(kernel).executor(); + // CHREG v1 (0x24b96f2150BF1ed10D3e8B28Ed33E392fbB4Cad5) has a bug with the registryCount. If the version is 1.0, mimic upgrading the module + (uint8 chregMajor, uint8 chregMinor) = CHREG.VERSION(); + if (chregMajor == 1 && chregMinor == 0) { + console2.log("CHREG v1.0 detected, upgrading to current version..."); + + // Determine the active clearinghouse + uint256 activeClearinghouseCount = CHREG.activeCount(); + address activeClearinghouse; + if (activeClearinghouseCount >= 1) { + activeClearinghouse = CHREG.active(0); + console2.log("Setting active clearinghouse to:", activeClearinghouse); + + // CHREG only accepts one active clearinghouse + activeClearinghouseCount = 1; + } + + // Determine the inactive clearinghouses + uint256 inactiveClearinghouseCount = CHREG.registryCount(); + address[] memory inactiveClearinghouses = new address[]( + inactiveClearinghouseCount - activeClearinghouseCount + ); + for (uint256 i = 0; i < inactiveClearinghouseCount; i++) { + // Skip if active, as the constructor will check for duplicates + if (CHREG.registry(i) == activeClearinghouse) continue; + + inactiveClearinghouses[i] = CHREG.registry(i); + } + + // Deploy the current version of CHREG + CHREG = new OlympusClearinghouseRegistry( + kernel, + activeClearinghouse, + inactiveClearinghouses + ); + + // Upgrade the module + vm.prank(kernelExecutor); + kernel.executeAction(Actions.UpgradeModule, address(CHREG)); + } + // Install RGSTY (since block is pinned, it won't be installed) RGSTY = new OlympusContractRegistry(address(kernel)); vm.prank(kernelExecutor); @@ -159,12 +199,6 @@ contract LoanConsolidatorForkTest is Test { trsryUsdsBalance = usds.balanceOf(address(TRSRY)); trsrySusdsBalance = susds.balanceOf(address(TRSRY)); - // Increment the CHREG registry count - // The registryCount does not seem to be incremented on the fork, which is... weird. - console2.log("CHREG registry count before:", CHREG.registryCount()); - stdstore.target(address(CHREG)).sig("registryCount()").checked_write(3); - console2.log("CHREG registry count after:", CHREG.registryCount()); - admin = vm.addr(0x2); // Deploy LoanConsolidator @@ -458,8 +492,6 @@ contract LoanConsolidatorForkTest is Test { } function _createClearinghouseWithLowerLTC() internal returns (Clearinghouse) { - uint256 registryCountBefore = CHREG.registryCount(); - ClearinghouseLowerLTC newClearinghouse = new ClearinghouseLowerLTC( address(ohm), address(gohm), @@ -478,18 +510,10 @@ contract LoanConsolidatorForkTest is Test { // Rebalance the new clearinghouse newClearinghouse.rebalance(); - // Increment the CHREG registry count - // The registryCount does not seem to be incremented on the fork, which is... weird. - stdstore.target(address(CHREG)).sig("registryCount()").checked_write( - registryCountBefore + 1 - ); - return Clearinghouse(address(newClearinghouse)); } function _createClearinghouseWithHigherLTC() internal returns (Clearinghouse) { - uint256 registryCountBefore = CHREG.registryCount(); - ClearinghouseHigherLTC newClearinghouse = new ClearinghouseHigherLTC( address(ohm), address(gohm), @@ -508,12 +532,6 @@ contract LoanConsolidatorForkTest is Test { // Rebalance the new clearinghouse newClearinghouse.rebalance(); - // Increment the CHREG registry count - // The registryCount does not seem to be incremented on the fork, which is... weird. - stdstore.target(address(CHREG)).sig("registryCount()").checked_write( - registryCountBefore + 1 - ); - return Clearinghouse(address(newClearinghouse)); } From c1c1ffaa931a92e1218a0025e1a44eea741abcc6 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 14:17:56 +0400 Subject: [PATCH 138/160] Add additional tests for contract/policy active state --- src/test/policies/LoanConsolidatorFork.t.sol | 141 ++++++++++++++++++- 1 file changed, 140 insertions(+), 1 deletion(-) diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index 9d7090e7d..d36c43d2b 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -20,7 +20,7 @@ import {RolesAdmin} from "src/policies/RolesAdmin.sol"; import {TRSRYv1} from "src/modules/TRSRY/TRSRY.v1.sol"; import {CHREGv1} from "src/modules/CHREG/CHREG.v1.sol"; import {OlympusClearinghouseRegistry} from "src/modules/CHREG/OlympusClearinghouseRegistry.sol"; -import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; +import {Kernel, Actions, toKeycode, Module} from "src/Kernel.sol"; import {ClonesWithImmutableArgs} from "clones/ClonesWithImmutableArgs.sol"; import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; @@ -779,6 +779,12 @@ contract LoanConsolidatorForkTest is Test { // [X] it reverts // given the caller is not the owner of coolerTo // [X] it reverts + // given clearinghouseFrom is not an active policy + // given clearinghouseFrom is disabled + // [X] it succeeds + // [X] it succeeds + // given clearinghouseFrom is disabled + // [X] it succeeds // given clearinghouseTo is disabled // [X] it reverts // given coolerFrom is equal to coolerTo @@ -980,6 +986,139 @@ contract LoanConsolidatorForkTest is Test { ); } + function test_consolidate_clearinghouseFromNotActive() public givenPolicyActive givenActivated { + uint256[] memory idsA = _idsA(); + + // Create a Cooler on the USDS Clearinghouse + address coolerUsds = _createCooler(coolerFactory, walletA, usds); + address coolerDai = address(coolerA); + + (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( + address(clearinghouseUsds), + coolerDai, + idsA + ); + + // Grant approvals + _grantCallerApprovals(walletA, address(clearinghouseUsds), coolerDai, coolerUsds, idsA); + + // Deal fees in USDS to the wallet + deal(address(usds), walletA, interest + protocolFee); + // Make sure the wallet has no DAI + deal(address(dai), walletA, 0); + + // Disable the previous clearinghouse + vm.prank(emergency); + clearinghouse.emergencyShutdown(); + + // Consolidate loans + _consolidate( + walletA, + address(clearinghouse), + address(clearinghouseUsds), + coolerDai, + coolerUsds, + idsA + ); + + _assertCoolerLoansCrossClearinghouse(coolerDai, coolerUsds, _GOHM_AMOUNT); + } + + function test_consolidate_clearinghouseFromPolicyNotActive() + public + givenPolicyActive + givenActivated + { + uint256[] memory idsA = _idsA(); + + // Create a Cooler on the USDS Clearinghouse + address coolerUsds = _createCooler(coolerFactory, walletA, usds); + address coolerDai = address(coolerA); + + (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( + address(clearinghouseUsds), + coolerDai, + idsA + ); + + // Grant approvals + _grantCallerApprovals(walletA, address(clearinghouseUsds), coolerDai, coolerUsds, idsA); + + // Deal fees in USDS to the wallet + deal(address(usds), walletA, interest + protocolFee); + // Make sure the wallet has no DAI + deal(address(dai), walletA, 0); + + // Uninstall the previous Clearinghouse as a policy + vm.prank(kernelExecutor); + kernel.executeAction(Actions.DeactivatePolicy, address(clearinghouse)); + + // Consolidate loans + _consolidate( + walletA, + address(clearinghouse), + address(clearinghouseUsds), + coolerDai, + coolerUsds, + idsA + ); + + _assertCoolerLoansCrossClearinghouse(coolerDai, coolerUsds, _GOHM_AMOUNT); + } + + function test_consolidate_clearinghouseFromNotActive_clearinghouseFromPolicyNotActive_reverts() + public + givenPolicyActive + givenActivated + { + uint256[] memory idsA = _idsA(); + + // Create a Cooler on the USDS Clearinghouse + address coolerUsds = _createCooler(coolerFactory, walletA, usds); + address coolerDai = address(coolerA); + + (, uint256 interest, , uint256 protocolFee) = utils.fundsRequired( + address(clearinghouseUsds), + coolerDai, + idsA + ); + + // Grant approvals + _grantCallerApprovals(walletA, address(clearinghouseUsds), coolerDai, coolerUsds, idsA); + + // Deal fees in USDS to the wallet + deal(address(usds), walletA, interest + protocolFee); + // Make sure the wallet has no DAI + deal(address(dai), walletA, 0); + + // Disable the previous Clearinghouse + vm.prank(emergency); + clearinghouse.emergencyShutdown(); + + // Uninstall the previous Clearinghouse as a policy + vm.prank(kernelExecutor); + kernel.executeAction(Actions.DeactivatePolicy, address(clearinghouse)); + + // Expect revert + // The Clearinghouse will attempt to be defunded, which will fail + vm.expectRevert( + abi.encodeWithSelector( + Module.Module_PolicyNotPermitted.selector, + address(clearinghouse) + ) + ); + + // Consolidate loans + _consolidate( + walletA, + address(clearinghouse), + address(clearinghouseUsds), + coolerDai, + coolerUsds, + idsA + ); + } + function test_consolidate_sameCooler_noLoans_reverts() public givenPolicyActive givenActivated { // Grant approvals _grantCallerApprovals(type(uint256).max, type(uint256).max, type(uint256).max); From f35eb2de6ac02e81d43c0d32f340280c7610ebc3 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 15:14:51 +0400 Subject: [PATCH 139/160] Use the contract address for the old CHREG version --- src/test/policies/LoanConsolidatorFork.t.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/policies/LoanConsolidatorFork.t.sol b/src/test/policies/LoanConsolidatorFork.t.sol index d36c43d2b..4009085a0 100644 --- a/src/test/policies/LoanConsolidatorFork.t.sol +++ b/src/test/policies/LoanConsolidatorFork.t.sol @@ -108,8 +108,7 @@ contract LoanConsolidatorForkTest is Test { kernelExecutor = Kernel(kernel).executor(); // CHREG v1 (0x24b96f2150BF1ed10D3e8B28Ed33E392fbB4Cad5) has a bug with the registryCount. If the version is 1.0, mimic upgrading the module - (uint8 chregMajor, uint8 chregMinor) = CHREG.VERSION(); - if (chregMajor == 1 && chregMinor == 0) { + if (address(CHREG) == 0x24b96f2150BF1ed10D3e8B28Ed33E392fbB4Cad5) { console2.log("CHREG v1.0 detected, upgrading to current version..."); // Determine the active clearinghouse From 8965a1fd31415b845d4c5ea62bc3b80d294f757b Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 15:16:54 +0400 Subject: [PATCH 140/160] Update CHREG to latest version --- src/scripts/env.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/env.json b/src/scripts/env.json index 078da7df3..641f428c3 100644 --- a/src/scripts/env.json +++ b/src/scripts/env.json @@ -68,7 +68,7 @@ "OlympusInstructions": "0x0000000000000000000000000000000000000000", "OlympusVotes": "0x0000000000000000000000000000000000000000", "OlympusBoostedLiquidityRegistry": "0x375E06C694B5E50aF8be8FB03495A612eA3e2275", - "OlympusClearinghouseRegistry": "0x24b96f2150BF1ed10D3e8B28Ed33E392fbB4Cad5", + "OlympusClearinghouseRegistry": "0x69a3E97027d21a5984B6a543b36603fFbC6543a4", "OlympusContractRegistry": "0x0000000000000000000000000000000000000000" }, "submodules": { From 8cac9bc50a8329b2568b61474ff978e40ea8145c Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 15:17:20 +0400 Subject: [PATCH 141/160] Bump proposal ids --- src/proposals/ContractRegistryProposal.sol | 2 +- src/proposals/LoanConsolidatorProposal.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proposals/ContractRegistryProposal.sol b/src/proposals/ContractRegistryProposal.sol index 9f99af0a3..cb821b782 100644 --- a/src/proposals/ContractRegistryProposal.sol +++ b/src/proposals/ContractRegistryProposal.sol @@ -36,7 +36,7 @@ contract ContractRegistryProposal is GovernorBravoProposal { // Returns the id of the proposal. function id() public pure override returns (uint256) { - return 5; + return 6; } // Returns the name of the proposal. diff --git a/src/proposals/LoanConsolidatorProposal.sol b/src/proposals/LoanConsolidatorProposal.sol index 846e1946c..e65e07a92 100644 --- a/src/proposals/LoanConsolidatorProposal.sol +++ b/src/proposals/LoanConsolidatorProposal.sol @@ -23,7 +23,7 @@ contract LoanConsolidatorProposal is GovernorBravoProposal { // Returns the id of the proposal. function id() public pure override returns (uint256) { - return 6; + return 7; } // Returns the name of the proposal. From f3f72765e0ebe695485cf685201b3f609338a23f Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 15:22:50 +0400 Subject: [PATCH 142/160] Deployment of RGSTY and ContractRegistryAdmin contracts --- deployments/.mainnet-1733829635.json | 4 ++++ src/proposals/addresses.json | 4 ++-- src/scripts/env.json | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 deployments/.mainnet-1733829635.json diff --git a/deployments/.mainnet-1733829635.json b/deployments/.mainnet-1733829635.json new file mode 100644 index 000000000..4b5fbb42a --- /dev/null +++ b/deployments/.mainnet-1733829635.json @@ -0,0 +1,4 @@ +{ + "OlympusContractRegistry": "0x89631595649Cc6dEBa249A8012a5b2d88C8ddE48", + "ContractRegistryAdmin": "0xBA05d48Fb94dC76820EB7ea1B360fd6DfDEabdc5" +} diff --git a/src/proposals/addresses.json b/src/proposals/addresses.json index 3831a3c7d..69beab46f 100644 --- a/src/proposals/addresses.json +++ b/src/proposals/addresses.json @@ -135,12 +135,12 @@ "chainId": 1 }, { - "addr": "0x0000000000000000000000000000000000000000", + "addr": "0x89631595649Cc6dEBa249A8012a5b2d88C8ddE48", "name": "olympus-module-rgsty", "chainId": 1 }, { - "addr": "0x0000000000000000000000000000000000000000", + "addr": "0xBA05d48Fb94dC76820EB7ea1B360fd6DfDEabdc5", "name": "olympus-policy-contract-registry-admin", "chainId": 1 } diff --git a/src/scripts/env.json b/src/scripts/env.json index 641f428c3..cfa4de222 100644 --- a/src/scripts/env.json +++ b/src/scripts/env.json @@ -69,7 +69,7 @@ "OlympusVotes": "0x0000000000000000000000000000000000000000", "OlympusBoostedLiquidityRegistry": "0x375E06C694B5E50aF8be8FB03495A612eA3e2275", "OlympusClearinghouseRegistry": "0x69a3E97027d21a5984B6a543b36603fFbC6543a4", - "OlympusContractRegistry": "0x0000000000000000000000000000000000000000" + "OlympusContractRegistry": "0x89631595649Cc6dEBa249A8012a5b2d88C8ddE48" }, "submodules": { "PRICE": { @@ -104,7 +104,7 @@ "YieldRepurchaseFacility": "0xcaA3d3E653A626e2656d2E799564fE952D39d855", "ReserveMigrator": "0x986b99579BEc7B990331474b66CcDB94Fa2419F5", "EmissionManager": "0x50f441a3387625bDA8B8081cE3fd6C04CC48C0A2", - "ContractRegistryAdmin": "0x0000000000000000000000000000000000000000" + "ContractRegistryAdmin": "0xBA05d48Fb94dC76820EB7ea1B360fd6DfDEabdc5" }, "legacy": { "OHM": "0x64aa3364F17a4D01c6f1751Fd97C2BD3D7e7f1D5", From be077d38d21856f8f3414420095e93c15bb89270 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 15:25:46 +0400 Subject: [PATCH 143/160] Deployment for LoanConsolidator --- deployments/.mainnet-1733829839.json | 3 +++ src/proposals/addresses.json | 2 +- src/scripts/env.json | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 deployments/.mainnet-1733829839.json diff --git a/deployments/.mainnet-1733829839.json b/deployments/.mainnet-1733829839.json new file mode 100644 index 000000000..d04e66f3a --- /dev/null +++ b/deployments/.mainnet-1733829839.json @@ -0,0 +1,3 @@ +{ + "LoanConsolidator": "0x784cA0C006b8651BAB183829A99fA46BeCe50dBc" +} diff --git a/src/proposals/addresses.json b/src/proposals/addresses.json index 69beab46f..d9fe7edab 100644 --- a/src/proposals/addresses.json +++ b/src/proposals/addresses.json @@ -130,7 +130,7 @@ "chainId": 1 }, { - "addr": "0x0000000000000000000000000000000000000000", + "addr": "0x784cA0C006b8651BAB183829A99fA46BeCe50dBc", "name": "olympus-policy-loan-consolidator", "chainId": 1 }, diff --git a/src/scripts/env.json b/src/scripts/env.json index cfa4de222..524840a41 100644 --- a/src/scripts/env.json +++ b/src/scripts/env.json @@ -99,7 +99,7 @@ "BLVaultLusd": "0xfbB3742628e8D19E0E2d7D8dde208821C09dE960", "Clearinghouse": "0x1e094fE00E13Fd06D64EeA4FB3cD912893606fE0", "LegacyBurner": "0x367149cf2d04D3114fFD1Cc6b273222664908D0B", - "LoanConsolidator": "0xB15bcb1b6593d85890f5287Baa2245B8A29F464a", + "LoanConsolidator": "0x784cA0C006b8651BAB183829A99fA46BeCe50dBc", "pOLY": "0xb37796941cA55b7E4243841930C104Ee325Da5a1", "YieldRepurchaseFacility": "0xcaA3d3E653A626e2656d2E799564fE952D39d855", "ReserveMigrator": "0x986b99579BEc7B990331474b66CcDB94Fa2419F5", From d25b3d8f1ec34b6635b663a2ebbc50b874a36a52 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 15:47:40 +0400 Subject: [PATCH 144/160] Assume installation batch script has been run during RGTSY proposal simulation --- .../proposals/ContractRegistryProposal.t.sol | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/test/proposals/ContractRegistryProposal.t.sol b/src/test/proposals/ContractRegistryProposal.t.sol index 0b4f28967..f98bbfb5f 100644 --- a/src/test/proposals/ContractRegistryProposal.t.sol +++ b/src/test/proposals/ContractRegistryProposal.t.sol @@ -2,14 +2,22 @@ pragma solidity ^0.8.0; import {ProposalTest} from "./ProposalTest.sol"; +import {TestSuite} from "proposal-sim/test/TestSuite.t.sol"; +import {console2} from "forge-std/console2.sol"; + +import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; +import {ContractRegistryAdmin} from "src/policies/ContractRegistryAdmin.sol"; import {ContractRegistryProposal} from "src/proposals/ContractRegistryProposal.sol"; contract ContractRegistryProposalTest is ProposalTest { + Kernel public kernel; + function setUp() public virtual { // Mainnet Fork at a fixed block // Prior to the proposal deployment (otherwise it will fail) - vm.createSelectFork(RPC_URL, 21070000); + // 21371770 is the deployment block for ContractRegistryAdmin + vm.createSelectFork(RPC_URL, 21371770); /// @dev Deploy your proposal ContractRegistryProposal proposal = new ContractRegistryProposal(); @@ -17,6 +25,49 @@ contract ContractRegistryProposalTest is ProposalTest { /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. hasBeenSubmitted = false; + // Populate addresses array + { + // Populate addresses array + address[] memory proposalsAddresses = new address[](1); + proposalsAddresses[0] = address(proposal); + + // Deploy TestSuite contract + suite = new TestSuite(ADDRESSES_PATH, proposalsAddresses); + + // Set addresses object + addresses = suite.addresses(); + + kernel = Kernel(addresses.getAddress("olympus-kernel")); + } + + // Simulate the ContractRegistryInstall batch script having been run + // The simulation will revert otherwise + // Install RGSTY + { + address rgsty = addresses.getAddress("olympus-module-rgsty"); + + if (address(kernel.getModuleForKeycode(toKeycode("RGSTY"))) == address(0)) { + console2.log("Installing RGSTY"); + + vm.prank(addresses.getAddress("olympus-multisig-dao")); + kernel.executeAction(Actions.InstallModule, rgsty); + } + } + + // Install ContractRegistryAdmin + { + address contractRegistryAdmin = addresses.getAddress( + "olympus-policy-contract-registry-admin" + ); + + if (!ContractRegistryAdmin(contractRegistryAdmin).isActive()) { + console2.log("Activating ContractRegistryAdmin"); + + vm.prank(addresses.getAddress("olympus-multisig-dao")); + kernel.executeAction(Actions.ActivatePolicy, contractRegistryAdmin); + } + } + // Simulate the proposal _simulateProposal(address(proposal)); } From 20f991b2ed152dc86cde0291e3574a60c13bd501 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 15:55:34 +0400 Subject: [PATCH 145/160] Assume installation batch script has been run during LoanConsolidator proposal simulation --- .../proposals/LoanConsolidatorProposal.t.sol | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/test/proposals/LoanConsolidatorProposal.t.sol b/src/test/proposals/LoanConsolidatorProposal.t.sol index d2d70e6e7..cb2bda72b 100644 --- a/src/test/proposals/LoanConsolidatorProposal.t.sol +++ b/src/test/proposals/LoanConsolidatorProposal.t.sol @@ -2,14 +2,22 @@ pragma solidity ^0.8.0; import {ProposalTest} from "./ProposalTest.sol"; +import {TestSuite} from "proposal-sim/test/TestSuite.t.sol"; +import {console2} from "forge-std/console2.sol"; + +import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; +import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; // Proposal imports import {LoanConsolidatorProposal} from "src/proposals/LoanConsolidatorProposal.sol"; contract LoanConsolidatorProposalTest is ProposalTest { + Kernel public kernel; + function setUp() public virtual { // Mainnet Fork at a fixed block // Prior to the proposal deployment (otherwise it will fail) + // TODO fill in the block number for the RGSTY proposal execution vm.createSelectFork(RPC_URL, 21070000); /// @dev Deploy your proposal @@ -18,6 +26,36 @@ contract LoanConsolidatorProposalTest is ProposalTest { /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. hasBeenSubmitted = false; + // Populate addresses array + { + // Populate addresses array + address[] memory proposalsAddresses = new address[](1); + proposalsAddresses[0] = address(proposal); + + // Deploy TestSuite contract + suite = new TestSuite(ADDRESSES_PATH, proposalsAddresses); + + // Set addresses object + addresses = suite.addresses(); + + kernel = Kernel(addresses.getAddress("olympus-kernel")); + } + + // Simulate the ContractRegistryInstall batch script having been run + // The simulation will revert otherwise + // This proposal will also fail until the RGSTY proposal has been executed + // Install LoanConsolidator + { + address loanConsolidator = addresses.getAddress("olympus-policy-loan-consolidator"); + + if (!LoanConsolidator(loanConsolidator).isActive()) { + console2.log("Activating LoanConsolidator"); + + vm.prank(addresses.getAddress("olympus-multisig-dao")); + kernel.executeAction(Actions.ActivatePolicy, loanConsolidator); + } + } + // Simulate the proposal _simulateProposal(address(proposal)); } From 217051aa733cba0d85009ef43d8192391bf911e0 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 10 Dec 2024 16:00:23 +0400 Subject: [PATCH 146/160] Update block for LoanConsolidator proposal --- src/test/proposals/LoanConsolidatorProposal.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/proposals/LoanConsolidatorProposal.t.sol b/src/test/proposals/LoanConsolidatorProposal.t.sol index cb2bda72b..64ff72ec0 100644 --- a/src/test/proposals/LoanConsolidatorProposal.t.sol +++ b/src/test/proposals/LoanConsolidatorProposal.t.sol @@ -18,7 +18,7 @@ contract LoanConsolidatorProposalTest is ProposalTest { // Mainnet Fork at a fixed block // Prior to the proposal deployment (otherwise it will fail) // TODO fill in the block number for the RGSTY proposal execution - vm.createSelectFork(RPC_URL, 21070000); + vm.createSelectFork(RPC_URL, 21371786); /// @dev Deploy your proposal LoanConsolidatorProposal proposal = new LoanConsolidatorProposal(); From 334f1ae417617846fdb712307f845eda8deede5c Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Wed, 11 Dec 2024 11:14:37 +0400 Subject: [PATCH 147/160] Update docs on proposal submission --- src/scripts/proposals/README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/scripts/proposals/README.md b/src/scripts/proposals/README.md index 9256843a5..d5eb5ce3a 100644 --- a/src/scripts/proposals/README.md +++ b/src/scripts/proposals/README.md @@ -2,7 +2,16 @@ This directory contains scripts for submitting proposals to the Olympus Governor. -## Setup +## Environment + +The following are required: + +- `bash` shell +- A [foundry](https://getfoundry.sh/) installation +- A `.env` file with the following environment variables: + - `RPC_URL`: The RPC URL of the chain you wish to submit the proposal on. + +## Creating a Proposal Script The OCG proposal must have a separate contract that inherits from `ProposalScript`. See the `ContractRegistryProposal` for an example. @@ -25,9 +34,10 @@ It is possible to test proposal submission (and execution) on a forked chain. To 6. Submit your proposal by running `./submitProposal.sh` with the appropriate arguments. 7. Alternatively, you can execute the proposal (as if the proposal has passed) by running `./executeOnTestnet.sh` with the appropriate arguments. -## Mainnet +## Submitting a Proposal 1. Configure a wallet with `cast wallet` 2. Delegate your gOHM voting power to your wallet address. - This can be done by running `./delegate.sh` with the appropriate arguments, or through the Tenderly dashboard. 3. Submit your proposal by running `./submitProposal.sh` with the appropriate arguments. + - Example: `./src/scripts/proposals/submitProposal.sh --file src/proposals/ContractRegistryProposal.sol --contract ContractRegistryProposalScript --account --broadcast true --env .env` From d181a3efa01017a87a3d2db2f012dca78e30e65d Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 13 Dec 2024 19:08:15 +0400 Subject: [PATCH 148/160] Add support for Ledger when running OlyBatch scripts --- src/scripts/ops/batch.sh | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/scripts/ops/batch.sh b/src/scripts/ops/batch.sh index c616be62b..0c779a186 100755 --- a/src/scripts/ops/batch.sh +++ b/src/scripts/ops/batch.sh @@ -3,7 +3,7 @@ # Run a multisig batch # # Usage: -# ./batch.sh --contract --batch --broadcast --testnet --env +# ./batch.sh --contract --batch --ledger --broadcast --testnet --env # # Environment variables: # RPC_URL @@ -36,6 +36,7 @@ set +a # Disable automatic export # Set sane defaults BROADCAST=${broadcast:-false} TESTNET=${testnet:-false} +LEDGER=${ledger:-false} # Check if contract is set if [ -z "$contract" ]; then @@ -61,12 +62,25 @@ if [ -z "$SIGNER_ADDRESS" ]; then exit 1 fi +# Validate that LEDGER is set to true or false +if [ "$LEDGER" != "true" ] && [ "$LEDGER" != "false" ]; then + echo "Invalid value for LEDGER. Must be true or false." + exit 1 +fi + +# Set the LEDGER_FLAG +LEDGER_FLAG="" +if [ "$LEDGER" == "true" ]; then + LEDGER_FLAG="--ledger" +fi + echo "Contract name: $contract" echo "Batch name: $batch" echo "Using RPC at URL: $RPC_URL" echo "Using signer address: $SIGNER_ADDRESS" echo "Broadcasting: $BROADCAST" echo "Using testnet: $TESTNET" +echo "Using ledger: $LEDGER" # Execute the batch -TESTNET=$TESTNET forge script ./src/scripts/ops/batches/$contract.sol:$contract --sig "$batch(bool)()" $BROADCAST --slow -vvv --sender $SIGNER_ADDRESS --rpc-url $RPC_URL +TESTNET=$TESTNET forge script ./src/scripts/ops/batches/$contract.sol:$contract --sig "$batch(bool)()" $BROADCAST --slow -vvv --sender $SIGNER_ADDRESS --rpc-url $RPC_URL $LEDGER_FLAG From 791d3153b81f2e0ffa3771c73a526346d8b5044a Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 13 Dec 2024 19:49:54 +0400 Subject: [PATCH 149/160] Add missing environment variables for batch scripts --- src/scripts/ops/batch.sh | 47 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/scripts/ops/batch.sh b/src/scripts/ops/batch.sh index 0c779a186..95c64247b 100755 --- a/src/scripts/ops/batch.sh +++ b/src/scripts/ops/batch.sh @@ -9,6 +9,11 @@ # RPC_URL # SIGNER_ADDRESS # TESTNET +# CHAIN +# DAO_MS +# POLICY_MS +# EMERGENCY_MS +# LEDGER_MNEMONIC_INDEX (ledger only) # Exit if any error occurs set -e @@ -68,19 +73,59 @@ if [ "$LEDGER" != "true" ] && [ "$LEDGER" != "false" ]; then exit 1 fi +# If LEDGER is true, validate that MNEMONIC_INDEX is set +if [ "$LEDGER" == "true" ] && [ -z "$LEDGER_MNEMONIC_INDEX" ]; then + echo "No LEDGER_MNEMONIC_INDEX provided. Specify the LEDGER_MNEMONIC_INDEX in the $ENV_FILE file." + exit 1 +fi + +# Validate that CHAIN is set +if [ -z "$CHAIN" ]; then + echo "No chain provided. Specify the CHAIN in the $ENV_FILE file." + exit 1 +fi + +# Validate that DAO_MS is set +if [ -z "$DAO_MS" ]; then + echo "No DAO MS provided. Specify the DAO_MS in the $ENV_FILE file." + exit 1 +fi + +# Validate that POLICY_MS is set +if [ -z "$POLICY_MS" ]; then + echo "No POLICY MS provided. Specify the POLICY_MS in the $ENV_FILE file." + exit 1 +fi + +# Validate that EMERGENCY_MS is set +if [ -z "$EMERGENCY_MS" ]; then + echo "No EMERGENCY MS provided. Specify the EMERGENCY_MS in the $ENV_FILE file." + exit 1 +fi + # Set the LEDGER_FLAG LEDGER_FLAG="" +WALLET_TYPE_ENV="" +LEDGER_MNEMONIC_INDEX_ENV="" if [ "$LEDGER" == "true" ]; then LEDGER_FLAG="--ledger" + WALLET_TYPE_ENV="WALLET_TYPE=ledger" + LEDGER_MNEMONIC_INDEX_ENV="MNEMONIC_INDEX=$LEDGER_MNEMONIC_INDEX" +else + WALLET_TYPE_ENV="WALLET_TYPE=local" fi echo "Contract name: $contract" echo "Batch name: $batch" +echo "Using chain: $CHAIN" echo "Using RPC at URL: $RPC_URL" echo "Using signer address: $SIGNER_ADDRESS" +echo "Using DAO MS: $DAO_MS" +echo "Using POLICY MS: $POLICY_MS" +echo "Using EMERGENCY MS: $EMERGENCY_MS" echo "Broadcasting: $BROADCAST" echo "Using testnet: $TESTNET" echo "Using ledger: $LEDGER" # Execute the batch -TESTNET=$TESTNET forge script ./src/scripts/ops/batches/$contract.sol:$contract --sig "$batch(bool)()" $BROADCAST --slow -vvv --sender $SIGNER_ADDRESS --rpc-url $RPC_URL $LEDGER_FLAG +TESTNET=$TESTNET $WALLET_TYPE_ENV $LEDGER_MNEMONIC_INDEX_ENV forge script ./src/scripts/ops/batches/$contract.sol:$contract --sig "$batch(bool)()" $BROADCAST --slow -vvv --sender $SIGNER_ADDRESS --rpc-url $RPC_URL $LEDGER_FLAG From 5d119cf65cab644a3a4b114422252e2da9f040b7 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 13 Dec 2024 22:32:03 +0400 Subject: [PATCH 150/160] Logging in batches --- src/scripts/ops/batches/ContractRegistryInstall.sol | 2 ++ src/scripts/ops/batches/LoanConsolidatorInstall.sol | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/scripts/ops/batches/ContractRegistryInstall.sol b/src/scripts/ops/batches/ContractRegistryInstall.sol index 331e4f6c8..570df2141 100644 --- a/src/scripts/ops/batches/ContractRegistryInstall.sol +++ b/src/scripts/ops/batches/ContractRegistryInstall.sol @@ -47,5 +47,7 @@ contract ContractRegistryInstall is OlyBatch { contractRegistryAdmin ) ); + + console2.log("Batch completed"); } } diff --git a/src/scripts/ops/batches/LoanConsolidatorInstall.sol b/src/scripts/ops/batches/LoanConsolidatorInstall.sol index 84c731012..c6df143c5 100644 --- a/src/scripts/ops/batches/LoanConsolidatorInstall.sol +++ b/src/scripts/ops/batches/LoanConsolidatorInstall.sol @@ -38,5 +38,7 @@ contract LoanConsolidatorInstall is OlyBatch { loanConsolidator ) ); + + console2.log("Batch completed"); } } From 7b69dd025b470bed12f4e948b0cd0a73e79b9a35 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 13 Dec 2024 22:46:14 +0400 Subject: [PATCH 151/160] Working batch script with Ledger --- src/scripts/ops/batch.sh | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/scripts/ops/batch.sh b/src/scripts/ops/batch.sh index 95c64247b..a1c82505f 100755 --- a/src/scripts/ops/batch.sh +++ b/src/scripts/ops/batch.sh @@ -103,16 +103,13 @@ if [ -z "$EMERGENCY_MS" ]; then exit 1 fi -# Set the LEDGER_FLAG -LEDGER_FLAG="" -WALLET_TYPE_ENV="" -LEDGER_MNEMONIC_INDEX_ENV="" +# Set the variables for using a Ledger wallet +# Export variables that BatchScript.sol uses if [ "$LEDGER" == "true" ]; then - LEDGER_FLAG="--ledger" - WALLET_TYPE_ENV="WALLET_TYPE=ledger" - LEDGER_MNEMONIC_INDEX_ENV="MNEMONIC_INDEX=$LEDGER_MNEMONIC_INDEX" + export WALLET_TYPE="ledger" + export MNEMONIC_INDEX="$LEDGER_MNEMONIC_INDEX" else - WALLET_TYPE_ENV="WALLET_TYPE=local" + export WALLET_TYPE="local" fi echo "Contract name: $contract" @@ -128,4 +125,4 @@ echo "Using testnet: $TESTNET" echo "Using ledger: $LEDGER" # Execute the batch -TESTNET=$TESTNET $WALLET_TYPE_ENV $LEDGER_MNEMONIC_INDEX_ENV forge script ./src/scripts/ops/batches/$contract.sol:$contract --sig "$batch(bool)()" $BROADCAST --slow -vvv --sender $SIGNER_ADDRESS --rpc-url $RPC_URL $LEDGER_FLAG +TESTNET=$TESTNET forge script ./src/scripts/ops/batches/$contract.sol:$contract --sig "$batch(bool)()" $BROADCAST --slow -vvv --sender $SIGNER_ADDRESS --rpc-url $RPC_URL From bc3dbfc1e02a425b10991596dd702cff642ae44d Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 16 Dec 2024 19:41:21 +0400 Subject: [PATCH 152/160] Set sender address for proposal submission script --- src/scripts/proposals/submitProposal.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/scripts/proposals/submitProposal.sh b/src/scripts/proposals/submitProposal.sh index e9ef7491f..38636ed26 100755 --- a/src/scripts/proposals/submitProposal.sh +++ b/src/scripts/proposals/submitProposal.sh @@ -64,9 +64,15 @@ if [ -z "$account" ]; then exit 1 fi +# Get the address of the cast wallet +echo "Getting address for cast account $account" +CAST_ADDRESS=$(cast wallet address --account $account) +echo "" + echo "Using proposal contract: $file:$contract" echo "Using RPC at URL: $RPC_URL" echo "Using forge account: $account" +echo "Sender address: $CAST_ADDRESS" # Set the fork flag FORK_FLAG="" @@ -87,4 +93,6 @@ else fi # Run the forge script -forge script $file:$contract -vvv --rpc-url $RPC_URL --account $account $FORK_FLAG $BROADCAST_FLAG +echo "" +echo "Running forge script..." +forge script $file:$contract --rpc-url $RPC_URL --account $account --froms $CAST_ADDRESS $FORK_FLAG $BROADCAST_FLAG -vvv From c296ff434310c677fee5809a0d57c677d0256b8f Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 16 Dec 2024 20:00:21 +0400 Subject: [PATCH 153/160] Mark ContractRegistryAdmin proposal as submitted (LoanConsolidator proposal will continue failing) --- src/test/proposals/ContractRegistryProposal.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/proposals/ContractRegistryProposal.t.sol b/src/test/proposals/ContractRegistryProposal.t.sol index f98bbfb5f..ce6fc68e1 100644 --- a/src/test/proposals/ContractRegistryProposal.t.sol +++ b/src/test/proposals/ContractRegistryProposal.t.sol @@ -23,7 +23,7 @@ contract ContractRegistryProposalTest is ProposalTest { ContractRegistryProposal proposal = new ContractRegistryProposal(); /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. - hasBeenSubmitted = false; + hasBeenSubmitted = true; // Populate addresses array { From 3130992f3764ee2f6549ad8b8cdd5bae6934eb98 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 30 Dec 2024 10:52:25 +0400 Subject: [PATCH 154/160] chore: linting --- src/test/proposals/LoanConsolidatorProposal.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/proposals/LoanConsolidatorProposal.t.sol b/src/test/proposals/LoanConsolidatorProposal.t.sol index 64ff72ec0..dcf6c7c7a 100644 --- a/src/test/proposals/LoanConsolidatorProposal.t.sol +++ b/src/test/proposals/LoanConsolidatorProposal.t.sol @@ -5,7 +5,7 @@ import {ProposalTest} from "./ProposalTest.sol"; import {TestSuite} from "proposal-sim/test/TestSuite.t.sol"; import {console2} from "forge-std/console2.sol"; -import {Kernel, Actions, toKeycode} from "src/Kernel.sol"; +import {Kernel, Actions} from "src/Kernel.sol"; import {LoanConsolidator} from "src/policies/LoanConsolidator.sol"; // Proposal imports From eb79b15a38a2cc0a166f6e0a076a94421e8a9aca Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 30 Dec 2024 10:52:42 +0400 Subject: [PATCH 155/160] Update fork block for LoanConsolidator proposal test --- src/test/proposals/LoanConsolidatorProposal.t.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/proposals/LoanConsolidatorProposal.t.sol b/src/test/proposals/LoanConsolidatorProposal.t.sol index dcf6c7c7a..625958eea 100644 --- a/src/test/proposals/LoanConsolidatorProposal.t.sol +++ b/src/test/proposals/LoanConsolidatorProposal.t.sol @@ -17,8 +17,7 @@ contract LoanConsolidatorProposalTest is ProposalTest { function setUp() public virtual { // Mainnet Fork at a fixed block // Prior to the proposal deployment (otherwise it will fail) - // TODO fill in the block number for the RGSTY proposal execution - vm.createSelectFork(RPC_URL, 21371786); + vm.createSelectFork(RPC_URL, 21501128 + 1); /// @dev Deploy your proposal LoanConsolidatorProposal proposal = new LoanConsolidatorProposal(); From ef0126735d4b7e35a87a26a5d20f1df903f97fe5 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 30 Dec 2024 10:55:39 +0400 Subject: [PATCH 156/160] Minor update to comment --- src/test/proposals/LoanConsolidatorProposal.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/proposals/LoanConsolidatorProposal.t.sol b/src/test/proposals/LoanConsolidatorProposal.t.sol index 625958eea..00a8f2de0 100644 --- a/src/test/proposals/LoanConsolidatorProposal.t.sol +++ b/src/test/proposals/LoanConsolidatorProposal.t.sol @@ -40,7 +40,7 @@ contract LoanConsolidatorProposalTest is ProposalTest { kernel = Kernel(addresses.getAddress("olympus-kernel")); } - // Simulate the ContractRegistryInstall batch script having been run + // Simulate the LoanConsolidatorInstall batch script having been run // The simulation will revert otherwise // This proposal will also fail until the RGSTY proposal has been executed // Install LoanConsolidator From 64c825c687a25264567a351888ecb8919b61200b Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 2 Jan 2025 11:37:42 +0400 Subject: [PATCH 157/160] Correct argument to forge for setting the sender --- src/scripts/proposals/submitProposal.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/proposals/submitProposal.sh b/src/scripts/proposals/submitProposal.sh index 38636ed26..cdd499f4e 100755 --- a/src/scripts/proposals/submitProposal.sh +++ b/src/scripts/proposals/submitProposal.sh @@ -95,4 +95,4 @@ fi # Run the forge script echo "" echo "Running forge script..." -forge script $file:$contract --rpc-url $RPC_URL --account $account --froms $CAST_ADDRESS $FORK_FLAG $BROADCAST_FLAG -vvv +forge script $file:$contract --rpc-url $RPC_URL --account $account --sender $CAST_ADDRESS $FORK_FLAG $BROADCAST_FLAG -vvv From 9546ab51514c46eed0b583c5dcd09696da028fdd Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 2 Jan 2025 11:42:49 +0400 Subject: [PATCH 158/160] Mark proposal submission as true --- src/test/proposals/LoanConsolidatorProposal.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/proposals/LoanConsolidatorProposal.t.sol b/src/test/proposals/LoanConsolidatorProposal.t.sol index 00a8f2de0..31ff51ae7 100644 --- a/src/test/proposals/LoanConsolidatorProposal.t.sol +++ b/src/test/proposals/LoanConsolidatorProposal.t.sol @@ -23,7 +23,7 @@ contract LoanConsolidatorProposalTest is ProposalTest { LoanConsolidatorProposal proposal = new LoanConsolidatorProposal(); /// @dev Set `hasBeenSubmitted` to `true` once the proposal has been submitted on-chain. - hasBeenSubmitted = false; + hasBeenSubmitted = true; // Populate addresses array { From ee9001705185d31dc1954c6db2f2661cc7190ec9 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 2 Jan 2025 11:43:30 +0400 Subject: [PATCH 159/160] Disable activation of LoanConsolidator policy during simulation --- src/test/proposals/LoanConsolidatorProposal.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/proposals/LoanConsolidatorProposal.t.sol b/src/test/proposals/LoanConsolidatorProposal.t.sol index 31ff51ae7..f2d70dc21 100644 --- a/src/test/proposals/LoanConsolidatorProposal.t.sol +++ b/src/test/proposals/LoanConsolidatorProposal.t.sol @@ -44,7 +44,7 @@ contract LoanConsolidatorProposalTest is ProposalTest { // The simulation will revert otherwise // This proposal will also fail until the RGSTY proposal has been executed // Install LoanConsolidator - { + if (!hasBeenSubmitted) { address loanConsolidator = addresses.getAddress("olympus-policy-loan-consolidator"); if (!LoanConsolidator(loanConsolidator).isActive()) { From 235af27c2787fa33fcd7b30857ba8b20ef443e08 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 14 Jan 2025 10:57:36 +0400 Subject: [PATCH 160/160] Move the fork block so that tests pass --- src/test/proposals/LoanConsolidatorProposal.t.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/proposals/LoanConsolidatorProposal.t.sol b/src/test/proposals/LoanConsolidatorProposal.t.sol index f2d70dc21..2823e8341 100644 --- a/src/test/proposals/LoanConsolidatorProposal.t.sol +++ b/src/test/proposals/LoanConsolidatorProposal.t.sol @@ -17,7 +17,8 @@ contract LoanConsolidatorProposalTest is ProposalTest { function setUp() public virtual { // Mainnet Fork at a fixed block // Prior to the proposal deployment (otherwise it will fail) - vm.createSelectFork(RPC_URL, 21501128 + 1); + // 21531758 is when the LoanConsolidator was installed in the kernel + vm.createSelectFork(RPC_URL, 21531758 + 1); /// @dev Deploy your proposal LoanConsolidatorProposal proposal = new LoanConsolidatorProposal();