From d4a2b81e02990083a1a7dcce1a43cef8d431c6f6 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Fri, 7 Aug 2026 14:28:16 -0300 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20auditor:=20support=20non-collateral?= =?UTF-8?q?=20markets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/brave-otter-glides.md | 5 + .gas-snapshot | 1240 +++++++++--------- contracts/Auditor.sol | 56 +- contracts/periphery/DebtManager.sol | 2 +- contracts/periphery/DebtPreviewer.sol | 72 +- contracts/periphery/DebtRoller.sol | 2 +- contracts/periphery/InstallmentsRouter.sol | 2 +- contracts/periphery/IntegrationPreviewer.sol | 20 +- contracts/periphery/Previewer.sol | 6 +- deploy/Markets.ts | 15 +- hardhat.config.ts | 5 + test/Auditor.t.sol | 231 +++- test/AuditorUpgrade.t.sol | 233 ++++ test/DebtManager.t.sol | 66 +- test/DebtPreviewer.t.sol | 151 ++- test/DebtRoller.t.sol | 2 +- test/InstallmentsRouter.t.sol | 4 +- test/IntegrationPreviewer.t.sol | 22 + test/Market.t.sol | 189 ++- test/Pauser.t.sol | 4 +- test/Previewer.t.sol | 88 +- test/Protocol.t.sol | 117 +- test/RewardsController.t.sol | 16 +- test/VerifiedMarket.t.sol | 207 ++- test/hardhat/0_admin.ts | 8 +- test/hardhat/1_auditor.ts | 45 + test/hardhat/defaultEnv.ts | 2 +- test/hardhat/marketEnv.ts | 2 +- 28 files changed, 2045 insertions(+), 767 deletions(-) create mode 100644 .changeset/brave-otter-glides.md create mode 100644 test/AuditorUpgrade.t.sol diff --git a/.changeset/brave-otter-glides.md b/.changeset/brave-otter-glides.md new file mode 100644 index 000000000..e9ea8e9f5 --- /dev/null +++ b/.changeset/brave-otter-glides.md @@ -0,0 +1,5 @@ +--- +"@exactly/protocol": minor +--- + +✨ auditor: support non-collateral markets diff --git a/.gas-snapshot b/.gas-snapshot index 3237eca19..83692cc72 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -5,100 +5,126 @@ AirdropTest:testClaimWrongAmountShouldRevert() (gas: 81891) AirdropTest:testClaimZeroShouldRevert() (gas: 45809) AirdropTest:testSablierStreamWithdraw() (gas: 359040) AirdropTest:testSablierStreamWithdrawMax() (gas: 362987) -AuditorTest:testAccountShortfall() (gas: 228911) -AuditorTest:testAccountShortfallRevert() (gas: 275777) -AuditorTest:testBorrowMPValidation() (gas: 250433) -AuditorTest:testBorrowMPValidationRevert() (gas: 297339) -AuditorTest:testDynamicCloseFactor() (gas: 1721221) -AuditorTest:testEnableEnterExitMultipleMarkets() (gas: 1877618) -AuditorTest:testEnableMarket() (gas: 183905) -AuditorTest:testEnableMarketAlreadyListed() (gas: 184325) -AuditorTest:testEnableMarketAuditorMismatch() (gas: 67401) -AuditorTest:testEnableMarketShouldRevertWithInvalidPriceFeed() (gas: 224028) -AuditorTest:testEnterExitMarket() (gas: 352813) -AuditorTest:testExitMarketOwning() (gas: 280661) -DebtManagerTest:testAllowanceSurplus() (gas: 1022365) -DebtManagerTest:testApproveMaliciousMarket() (gas: 50443) -DebtManagerTest:testApproveMarket() (gas: 87838) +AuditorTest:testAccountLiquiditySkipsWithdrawSimulationOfNonCollateralMarket() (gas: 358928) +AuditorTest:testAccountShortfall() (gas: 232046) +AuditorTest:testAccountShortfallRevert() (gas: 278958) +AuditorTest:testBorrowMPValidation() (gas: 253169) +AuditorTest:testBorrowMPValidationRevert() (gas: 300098) +AuditorTest:testCheckLiquidationSkipsNonCollateralMarkets() (gas: 860404) +AuditorTest:testCheckLiquidationZeroRepayForNonCollateralSeizeMarket() (gas: 1363622) +AuditorTest:testCheckShortfallBypassesNonCollateralMarket() (gas: 380970) +AuditorTest:testDynamicCloseFactor() (gas: 1904339) +AuditorTest:testEnableEnterExitMultipleMarkets() (gas: 2062510) +AuditorTest:testEnableMarket() (gas: 185021) +AuditorTest:testEnableMarketAlreadyListed() (gas: 185118) +AuditorTest:testEnableMarketAuditorMismatch() (gas: 67577) +AuditorTest:testEnableMarketNonCollateral() (gas: 202309) +AuditorTest:testEnableMarketShouldRevertWithInvalidPriceFeed() (gas: 224269) +AuditorTest:testEnableSecondMarketNonCollateral() (gas: 650491) +AuditorTest:testEnterExitMarket() (gas: 356288) +AuditorTest:testExitMarketOwning() (gas: 283320) +AuditorTest:testExitNonCollateralMarketWithShortfall() (gas: 878071) +AuditorTest:testHandleBadDebtIgnoresNonCollateralMarkets() (gas: 939389) +AuditorTest:testSetNonCollateral() (gas: 441647) +AuditorTest:testSetNonCollateralNotListed() (gas: 41722) +AuditorTest:testSetNonCollateralOnlyAdmin() (gas: 204776) +AuditorUpgradeForkTest:testBorrowFromNonCollateralMarketAfterUpgrade() (gas: 2855175) +AuditorUpgradeForkTest:testLegacyMarketsGetterAfterUpgrade() (gas: 207927) +AuditorUpgradeForkTest:testMarketsOperateAfterUpgrade() (gas: 15010210) +AuditorUpgradeForkTest:testSetNonCollateralAfterUpgrade() (gas: 1661429) +AuditorUpgradeForkTest:testUpgradePreservesState() (gas: 1255586) +DebtManagerTest:testAllowanceSurplus() (gas: 1022592) +DebtManagerTest:testApproveMaliciousMarket() (gas: 50536) +DebtManagerTest:testApproveMarket() (gas: 87931) DebtManagerTest:testBalancerFlashloanCallFromDifferentOrigin() (gas: 102422) DebtManagerTest:testCallReceiveFlashLoanFromAnyAddress() (gas: 90578) -DebtManagerTest:testDeleverage() (gas: 1088400) -DebtManagerTest:testDeleverageHalfPosition() (gas: 1203869) -DebtManagerTest:testDeleverageIncremental() (gas: 1992646) -DebtManagerTest:testDeleverageWithMoreThanBalancerAvailableLiquidity() (gas: 1263035) -DebtManagerTest:testDeleverageWithPartialWithdraw() (gas: 1140921) -DebtManagerTest:testDeleverageWithWithdraw() (gas: 1131376) -DebtManagerTest:testFakeMarketDeleverage() (gas: 1271920) -DebtManagerTest:testFakeMarketLeverage() (gas: 1310259) -DebtManagerTest:testFakeMarketRollFixed() (gas: 2000450) -DebtManagerTest:testFixedRoll() (gas: 1243426) -DebtManagerTest:testFixedRollSameMaturityWithThreeLoops() (gas: 1018921) -DebtManagerTest:testFixedRollWithAccurateBorrowSlippage() (gas: 1767036) -DebtManagerTest:testFixedRollWithAccurateBorrowSlippageWithThreeLoops() (gas: 2807997) -DebtManagerTest:testFixedRollWithAccurateRepaySlippage() (gas: 1766975) -DebtManagerTest:testFixedRollWithAccurateRepaySlippageWithThreeLoops() (gas: 2767551) -DebtManagerTest:testFixedToFloatingRoll() (gas: 1255924) -DebtManagerTest:testFixedToFloatingRollHigherThanAvailableLiquidity() (gas: 1331325) -DebtManagerTest:testFixedToFloatingRollHigherThanAvailableLiquidityWithSlippage() (gas: 2012662) -DebtManagerTest:testFixedToFloatingRollHigherThanAvailableLiquidityWithSlippageWithThreeLoops() (gas: 2699957) -DebtManagerTest:testFixedToFloatingRollWithAccurateSlippage() (gas: 1683565) -DebtManagerTest:testFlashloanFeeGreaterThanZero() (gas: 782096) -DebtManagerTest:testFloatingToFixedRoll() (gas: 1165678) -DebtManagerTest:testFloatingToFixedRollHigherThanAvailableLiquidity() (gas: 1352455) -DebtManagerTest:testFloatingToFixedRollHigherThanAvailableLiquidityWithSlippage() (gas: 2128858) -DebtManagerTest:testFloatingToFixedRollHigherThanAvailableLiquidityWithSlippageWithThreePools() (gas: 2818426) -DebtManagerTest:testFloatingToFixedRollWithAccurateSlippage() (gas: 1732739) -DebtManagerTest:testFloatingToFixedRollWithAccurateSlippageWithPreviousPosition() (gas: 1954939) -DebtManagerTest:testFuzzRolls(uint8[4],uint8[4],uint256[4],uint40[4],uint8[4]) (runs: 256, μ: 8031714, ~: 8129987) -DebtManagerTest:testLateFixedRoll() (gas: 1454770) -DebtManagerTest:testLateFixedRollWithThreeLoops() (gas: 2043936) -DebtManagerTest:testLateFixedToFloatingRoll() (gas: 1449634) -DebtManagerTest:testLateFixedToFloatingRollWithThreeLoops() (gas: 2012715) -DebtManagerTest:testLeverage() (gas: 749989) -DebtManagerTest:testLeverageIncremental() (gas: 1665713) -DebtManagerTest:testLeverageShouldFailWhenHealthFactorNearOne() (gas: 1458607) -DebtManagerTest:testLeverageWithAlreadyDepositedAmount() (gas: 941971) -DebtManagerTest:testLeverageWithInvalidBalancerVault() (gas: 4042887) -DebtManagerTest:testLeverageWithMoreThanBalancerAvailableLiquidity() (gas: 1125430) -DebtManagerTest:testLeverageWithNegativePrincipal() (gas: 1337277) -DebtManagerTest:testLeverageWithPartialNegativePrincipal() (gas: 1506311) -DebtManagerTest:testMockBalancerVault() (gas: 6414698) -DebtManagerTest:testPartialDeleverageWithWithdrawAndNewRatio() (gas: 1156222) -DebtManagerTest:testPartialDeleverageWithWithdrawKeepingRatio() (gas: 1156227) -DebtManagerTest:testPartialFixedRoll() (gas: 1263664) -DebtManagerTest:testPartialFixedToFloatingRoll() (gas: 1219810) -DebtManagerTest:testPartialLateFixedRoll() (gas: 1480224) -DebtManagerTest:testPartialLateFixedToFloatingRoll() (gas: 1481888) -DebtManagerTest:testPermit2AndLeverage() (gas: 885615) -DebtManagerTest:testPermitAndDeleverage() (gas: 1231263) -DebtManagerTest:testPermitAndLeverage() (gas: 992037) -DebtManagerTest:testPermitAndRollFloatingToFixed() (gas: 1348662) -DebtManagerTest:testPermitAndTransferLeverage() (gas: 914899) -DebtPreviewerTest:testDeleverageRatesSameAsset() (gas: 1045747) -DebtPreviewerTest:testLeverageRatesCrossAsset() (gas: 778919) -DebtPreviewerTest:testLeverageRatesSameAsset() (gas: 488430) -DebtPreviewerTest:testLeverageRatesWithNativeBorrow() (gas: 415439) -DebtPreviewerTest:testLeverageRatesWithNegativeNativeResult() (gas: 415524) -DebtPreviewerTest:testLeverageRatesZeroPrincipalCrossAsset() (gas: 710976) -DebtPreviewerTest:testLeverageRatesZeroPrincipalSameAsset() (gas: 418324) -DebtPreviewerTest:testPreviewDeleverageSameAsset() (gas: 2713003) -DebtPreviewerTest:testPreviewEmptyLeverage() (gas: 346638) -DebtPreviewerTest:testPreviewLeverage() (gas: 1000709) -DebtPreviewerTest:testPreviewLeverageBalancerAvailableLiquidity() (gas: 351622) -DebtPreviewerTest:testPreviewLeverageSameAsset() (gas: 1407496) -DebtPreviewerTest:testPreviewLeverageSameAssetNegativePrincipal() (gas: 2383847) -DebtPreviewerTest:testPreviewLeverageSameAssetPartialNegativePrincipal() (gas: 2028140) -DebtPreviewerTest:testPreviewLeverageSameUSDCAssetMaxRatioMultipleCollateralAndDebt() (gas: 2862639) -DebtPreviewerTest:testPreviewLeverageSameUSDCAssetWithDeposit() (gas: 2798718) -DebtPreviewerTest:testPreviewLeverageSameWETHAssetMaxRatioMultipleCollateralAndDebt() (gas: 2933847) -DebtPreviewerTest:testPreviewLeverageSameWETHAssetMultipleCollateralAndDebtWithMinHealthFactor() (gas: 2933875) -DebtPreviewerTest:testPreviewMaxRatioWithdrawWithSameAssetLeverage() (gas: 1189820) -DebtPreviewerTest:testPreviewSameAssetInvalidLeverageShouldCapRatio() (gas: 1229662) +DebtManagerTest:testDeleverage() (gas: 1089293) +DebtManagerTest:testDeleverageHalfPosition() (gas: 1204740) +DebtManagerTest:testDeleverageIncremental() (gas: 1994781) +DebtManagerTest:testDeleverageWithMoreThanBalancerAvailableLiquidity() (gas: 1264405) +DebtManagerTest:testDeleverageWithNonCollateralMarket() (gas: 2291786) +DebtManagerTest:testDeleverageWithPartialWithdraw() (gas: 1142239) +DebtManagerTest:testDeleverageWithWithdraw() (gas: 1132694) +DebtManagerTest:testFakeMarketDeleverage() (gas: 1272013) +DebtManagerTest:testFakeMarketLeverage() (gas: 1310352) +DebtManagerTest:testFakeMarketRollFixed() (gas: 2000614) +DebtManagerTest:testFixedRoll() (gas: 1243717) +DebtManagerTest:testFixedRollSameMaturityWithThreeLoops() (gas: 1019067) +DebtManagerTest:testFixedRollWithAccurateBorrowSlippage() (gas: 1767566) +DebtManagerTest:testFixedRollWithAccurateBorrowSlippageWithThreeLoops() (gas: 2808849) +DebtManagerTest:testFixedRollWithAccurateRepaySlippage() (gas: 1767505) +DebtManagerTest:testFixedRollWithAccurateRepaySlippageWithThreeLoops() (gas: 2768403) +DebtManagerTest:testFixedToFloatingRoll() (gas: 1256261) +DebtManagerTest:testFixedToFloatingRollHigherThanAvailableLiquidity() (gas: 1331671) +DebtManagerTest:testFixedToFloatingRollHigherThanAvailableLiquidityWithSlippage() (gas: 2013302) +DebtManagerTest:testFixedToFloatingRollHigherThanAvailableLiquidityWithSlippageWithThreeLoops() (gas: 2700831) +DebtManagerTest:testFixedToFloatingRollWithAccurateSlippage() (gas: 1684119) +DebtManagerTest:testFlashloanFeeGreaterThanZero() (gas: 782335) +DebtManagerTest:testFloatingToFixedRoll() (gas: 1166015) +DebtManagerTest:testFloatingToFixedRollHigherThanAvailableLiquidity() (gas: 1352799) +DebtManagerTest:testFloatingToFixedRollHigherThanAvailableLiquidityWithSlippage() (gas: 2129494) +DebtManagerTest:testFloatingToFixedRollHigherThanAvailableLiquidityWithSlippageWithThreePools() (gas: 2819298) +DebtManagerTest:testFloatingToFixedRollWithAccurateSlippage() (gas: 1733293) +DebtManagerTest:testFloatingToFixedRollWithAccurateSlippageWithPreviousPosition() (gas: 1955460) +DebtManagerTest:testFuzzRolls(uint8[4],uint8[4],uint256[4],uint40[4],uint8[4]) (runs: 256, μ: 8020238, ~: 8077953) +DebtManagerTest:testLateFixedRoll() (gas: 1455061) +DebtManagerTest:testLateFixedRollWithThreeLoops() (gas: 2044387) +DebtManagerTest:testLateFixedToFloatingRoll() (gas: 1449884) +DebtManagerTest:testLateFixedToFloatingRollWithThreeLoops() (gas: 2013167) +DebtManagerTest:testLeverage() (gas: 750228) +DebtManagerTest:testLeverageIncremental() (gas: 1666386) +DebtManagerTest:testLeverageShouldFailWhenHealthFactorNearOne() (gas: 1459136) +DebtManagerTest:testLeverageWithAlreadyDepositedAmount() (gas: 942209) +DebtManagerTest:testLeverageWithInvalidBalancerVault() (gas: 4051519) +DebtManagerTest:testLeverageWithMoreThanBalancerAvailableLiquidity() (gas: 1125881) +DebtManagerTest:testLeverageWithNegativePrincipal() (gas: 1339628) +DebtManagerTest:testLeverageWithNonCollateralMarket() (gas: 1291324) +DebtManagerTest:testLeverageWithPartialNegativePrincipal() (gas: 1508662) +DebtManagerTest:testMockBalancerVault() (gas: 6424098) +DebtManagerTest:testPartialDeleverageWithWithdrawAndNewRatio() (gas: 1157540) +DebtManagerTest:testPartialDeleverageWithWithdrawKeepingRatio() (gas: 1157545) +DebtManagerTest:testPartialFixedRoll() (gas: 1263955) +DebtManagerTest:testPartialFixedToFloatingRoll() (gas: 1220102) +DebtManagerTest:testPartialLateFixedRoll() (gas: 1480515) +DebtManagerTest:testPartialLateFixedToFloatingRoll() (gas: 1482181) +DebtManagerTest:testPermit2AndLeverage() (gas: 885875) +DebtManagerTest:testPermitAndDeleverage() (gas: 1232406) +DebtManagerTest:testPermitAndLeverage() (gas: 994488) +DebtManagerTest:testPermitAndRollFloatingToFixed() (gas: 1348953) +DebtManagerTest:testPermitAndTransferLeverage() (gas: 915082) +DebtManagerTest:testRollBorrowFromNonCollateralMarket() (gas: 3092309) +DebtPreviewerTest:testDeleverageRatesSameAsset() (gas: 1047392) +DebtPreviewerTest:testLeveragePreviewsWithNonCollateralMarket() (gas: 1710207) +DebtPreviewerTest:testLeverageRatesCrossAsset() (gas: 780641) +DebtPreviewerTest:testLeverageRatesSameAsset() (gas: 489812) +DebtPreviewerTest:testLeverageRatesWithNativeBorrow() (gas: 416141) +DebtPreviewerTest:testLeverageRatesWithNegativeNativeResult() (gas: 416203) +DebtPreviewerTest:testLeverageRatesZeroPrincipalCrossAsset() (gas: 712238) +DebtPreviewerTest:testLeverageRatesZeroPrincipalSameAsset() (gas: 419227) +DebtPreviewerTest:testLeverageWithNonCollateralMarket() (gas: 1380490) +DebtPreviewerTest:testMinDepositWithNonCollateralMarket() (gas: 1559127) +DebtPreviewerTest:testPreviewDeleverageSameAsset() (gas: 2724100) +DebtPreviewerTest:testPreviewDeleverageWithNonCollateralMarket() (gas: 1763093) +DebtPreviewerTest:testPreviewEmptyLeverage() (gas: 347859) +DebtPreviewerTest:testPreviewLeverage() (gas: 1005726) +DebtPreviewerTest:testPreviewLeverageBalancerAvailableLiquidity() (gas: 352647) +DebtPreviewerTest:testPreviewLeverageExcludesNonCollateralMarketBalances() (gas: 2573170) +DebtPreviewerTest:testPreviewLeverageSameAsset() (gas: 1413676) +DebtPreviewerTest:testPreviewLeverageSameAssetNegativePrincipal() (gas: 2389512) +DebtPreviewerTest:testPreviewLeverageSameAssetPartialNegativePrincipal() (gas: 2032618) +DebtPreviewerTest:testPreviewLeverageSameUSDCAssetMaxRatioMultipleCollateralAndDebt() (gas: 2871102) +DebtPreviewerTest:testPreviewLeverageSameUSDCAssetWithDeposit() (gas: 2807063) +DebtPreviewerTest:testPreviewLeverageSameWETHAssetMaxRatioMultipleCollateralAndDebt() (gas: 2942527) +DebtPreviewerTest:testPreviewLeverageSameWETHAssetMultipleCollateralAndDebtWithMinHealthFactor() (gas: 2942512) +DebtPreviewerTest:testPreviewMaxRatioWithdrawWithSameAssetLeverage() (gas: 1199168) +DebtPreviewerTest:testPreviewSameAssetInvalidLeverageShouldCapRatio() (gas: 1233152) +DebtPreviewerTest:testPreviewsUnderwaterAccountAfterNonCollateral() (gas: 3378193) +DebtPreviewerTest:testPreviewsUnderwaterAccountByPrice() (gas: 3908973) DebtRollerTest:test_receiveFlashLoan_reverts_whenUnauthorizedFlashLoaner() (gas: 39801) -DebtRollerTest:test_rollFixed_reverts_whenInvalidOperation() (gas: 51027) -DebtRollerTest:test_rollFixed_reverts_whenNotMarket() (gas: 49047) -DebtRollerTest:test_rollFixed_rolls() (gas: 1100779) -DebtRollerTest:test_rollFixed_rolls_partial() (gas: 1139976) +DebtRollerTest:test_rollFixed_reverts_whenInvalidOperation() (gas: 51126) +DebtRollerTest:test_rollFixed_reverts_whenNotMarket() (gas: 49146) +DebtRollerTest:test_rollFixed_rolls() (gas: 1101034) +DebtRollerTest:test_rollFixed_rolls_partial() (gas: 1140231) EXATest:test_burn_burnsAndEmitsCrosschainBurn() (gas: 149890) EXATest:test_burn_reverts_whenCallerLacksBridgeRole() (gas: 69255) EXATest:test_crosschainBurn_burnsAndEmitsCrosschainBurn() (gas: 149888) @@ -173,269 +199,280 @@ FlashLoanAdapterTest:test_setWToken_emitsWTokenSet() (gas: 80696) FlashLoanAdapterTest:test_setWToken_reverts_whenNotAdmin() (gas: 66649) FlashLoanAdapterTest:test_setWToken_sets_whenAdmin() (gas: 82048) InstallmentsRouterTest:testAmountsLength() (gas: 36806) -InstallmentsRouterTest:testBorrowETHToAnotherReceiver() (gas: 801220) -InstallmentsRouterTest:testBorrowETHWithPermit() (gas: 1239104) -InstallmentsRouterTest:testBorrowRouter() (gas: 691277) -InstallmentsRouterTest:testBorrowToAnotherReceiver() (gas: 715159) -InstallmentsRouterTest:testBorrowUnwrappedETH() (gas: 768423) -InstallmentsRouterTest:testBorrowWithPermit() (gas: 1375012) -InstallmentsRouterTest:testFakeMarket() (gas: 47946) -InstallmentsRouterTest:testInsufficientMaxRepay() (gas: 676442) -InstallmentsRouterTest:testMaxRepay() (gas: 676436) -InstallmentsRouterTest:testMissingMarketWETH() (gas: 818663) -InstallmentsRouterTest:testMoreBorrowsThanMaxPools() (gas: 680308) -IntegrationPreviewerTest:test_borrowLimit() (gas: 2066442) +InstallmentsRouterTest:testBorrowETHToAnotherReceiver() (gas: 801979) +InstallmentsRouterTest:testBorrowETHWithPermit() (gas: 1241516) +InstallmentsRouterTest:testBorrowRouter() (gas: 691607) +InstallmentsRouterTest:testBorrowToAnotherReceiver() (gas: 715489) +InstallmentsRouterTest:testBorrowUnwrappedETH() (gas: 769182) +InstallmentsRouterTest:testBorrowWithPermit() (gas: 1377853) +InstallmentsRouterTest:testFakeMarket() (gas: 48042) +InstallmentsRouterTest:testInsufficientMaxRepay() (gas: 676772) +InstallmentsRouterTest:testMaxRepay() (gas: 676766) +InstallmentsRouterTest:testMissingMarketWETH() (gas: 824820) +InstallmentsRouterTest:testMoreBorrowsThanMaxPools() (gas: 680638) +IntegrationPreviewerTest:test_borrowLimit() (gas: 2068366) IntegrationPreviewerTest:test_fixedRepayAssets_afterMaturity() (gas: 672580) -IntegrationPreviewerTest:test_fixedRepayAssets_beforeMaturity() (gas: 580057) +IntegrationPreviewerTest:test_fixedRepayAssets_beforeMaturity() (gas: 580012) IntegrationPreviewerTest:test_fixedRepayAssets_maxUintAfterMaturity() (gas: 657302) -IntegrationPreviewerTest:test_fixedRepayAssets_maxUintBeforeMaturity() (gas: 564802) +IntegrationPreviewerTest:test_fixedRepayAssets_maxUintBeforeMaturity() (gas: 564757) IntegrationPreviewerTest:test_fixedRepayPosition_afterMaturity() (gas: 673131) -IntegrationPreviewerTest:test_fixedRepayPosition_beforeMaturity() (gas: 579991) -IntegrationPreviewerTest:test_fixedRepayPosition_maxUint() (gas: 79468) +IntegrationPreviewerTest:test_fixedRepayPosition_beforeMaturity() (gas: 580013) +IntegrationPreviewerTest:test_fixedRepayPosition_maxUint() (gas: 79490) IntegrationPreviewerTest:test_fixedRepayPosition_saturatedFallback() (gas: 63069) -IntegrationPreviewerTest:test_healthFactor() (gas: 463879) -IntegrationPreviewerTest:test_previewDeposit_(uint256) (runs: 256, μ: 1590393, ~: 1594456) -IntegrationPreviewerTest:test_previewHealthFactor() (gas: 5805294) -IntegrationPreviewerTest:test_previewWithdraw_(uint256) (runs: 256, μ: 1649847, ~: 1654116) +IntegrationPreviewerTest:test_healthFactor() (gas: 464287) +IntegrationPreviewerTest:test_previewDeposit_(uint256) (runs: 256, μ: 1595749, ~: 1599768) +IntegrationPreviewerTest:test_previewHealthFactor() (gas: 5827669) +IntegrationPreviewerTest:test_previewHealthFactor_nonCollateralMarket() (gas: 3511265) +IntegrationPreviewerTest:test_previewWithdraw_(uint256) (runs: 256, μ: 1655952, ~: 1660185) InterestRateModelTest:testFixedBorrowRate() (gas: 2088868) InterestRateModelTest:testFixedRateRevertAlreadyMatured() (gas: 2083135) InterestRateModelTest:testFixedRateRevertUtilizationExceeded() (gas: 2097203) InterestRateModelTest:testFloatingBorrowRate() (gas: 2082456) -InterestRateModelTest:testFuzzFixedRateGrowth(uint256,uint256,uint256,uint256) (runs: 256, μ: 2109079, ~: 2105195) -InterestRateModelTest:testFuzzFixedRateTimeSensitivity(uint256,uint256,uint256) (runs: 256, μ: 2117002, ~: 2117161) -InterestRateModelTest:testFuzzReferenceLegacyRateFixed(uint32,uint256,uint256[2],uint256[2],uint256,uint256,uint256) (runs: 256, μ: 12909428, ~: 13130610) -InterestRateModelTest:testFuzzReferenceRateFixed(uint256,uint256,uint256,uint256,uint256,uint256,(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,int256,uint256,uint256)) (runs: 256, μ: 2384608, ~: 2386846) -InterestRateModelTest:testFuzzReferenceRateFloating(uint256,uint256,(uint256,uint256,uint256,uint256,uint256,uint256,uint256)) (runs: 256, μ: 2324354, ~: 2324970) +InterestRateModelTest:testFuzzFixedRateGrowth(uint256,uint256,uint256,uint256) (runs: 256, μ: 2108982, ~: 2105195) +InterestRateModelTest:testFuzzFixedRateTimeSensitivity(uint256,uint256,uint256) (runs: 256, μ: 2117005, ~: 2117161) +InterestRateModelTest:testFuzzReferenceLegacyRateFixed(uint32,uint256,uint256[2],uint256[2],uint256,uint256,uint256) (runs: 256, μ: 12924595, ~: 13131281) +InterestRateModelTest:testFuzzReferenceRateFixed(uint256,uint256,uint256,uint256,uint256,uint256,(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,int256,uint256,uint256)) (runs: 256, μ: 2384593, ~: 2386834) +InterestRateModelTest:testFuzzReferenceRateFloating(uint256,uint256,(uint256,uint256,uint256,uint256,uint256,uint256,uint256)) (runs: 256, μ: 2324290, ~: 2324936) InterestRateModelTest:testMinTimeToMaturity() (gas: 2107232) InterestRateModelTest:testRevertMaxUtilizationLowerThanWad() (gas: 268402) -MarketTest:testAccountLiquidityAdjustedDebt() (gas: 654260) -MarketTest:testAccountsFixedConsolidated() (gas: 2084688) -MarketTest:testAccountsFixedConsolidatedWhenClearBadDebt() (gas: 2147165) -MarketTest:testAccountsFixedConsolidatedWhenSenderNotOwner() (gas: 973309) -MarketTest:testAccountsFixedConsolidatedWithPartialRepayAndWithdraw() (gas: 1478554) -MarketTest:testAccrueEarningsBeforeLiquidation() (gas: 1572539) -MarketTest:testAnotherUserRedeemWhenOwnerHasShortfall() (gas: 908863) -MarketTest:testAnotherUserWithdrawWhenOwnerHasShortfall() (gas: 855063) -MarketTest:testBorrowAfterFreezing() (gas: 493230) -MarketTest:testBorrowAtMaturity() (gas: 545799) -MarketTest:testBorrowAtMaturityAfterFreezing() (gas: 619322) -MarketTest:testBorrowAtMaturityUpdatesFloatingDebtAndFloatingAssets() (gas: 888535) -MarketTest:testBorrowAtMaturityWhenFrozen() (gas: 93430) -MarketTest:testBorrowAtMaturityWithZeroAssets() (gas: 40235) -MarketTest:testBorrowDisagreement() (gas: 297292) -MarketTest:testBorrowFromFreeLunchShouldNotRevertWithFloatingFullUtilization() (gas: 1387293) +MarketTest:testAccountLiquidityAdjustedDebt() (gas: 654879) +MarketTest:testAccountsFixedConsolidated() (gas: 2084965) +MarketTest:testAccountsFixedConsolidatedWhenClearBadDebt() (gas: 2149955) +MarketTest:testAccountsFixedConsolidatedWhenSenderNotOwner() (gas: 973409) +MarketTest:testAccountsFixedConsolidatedWithPartialRepayAndWithdraw() (gas: 1478798) +MarketTest:testAccrueEarningsBeforeLiquidation() (gas: 1572989) +MarketTest:testAnotherUserRedeemWhenOwnerHasShortfall() (gas: 909721) +MarketTest:testAnotherUserWithdrawWhenOwnerHasShortfall() (gas: 855875) +MarketTest:testBorrowAfterFreezing() (gas: 493451) +MarketTest:testBorrowAtMaturity() (gas: 545980) +MarketTest:testBorrowAtMaturityAfterFreezing() (gas: 619521) +MarketTest:testBorrowAtMaturityUpdatesFloatingDebtAndFloatingAssets() (gas: 890837) +MarketTest:testBorrowAtMaturityWhenFrozen() (gas: 93407) +MarketTest:testBorrowAtMaturityWithZeroAssets() (gas: 40300) +MarketTest:testBorrowDisagreement() (gas: 297315) +MarketTest:testBorrowFromFreeLunchShouldNotRevertWithFloatingFullUtilization() (gas: 1389839) MarketTest:testBorrowWhenFrozen() (gas: 92933) -MarketTest:testBorrowWithZeroAssets() (gas: 39676) -MarketTest:testCappedLiquidation() (gas: 1354473) -MarketTest:testChargeTreasuryToEarlyWithdraws() (gas: 1453688) -MarketTest:testChargeTreasuryToFixedBorrows() (gas: 1813994) -MarketTest:testClearBadDebtAvoidingFixedBorrowsIfAccumulatorLower() (gas: 2456419) -MarketTest:testClearBadDebtBeforeMaturity() (gas: 2497629) -MarketTest:testClearBadDebtCalledByAccount() (gas: 34965) -MarketTest:testClearBadDebtEmptiesUnassignedEarnings() (gas: 2808291) -MarketTest:testClearBadDebtExactlyRepaysFixedBorrowWithAccumulatorAmount() (gas: 2197334) -MarketTest:testClearBadDebtPartiallyRepaysEachFixedBorrow() (gas: 2150073) -MarketTest:testClearBadDebtPartiallyRepaysFloatingDebt() (gas: 2523178) -MarketTest:testClearBadDebtShouldAccrueAccumulatedEarningsBeforeSpreadingLosses() (gas: 2335598) -MarketTest:testClearBadDebtWithEmptyAccumulatorShouldNotRevert() (gas: 1128899) -MarketTest:testClearMaturity() (gas: 1853424) -MarketTest:testCollectTreasuryFreeLunchToEarlyWithdraws() (gas: 2081019) -MarketTest:testCollectTreasuryFreeLunchToEarlyWithdrawsWithZeroFees() (gas: 649933) -MarketTest:testCollectTreasuryFreeLunchToFixedBorrows() (gas: 2087449) -MarketTest:testCollectTreasuryFreeLunchToFixedBorrowsWithZeroFees() (gas: 864257) -MarketTest:testCrossMaturityLiquidation() (gas: 2877660) -MarketTest:testDepositAfterFreezing() (gas: 258021) -MarketTest:testDepositAtMaturity() (gas: 237982) -MarketTest:testDepositAtMaturityAfterFreezing() (gas: 311834) +MarketTest:testBorrowWithZeroAssets() (gas: 39635) +MarketTest:testCappedLiquidation() (gas: 1355635) +MarketTest:testChargeTreasuryToEarlyWithdraws() (gas: 1453932) +MarketTest:testChargeTreasuryToFixedBorrows() (gas: 1814699) +MarketTest:testClearBadDebtAvoidingFixedBorrowsIfAccumulatorLower() (gas: 2459529) +MarketTest:testClearBadDebtBeforeMaturity() (gas: 2498870) +MarketTest:testClearBadDebtCalledByAccount() (gas: 34943) +MarketTest:testClearBadDebtEmptiesUnassignedEarnings() (gas: 2811450) +MarketTest:testClearBadDebtExactlyRepaysFixedBorrowWithAccumulatorAmount() (gas: 2200223) +MarketTest:testClearBadDebtPartiallyRepaysEachFixedBorrow() (gas: 2152984) +MarketTest:testClearBadDebtPartiallyRepaysFloatingDebt() (gas: 2526310) +MarketTest:testClearBadDebtShouldAccrueAccumulatedEarningsBeforeSpreadingLosses() (gas: 2338487) +MarketTest:testClearBadDebtWithEmptyAccumulatorShouldNotRevert() (gas: 1131656) +MarketTest:testClearMaturity() (gas: 1854109) +MarketTest:testCollectTreasuryFreeLunchToEarlyWithdraws() (gas: 2081483) +MarketTest:testCollectTreasuryFreeLunchToEarlyWithdrawsWithZeroFees() (gas: 649978) +MarketTest:testCollectTreasuryFreeLunchToFixedBorrows() (gas: 2088112) +MarketTest:testCollectTreasuryFreeLunchToFixedBorrowsWithZeroFees() (gas: 864436) +MarketTest:testCrossMaturityLiquidation() (gas: 2879573) +MarketTest:testDepositAfterFreezing() (gas: 258043) +MarketTest:testDepositAtMaturity() (gas: 238004) +MarketTest:testDepositAtMaturityAfterFreezing() (gas: 311856) MarketTest:testDepositAtMaturityWhenFrozen() (gas: 92917) MarketTest:testDepositAtMaturityWithZeroAssets() (gas: 39804) -MarketTest:testDepositDisagreement() (gas: 73930) -MarketTest:testDepositShouldUpdateFlexibleBorrowVariables() (gas: 799178) +MarketTest:testDepositBorrowRepayWithdrawOnNonCollateralMarket() (gas: 2397007) +MarketTest:testDepositDisagreement() (gas: 73908) +MarketTest:testDepositShouldUpdateFlexibleBorrowVariables() (gas: 799421) MarketTest:testDepositToSmartPool() (gas: 183805) -MarketTest:testDepositWhenFrozen() (gas: 176182) -MarketTest:testDistributeMultipleAccumulatedEarnings() (gas: 1736794) -MarketTest:testDistributionOfLossesShouldReduceFromFloatingBackupBorrowedAccordingly() (gas: 6804742) -MarketTest:testEarlyRepayLiquidationUnassignedEarnings() (gas: 2587913) -MarketTest:testEarlyRepaymentWithExcessiveAmountOfFees() (gas: 3549215) -MarketTest:testEarlyWithdrawFromFreeLunchShouldNotRevertWithFloatingFullUtilization() (gas: 1092157) -MarketTest:testEmergencyAdminRole() (gas: 373284) -MarketTest:testEmitFrozen() (gas: 90654) -MarketTest:testFixedBorrowFailingWhenFlexibleBorrowAccruesDebt() (gas: 1585308) -MarketTest:testFixedBorrowRateToMaturity() (gas: 608879) -MarketTest:testFixedOperationsUpdateFloatingDebt() (gas: 994095) -MarketTest:testFlexibleBorrow() (gas: 460901) -MarketTest:testFlexibleBorrowAccountingDebt() (gas: 754994) -MarketTest:testFlexibleBorrowChargingDebtToTreasury() (gas: 847810) -MarketTest:testFlexibleBorrowExceedingReserve() (gas: 882330) -MarketTest:testFlexibleBorrowExceedingReserveIncludingFixedBorrow() (gas: 1315731) -MarketTest:testFlexibleBorrowExceedingReserveWithNewDebt() (gas: 1005217) -MarketTest:testFlexibleBorrowFromAnotherUserSubtractsAllowance() (gas: 480119) -MarketTest:testFlexibleBorrowFromAnotherUserWithAllowance() (gas: 460698) +MarketTest:testDepositWhenFrozen() (gas: 176226) +MarketTest:testDistributeMultipleAccumulatedEarnings() (gas: 1737015) +MarketTest:testDistributionOfLossesShouldReduceFromFloatingBackupBorrowedAccordingly() (gas: 6807920) +MarketTest:testEarlyRepayLiquidationUnassignedEarnings() (gas: 2588661) +MarketTest:testEarlyRepaymentWithExcessiveAmountOfFees() (gas: 3551695) +MarketTest:testEarlyWithdrawFromFreeLunchShouldNotRevertWithFloatingFullUtilization() (gas: 1094438) +MarketTest:testEmergencyAdminRole() (gas: 373262) +MarketTest:testEmitFrozen() (gas: 90631) +MarketTest:testExitNonCollateralMarketWithBadHealthFactor() (gas: 1268325) +MarketTest:testFixedBorrowFailingWhenFlexibleBorrowAccruesDebt() (gas: 1588228) +MarketTest:testFixedBorrowRateToMaturity() (gas: 611205) +MarketTest:testFixedOperationsUpdateFloatingDebt() (gas: 994294) +MarketTest:testFlexibleBorrow() (gas: 461144) +MarketTest:testFlexibleBorrowAccountingDebt() (gas: 755259) +MarketTest:testFlexibleBorrowChargingDebtToTreasury() (gas: 848297) +MarketTest:testFlexibleBorrowExceedingReserve() (gas: 882528) +MarketTest:testFlexibleBorrowExceedingReserveIncludingFixedBorrow() (gas: 1316151) +MarketTest:testFlexibleBorrowExceedingReserveWithNewDebt() (gas: 1005682) +MarketTest:testFlexibleBorrowFromAnotherUserSubtractsAllowance() (gas: 480220) +MarketTest:testFlexibleBorrowFromAnotherUserWithAllowance() (gas: 460754) MarketTest:testFlexibleBorrowFromAnotherUserWithoutAllowance() (gas: 254380) -MarketTest:testFrontRunSmartPoolEarningsDistributionWithBigPenaltyRepayment() (gas: 1681141) -MarketTest:testFullPause() (gas: 6091490) -MarketTest:testInitiallyUnfrozen() (gas: 15498) -MarketTest:testInsufficientProtocolLiquidity() (gas: 1990516) -MarketTest:testLiquidateAndChargeIncentiveForLenders() (gas: 2554368) -MarketTest:testLiquidateAndDistributeLosses() (gas: 3522051) -MarketTest:testLiquidateAndSeizeExactAmountWithDustAsCollateral() (gas: 3077301) -MarketTest:testLiquidateAndSeizeFromEmptyCollateral() (gas: 1091860) -MarketTest:testLiquidateAndSubtractLossesFromAccumulator() (gas: 4260300) -MarketTest:testLiquidateFlexibleAndFixedBorrowPositionsInSingleCall() (gas: 2792642) -MarketTest:testLiquidateFlexibleBorrow() (gas: 2771697) -MarketTest:testLiquidateFlexibleBorrowChargeLendersAssetsToLiquidator() (gas: 1201555) -MarketTest:testLiquidateFlexibleBorrowConsideringDebtOverTime() (gas: 1277306) -MarketTest:testLiquidateLeavingDustAsCollateral() (gas: 4000989) -MarketTest:testLiquidateTransferRepayAssetsBeforeSeize() (gas: 1222151) -MarketTest:testLiquidateUpdateFloatingDebt() (gas: 1981044) -MarketTest:testLiquidateWhenFrozen() (gas: 1236824) -MarketTest:testLiquidateWithTwoUnitsAsMaxAssets() (gas: 1809967) -MarketTest:testLiquidateWithZeroAsMaxAssets() (gas: 1091788) -MarketTest:testLiquidationClearingDebtOfAllAccountMarkets() (gas: 3419654) -MarketTest:testLiquidationResultingInZeroCollateralAndZeroDebt() (gas: 2193789) -MarketTest:testMaturityInsufficientProtocolLiquidity() (gas: 1569373) -MarketTest:testMultipleBorrowsForMultipleAssets() (gas: 2483945638) -MarketTest:testMultipleDepositsToSmartPool() (gas: 930093) -MarketTest:testMultipleFixedBorrowsRepays() (gas: 1468900) -MarketTest:testMultipleLiquidationSameUser() (gas: 3597785) -MarketTest:testNotEnteredMarketShouldNotBeSeized() (gas: 11369955) -MarketTest:testOnlyAdminCanFreezeUnfreeze() (gas: 228264) -MarketTest:testOperationsShouldUpdateFloatingAssetsAverage() (gas: 1648939) -MarketTest:testOperationsWithBtcWbtcRate() (gas: 11073100) -MarketTest:testOperationsWithStEthAsset() (gas: 10962439) -MarketTest:testPausable() (gas: 183084) -MarketTest:testPauserRole() (gas: 88344) -MarketTest:testPreviewOperationsWithSmartPoolCorrectlyAccountingEarnings() (gas: 2264812) -MarketTest:testRepayAtMaturity() (gas: 638954) -MarketTest:testRepayDisagreement() (gas: 640370) -MarketTest:testRepayFlexibleBorrow() (gas: 1251041) -MarketTest:testRepayWhenFrozen() (gas: 537207) -MarketTest:testRoundingDownAssetsToValidateShortfallWhenTransferring() (gas: 9584811) -MarketTest:testRoundingDownAssetsToValidateShortfallWhenTransferringFrom() (gas: 9640246) -MarketTest:testRoundingDownAssetsWhenTransferingFromAnAccountWithoutShortfall() (gas: 1095887) -MarketTest:testRoundingDownAssetsWhenTransferingWithAnAccountWithoutShortfall() (gas: 1037930) -MarketTest:testRoundingUpAllowanceWhenBorrowingAtMaturity() (gas: 902010) -MarketTest:testRoundingUpAllowanceWhenWithdrawingAtMaturity() (gas: 885030) -MarketTest:testSetDampSpeedFactorShouldUpdateFloatingAssetsAverage() (gas: 410811) -MarketTest:testSetEarningsAccumulatorSmoothFactorShouldDistributeEarnings() (gas: 905579) -MarketTest:testSetInterestRateModelShouldUpdateFloatingDebt() (gas: 2853596) -MarketTest:testSetInterestRateModelWithAddressZeroShouldNotUpdateFloatingDebt() (gas: 2642607) -MarketTest:testShareValueNotDecreasingAfterDeposit() (gas: 796909) -MarketTest:testShareValueNotDecreasingWhenMintingToTreasury() (gas: 846167) -MarketTest:testSingleFloatingBorrow() (gas: 419400) -MarketTest:testSingleFloatingRepay() (gas: 486605) -MarketTest:testSmartPoolEarningsDistribution() (gas: 1234938) -MarketTest:testSmartPoolSharesDoNotAccountUnassignedEarningsFromMoreThanOneIntervalPastMaturities() (gas: 680272) -MarketTest:testSumDebtPlusEffectsShouldntRoundUpWhenWithdrawing() (gas: 1068743) -MarketTest:testTotalAssetsProjectingBackupEarningsCorrectly() (gas: 594116) -MarketTest:testTotalAssetsProjectingFloatingDebtCorrectly() (gas: 844209) -MarketTest:testUpdateAccumulatedEarningsFactorToZero() (gas: 1789242) -MarketTest:testUpdateFloatingAssetsAverageWhenDepositingAndBorrowingContinuously() (gas: 421757) -MarketTest:testUpdateFloatingAssetsAverageWhenDepositingRightBeforeBorrow() (gas: 741110) -MarketTest:testUpdateFloatingAssetsAverageWhenDepositingRightBeforeEarlyWithdraw() (gas: 695051) -MarketTest:testUpdateFloatingAssetsAverageWhenDepositingSomeSecondsBeforeBorrow() (gas: 1059214) -MarketTest:testUpdateFloatingAssetsAverageWhenWithdrawingRightBeforeBorrow() (gas: 757932) -MarketTest:testUpdateFloatingAssetsAverageWhenWithdrawingRightBeforeEarlyWithdraw() (gas: 711895) -MarketTest:testUpdateFloatingAssetsAverageWhenWithdrawingSomeSecondsBeforeBorrow() (gas: 539236) -MarketTest:testUpdateFloatingAssetsAverageWithDampSpeedDown() (gas: 444219) +MarketTest:testFrontRunSmartPoolEarningsDistributionWithBigPenaltyRepayment() (gas: 1681384) +MarketTest:testFullPause() (gas: 6097790) +MarketTest:testHandleBadDebtWithNonCollateralMarketSupply() (gas: 2431431) +MarketTest:testInitiallyUnfrozen() (gas: 15521) +MarketTest:testInsufficientProtocolLiquidity() (gas: 1992844) +MarketTest:testLiquidateAccountWithOnlyNonCollateralSupply() (gas: 1304992) +MarketTest:testLiquidateAndChargeIncentiveForLenders() (gas: 2555907) +MarketTest:testLiquidateAndDistributeLosses() (gas: 3523668) +MarketTest:testLiquidateAndSeizeExactAmountWithDustAsCollateral() (gas: 3081108) +MarketTest:testLiquidateAndSeizeFromEmptyCollateral() (gas: 1092372) +MarketTest:testLiquidateAndSubtractLossesFromAccumulator() (gas: 4261995) +MarketTest:testLiquidateFlexibleAndFixedBorrowPositionsInSingleCall() (gas: 2795333) +MarketTest:testLiquidateFlexibleBorrow() (gas: 2773965) +MarketTest:testLiquidateFlexibleBorrowChargeLendersAssetsToLiquidator() (gas: 1202430) +MarketTest:testLiquidateFlexibleBorrowConsideringDebtOverTime() (gas: 1278181) +MarketTest:testLiquidateLeavingDustAsCollateral() (gas: 4005625) +MarketTest:testLiquidateTransferRepayAssetsBeforeSeize() (gas: 1222569) +MarketTest:testLiquidateUpdateFloatingDebt() (gas: 1982383) +MarketTest:testLiquidateWhenFrozen() (gas: 1239826) +MarketTest:testLiquidateWithTwoUnitsAsMaxAssets() (gas: 1811415) +MarketTest:testLiquidateWithZeroAsMaxAssets() (gas: 1092299) +MarketTest:testLiquidationClearingDebtOfAllAccountMarkets() (gas: 3421116) +MarketTest:testLiquidationResultingInZeroCollateralAndZeroDebt() (gas: 2195073) +MarketTest:testMaturityInsufficientProtocolLiquidity() (gas: 1569571) +MarketTest:testMultipleBorrowsForMultipleAssets() (gas: 2484524691) +MarketTest:testMultipleDepositsToSmartPool() (gas: 930115) +MarketTest:testMultipleFixedBorrowsRepays() (gas: 1469552) +MarketTest:testMultipleLiquidationSameUser() (gas: 3601082) +MarketTest:testNonCollateralMarketAddsNoBorrowingPower() (gas: 1089164) +MarketTest:testNonCollateralMarketShouldNotBeSeized() (gas: 1958203) +MarketTest:testNotEnteredMarketShouldNotBeSeized() (gas: 11373798) +MarketTest:testOnlyAdminCanFreezeUnfreeze() (gas: 228220) +MarketTest:testOperationsShouldUpdateFloatingAssetsAverage() (gas: 1649304) +MarketTest:testOperationsWithBtcWbtcRate() (gas: 11074573) +MarketTest:testOperationsWithStEthAsset() (gas: 10963891) +MarketTest:testPausable() (gas: 183061) +MarketTest:testPauserRole() (gas: 88322) +MarketTest:testPreviewOperationsWithSmartPoolCorrectlyAccountingEarnings() (gas: 2266339) +MarketTest:testRepayAtMaturity() (gas: 639197) +MarketTest:testRepayDisagreement() (gas: 640591) +MarketTest:testRepayFlexibleBorrow() (gas: 1251340) +MarketTest:testRepayWhenFrozen() (gas: 537406) +MarketTest:testRoundingDownAssetsToValidateShortfallWhenTransferring() (gas: 9586241) +MarketTest:testRoundingDownAssetsToValidateShortfallWhenTransferringFrom() (gas: 9641676) +MarketTest:testRoundingDownAssetsWhenTransferingFromAnAccountWithoutShortfall() (gas: 1098639) +MarketTest:testRoundingDownAssetsWhenTransferingWithAnAccountWithoutShortfall() (gas: 1040748) +MarketTest:testRoundingUpAllowanceWhenBorrowingAtMaturity() (gas: 902440) +MarketTest:testRoundingUpAllowanceWhenWithdrawingAtMaturity() (gas: 885273) +MarketTest:testSetDampSpeedFactorShouldUpdateFloatingAssetsAverage() (gas: 410834) +MarketTest:testSetEarningsAccumulatorSmoothFactorShouldDistributeEarnings() (gas: 905778) +MarketTest:testSetInterestRateModelShouldUpdateFloatingDebt() (gas: 2853817) +MarketTest:testSetInterestRateModelWithAddressZeroShouldNotUpdateFloatingDebt() (gas: 2642806) +MarketTest:testShareValueNotDecreasingAfterDeposit() (gas: 797130) +MarketTest:testShareValueNotDecreasingWhenMintingToTreasury() (gas: 846609) +MarketTest:testSingleFloatingBorrow() (gas: 419644) +MarketTest:testSingleFloatingRepay() (gas: 486804) +MarketTest:testSmartPoolEarningsDistribution() (gas: 1234994) +MarketTest:testSmartPoolSharesDoNotAccountUnassignedEarningsFromMoreThanOneIntervalPastMaturities() (gas: 680493) +MarketTest:testSumDebtPlusEffectsShouldntRoundUpWhenWithdrawing() (gas: 1071539) +MarketTest:testTotalAssetsProjectingBackupEarningsCorrectly() (gas: 594359) +MarketTest:testTotalAssetsProjectingFloatingDebtCorrectly() (gas: 844408) +MarketTest:testTransferNonCollateralMarketWithBadHealthFactor() (gas: 1702883) +MarketTest:testUpdateAccumulatedEarningsFactorToZero() (gas: 1789441) +MarketTest:testUpdateFloatingAssetsAverageWhenDepositingAndBorrowingContinuously() (gas: 421779) +MarketTest:testUpdateFloatingAssetsAverageWhenDepositingRightBeforeBorrow() (gas: 741374) +MarketTest:testUpdateFloatingAssetsAverageWhenDepositingRightBeforeEarlyWithdraw() (gas: 695073) +MarketTest:testUpdateFloatingAssetsAverageWhenDepositingSomeSecondsBeforeBorrow() (gas: 1059613) +MarketTest:testUpdateFloatingAssetsAverageWhenWithdrawingRightBeforeBorrow() (gas: 758098) +MarketTest:testUpdateFloatingAssetsAverageWhenWithdrawingRightBeforeEarlyWithdraw() (gas: 711796) +MarketTest:testUpdateFloatingAssetsAverageWhenWithdrawingSomeSecondsBeforeBorrow() (gas: 539159) +MarketTest:testUpdateFloatingAssetsAverageWithDampSpeedDown() (gas: 444142) MarketTest:testUpdateFloatingAssetsAverageWithDampSpeedUp() (gas: 318341) MarketTest:testUpdateFloatingDebtBeforeSettingTreasury() (gas: 115219) -MarketTest:testWithdrawAtMaturity() (gas: 342177) -MarketTest:testWithdrawFromSmartPool() (gas: 289943) -MarketTest:testWithdrawShouldUpdateFlexibleBorrowVariables() (gas: 941790) -MarketTest:testWithdrawWhenFrozen() (gas: 327699) -MarketTest:test_deposit_allowsDepositUpToMaxSupply() (gas: 349979) -MarketTest:test_deposit_deposits_whenTotalSupplyLowerThanMaxSupply() (gas: 224695) -MarketTest:test_deposit_reverts_whenTotalSupplyHigherThanMaxSupply() (gas: 160772) -MarketTest:test_setMaxSupply_emits_maxSupplySet() (gas: 44325) -MarketTest:test_setMaxSupply_reverts_whenNotAdmin() (gas: 65179) +MarketTest:testWithdrawAtMaturity() (gas: 342199) +MarketTest:testWithdrawFromSmartPool() (gas: 289888) +MarketTest:testWithdrawNonCollateralMarketWithBadHealthFactor() (gas: 1647375) +MarketTest:testWithdrawShouldUpdateFlexibleBorrowVariables() (gas: 943217) +MarketTest:testWithdrawWhenFrozen() (gas: 327666) +MarketTest:test_deposit_allowsDepositUpToMaxSupply() (gas: 349935) +MarketTest:test_deposit_deposits_whenTotalSupplyLowerThanMaxSupply() (gas: 224739) +MarketTest:test_deposit_reverts_whenTotalSupplyHigherThanMaxSupply() (gas: 160794) +MarketTest:test_setMaxSupply_emits_maxSupplySet() (gas: 44368) +MarketTest:test_setMaxSupply_reverts_whenNotAdmin() (gas: 65157) MarketTest:test_setMaxSupply_sets_whenAdmin() (gas: 66356) PauserTest:testPauseProtocolFromRando() (gas: 32819) -PauserTest:testPauseProtocolWhenMarketsArePaused() (gas: 186975) -PauserTest:testPauseProtocolWhenMarketsAreUnpaused() (gas: 147173) -PauserTest:testPauseProtocolWhenOneMarketIsPaused() (gas: 177425) -PauserTest:testPauseProtocolWithExtra() (gas: 290829) +PauserTest:testPauseProtocolWhenMarketsArePaused() (gas: 186997) +PauserTest:testPauseProtocolWhenMarketsAreUnpaused() (gas: 147195) +PauserTest:testPauseProtocolWhenOneMarketIsPaused() (gas: 177447) +PauserTest:testPauseProtocolWithExtra() (gas: 290851) PauserTest:testPauseTargets() (gas: 490850) PauserTest:testPauseTargetsAlreadyPaused() (gas: 508799) PoolLibTest:testAtomicDepositBorrowRepayWithdraw() (gas: 45922) PoolLibTest:testBackupBorrow() (gas: 33624) PoolLibTest:testEarningsAccrual() (gas: 38952) PoolLibTest:testEarningsDistribution() (gas: 32679) -PoolLibTest:testFuzzAddRemoveAll(uint8[12]) (runs: 256, μ: 104449, ~: 105534) +PoolLibTest:testFuzzAddRemoveAll(uint8[12]) (runs: 256, μ: 104450, ~: 105534) PoolLibTest:testMaturityRangeLimit() (gas: 7866) PoolLibTest:testMaturityRangeTooWide() (gas: 11656) -PreviewerTest:testAccountsReturningAccurateAmounts() (gas: 1686464) -PreviewerTest:testAccountsReturningUtilizationForDifferentMaturities() (gas: 5278442) -PreviewerTest:testAccountsWithAccountOnlyDeposit() (gas: 1045023) -PreviewerTest:testAccountsWithAccountThatHasBalances() (gas: 2755895) -PreviewerTest:testAccountsWithEmptyAccount() (gas: 713126) -PreviewerTest:testAccountsWithIntermediateOperationsReturningAccurateAmounts() (gas: 22748786) -PreviewerTest:testActualTimeBeforeStartDistributionRewards() (gas: 8693116) -PreviewerTest:testEmptyExactly() (gas: 6258134) -PreviewerTest:testExactlyReturningInterestRateModelData() (gas: 737924) -PreviewerTest:testFixedAvailableLiquidityProjectingNewFloatingDebt() (gas: 16816276) -PreviewerTest:testFixedPoolsA() (gas: 24432513) -PreviewerTest:testFixedPoolsChangingMaturityInTime() (gas: 2001786) -PreviewerTest:testFixedPoolsRatesAndUtilizations() (gas: 18751611) -PreviewerTest:testFixedPoolsWithFloatingAssetsAverage() (gas: 22411286) -PreviewerTest:testFlexibleAvailableLiquidity() (gas: 21939566) -PreviewerTest:testFlexibleBorrowSharesAndAssets() (gas: 5159099) -PreviewerTest:testFloatingAvailableLiquidityProjectingNewFloatingDebt() (gas: 15996170) -PreviewerTest:testFloatingRateAndUtilization() (gas: 1236120) -PreviewerTest:testJustUpdatedRewardRatesShouldStillReturnRate() (gas: 7784217) -PreviewerTest:testMaxBorrowAssetsCapacity() (gas: 2892849) -PreviewerTest:testMaxBorrowAssetsCapacityForAccountWithShortfall() (gas: 14262617) -PreviewerTest:testMaxBorrowAssetsCapacityPerMarket() (gas: 16616352) -PreviewerTest:testOraclePriceReturningAccurateValues() (gas: 13040201) -PreviewerTest:testPreviewBorrowAtAllMaturitiesReturningAccurateAmount() (gas: 4752204) -PreviewerTest:testPreviewBorrowAtMaturityReturningAccurateAmount() (gas: 704974) -PreviewerTest:testPreviewBorrowAtMaturityReturningAccurateAmountWithIntermediateOperations() (gas: 2249991) +PreviewerTest:testAccountsReturningAccurateAmounts() (gas: 1687100) +PreviewerTest:testAccountsReturningUtilizationForDifferentMaturities() (gas: 5280079) +PreviewerTest:testAccountsWithAccountOnlyDeposit() (gas: 1045246) +PreviewerTest:testAccountsWithAccountThatHasBalances() (gas: 2756466) +PreviewerTest:testAccountsWithEmptyAccount() (gas: 713350) +PreviewerTest:testAccountsWithIntermediateOperationsReturningAccurateAmounts() (gas: 22756265) +PreviewerTest:testActualTimeBeforeStartDistributionRewards() (gas: 8694362) +PreviewerTest:testEmptyExactly() (gas: 6258445) +PreviewerTest:testExactlyIsCollateralFalseWhenBorrowingFromNonCollateralMarket() (gas: 14007188) +PreviewerTest:testExactlyIsCollateralWithNonCollateralMarket() (gas: 2419418) +PreviewerTest:testExactlyReturningInterestRateModelData() (gas: 738148) +PreviewerTest:testFixedAvailableLiquidityProjectingNewFloatingDebt() (gas: 16821208) +PreviewerTest:testFixedPoolsA() (gas: 24440631) +PreviewerTest:testFixedPoolsChangingMaturityInTime() (gas: 2002480) +PreviewerTest:testFixedPoolsRatesAndUtilizations() (gas: 18757121) +PreviewerTest:testFixedPoolsWithFloatingAssetsAverage() (gas: 22417771) +PreviewerTest:testFlexibleAvailableLiquidity() (gas: 21946669) +PreviewerTest:testFlexibleBorrowSharesAndAssets() (gas: 5161050) +PreviewerTest:testFloatingAvailableLiquidityProjectingNewFloatingDebt() (gas: 16000902) +PreviewerTest:testFloatingRateAndUtilization() (gas: 1238694) +PreviewerTest:testJustUpdatedRewardRatesShouldStillReturnRate() (gas: 7784946) +PreviewerTest:testMaxBorrowAssetsCapacity() (gas: 2896116) +PreviewerTest:testMaxBorrowAssetsCapacityForAccountWithShortfall() (gas: 14268657) +PreviewerTest:testMaxBorrowAssetsCapacityPerMarket() (gas: 16623364) +PreviewerTest:testOraclePriceReturningAccurateValues() (gas: 13041275) +PreviewerTest:testPreviewBorrowAtAllMaturitiesReturningAccurateAmount() (gas: 4752672) +PreviewerTest:testPreviewBorrowAtMaturityReturningAccurateAmount() (gas: 705008) +PreviewerTest:testPreviewBorrowAtMaturityReturningAccurateAmountWithIntermediateOperations() (gas: 2250225) PreviewerTest:testPreviewBorrowAtMaturityReturningAccurateUtilization() (gas: 717012) PreviewerTest:testPreviewBorrowAtMaturityWithFiveUnits() (gas: 456632) -PreviewerTest:testPreviewBorrowAtMaturityWithInvalidMaturity() (gas: 262266) +PreviewerTest:testPreviewBorrowAtMaturityWithInvalidMaturity() (gas: 262265) PreviewerTest:testPreviewBorrowAtMaturityWithMaturedMaturity() (gas: 49802) PreviewerTest:testPreviewBorrowAtMaturityWithOneUnit() (gas: 456654) PreviewerTest:testPreviewBorrowAtMaturityWithSameTimestamp() (gas: 49742) -PreviewerTest:testPreviewBorrowAtMaturityWithZeroAmount() (gas: 251520) -PreviewerTest:testPreviewDepositAtAllMaturitiesReturningAccurateAmounts() (gas: 2805604) -PreviewerTest:testPreviewDepositAtMaturityReturningAccurateAmount() (gas: 925011) -PreviewerTest:testPreviewDepositAtMaturityReturningAccurateAmountWithIntermediateOperations() (gas: 1745623) +PreviewerTest:testPreviewBorrowAtMaturityWithZeroAmount() (gas: 251497) +PreviewerTest:testPreviewDepositAtAllMaturitiesReturningAccurateAmounts() (gas: 2805838) +PreviewerTest:testPreviewDepositAtMaturityReturningAccurateAmount() (gas: 925045) +PreviewerTest:testPreviewDepositAtMaturityReturningAccurateAmountWithIntermediateOperations() (gas: 1745801) PreviewerTest:testPreviewDepositAtMaturityWithEmptyMaturity() (gas: 48556) -PreviewerTest:testPreviewDepositAtMaturityWithEmptyMaturityAndZeroAmount() (gas: 48592) +PreviewerTest:testPreviewDepositAtMaturityWithEmptyMaturityAndZeroAmount() (gas: 48547) PreviewerTest:testPreviewDepositAtMaturityWithInvalidMaturity() (gas: 48570) -PreviewerTest:testPreviewDepositAtMaturityWithMaturedMaturity() (gas: 12418) -PreviewerTest:testPreviewDepositAtMaturityWithOneUnit() (gas: 660629) -PreviewerTest:testPreviewDepositAtMaturityWithSameTimestamp() (gas: 51101) -PreviewerTest:testPreviewDepositAtMaturityWithZeroAmount() (gas: 660654) -PreviewerTest:testPreviewRepayAtMaturityLastAccrualIsMaturity() (gas: 1556909) -PreviewerTest:testPreviewRepayAtMaturityReturningAccurateAmount() (gas: 1379627) -PreviewerTest:testPreviewRepayAtMaturityReturningAccurateAmountWithIntermediateOperations() (gas: 1833020) +PreviewerTest:testPreviewDepositAtMaturityWithMaturedMaturity() (gas: 12396) +PreviewerTest:testPreviewDepositAtMaturityWithOneUnit() (gas: 660729) +PreviewerTest:testPreviewDepositAtMaturityWithSameTimestamp() (gas: 51123) +PreviewerTest:testPreviewDepositAtMaturityWithZeroAmount() (gas: 660710) +PreviewerTest:testPreviewRepayAtMaturityLastAccrualIsMaturity() (gas: 1557087) +PreviewerTest:testPreviewRepayAtMaturityReturningAccurateAmount() (gas: 1379827) +PreviewerTest:testPreviewRepayAtMaturityReturningAccurateAmountWithIntermediateOperations() (gas: 1833153) PreviewerTest:testPreviewRepayAtMaturityWithEmptyMaturity() (gas: 35162) PreviewerTest:testPreviewRepayAtMaturityWithEmptyMaturityAndZeroAmount() (gas: 35170) -PreviewerTest:testPreviewRepayAtMaturityWithInvalidMaturity() (gas: 35215) -PreviewerTest:testPreviewRepayAtMaturityWithMaturedMaturity() (gas: 674390) -PreviewerTest:testPreviewRepayAtMaturityWithOneUnit() (gas: 667087) -PreviewerTest:testPreviewRepayAtMaturityWithSameTimestamp() (gas: 664210) -PreviewerTest:testPreviewRepayAtMaturityWithZeroAmount() (gas: 667090) -PreviewerTest:testPreviewValueInFixedOperations() (gas: 2882224) +PreviewerTest:testPreviewRepayAtMaturityWithInvalidMaturity() (gas: 35170) +PreviewerTest:testPreviewRepayAtMaturityWithMaturedMaturity() (gas: 674468) +PreviewerTest:testPreviewRepayAtMaturityWithOneUnit() (gas: 667165) +PreviewerTest:testPreviewRepayAtMaturityWithSameTimestamp() (gas: 664288) +PreviewerTest:testPreviewRepayAtMaturityWithZeroAmount() (gas: 667190) +PreviewerTest:testPreviewValueInFixedOperations() (gas: 2883110) PreviewerTest:testPreviewWithdrawAtMaturityReturningAccurateAmount() (gas: 457863) -PreviewerTest:testPreviewWithdrawAtMaturityReturningAccurateAmountWithIntermediateOperations() (gas: 1717560) -PreviewerTest:testPreviewWithdrawAtMaturityWithEmptyMaturity() (gas: 35184) +PreviewerTest:testPreviewWithdrawAtMaturityReturningAccurateAmountWithIntermediateOperations() (gas: 1717660) +PreviewerTest:testPreviewWithdrawAtMaturityWithEmptyMaturity() (gas: 35206) PreviewerTest:testPreviewWithdrawAtMaturityWithEmptyMaturityAndZeroAmount() (gas: 35182) PreviewerTest:testPreviewWithdrawAtMaturityWithFiveUnits() (gas: 329675) PreviewerTest:testPreviewWithdrawAtMaturityWithInvalidMaturity() (gas: 35225) -PreviewerTest:testPreviewWithdrawAtMaturityWithMaturedMaturity() (gas: 303436) +PreviewerTest:testPreviewWithdrawAtMaturityWithMaturedMaturity() (gas: 303458) PreviewerTest:testPreviewWithdrawAtMaturityWithOneUnit() (gas: 329686) PreviewerTest:testPreviewWithdrawAtMaturityWithSameTimestamp() (gas: 303363) -PreviewerTest:testPreviewWithdrawAtMaturityWithZeroAmount() (gas: 329691) -PreviewerTest:testReserveFactor() (gas: 716070) -PreviewerTest:testReturnRewardAssetUsdPrice() (gas: 7458678) -PreviewerTest:testRewardRatesMaturities() (gas: 7582626) -PreviewerTest:testRewardsRateAfterDistributionEnd() (gas: 8075247) -PreviewerTest:testRewardsRateOnlyWithFixedBorrows() (gas: 7513863) -PreviewerTest:testRewardsRateWithDifferentRewardLengths() (gas: 22763251) -PreviewerTest:testRewardsRateWithMarketWithDifferentDecimals() (gas: 22475605) -PreviewerTest:testRewardsRateX() (gas: 9180159) +PreviewerTest:testPreviewWithdrawAtMaturityWithZeroAmount() (gas: 329713) +PreviewerTest:testReserveFactor() (gas: 716271) +PreviewerTest:testReturnRewardAssetUsdPrice() (gas: 7459400) +PreviewerTest:testRewardRatesMaturities() (gas: 7583032) +PreviewerTest:testRewardsRateAfterDistributionEnd() (gas: 8075977) +PreviewerTest:testRewardsRateOnlyWithFixedBorrows() (gas: 7514537) +PreviewerTest:testRewardsRateWithDifferentRewardLengths() (gas: 22765450) +PreviewerTest:testRewardsRateWithMarketWithDifferentDecimals() (gas: 22477971) +PreviewerTest:testRewardsRateX() (gas: 9181458) PriceFeedDoubleTest:testPriceFeedDoubleReturningAccurateDecimals() (gas: 617669) PriceFeedDoubleTest:testPriceFeedDoubleReturningPrice() (gas: 76193) PriceFeedDoubleTest:testPriceFeedDoubleWithActualOnChainValues() (gas: 85326) @@ -456,81 +493,81 @@ RatePreviewerTest:testPreviewRate() (gas: 1215583) RatePreviewerTest:testPreviewRateAfterFloatingDepositAndBorrow() (gas: 1411833) RatePreviewerTest:testSnapshot() (gas: 1654022) RatePreviewerTest:testSnapshotWithSignificantElapsedTime() (gas: 1724086) -RewardsControllerTest:testAccountKeeperClaimOnBehalfOf() (gas: 1443126) -RewardsControllerTest:testAccrueRewardsForWholeDistributionPeriod() (gas: 1150887) -RewardsControllerTest:testAccrueRewardsWithBadDebtClearingOfFixedBorrow() (gas: 3594463) -RewardsControllerTest:testAccrueRewardsWithFixedWithdraw() (gas: 1368370) -RewardsControllerTest:testAccrueRewardsWithRepayOfBorrowBalance() (gas: 1608461) -RewardsControllerTest:testAccrueRewardsWithRepayOfFixedBorrowBalance() (gas: 1877715) -RewardsControllerTest:testAccrueRewardsWithSeizeOfAllDepositShares() (gas: 2045291) -RewardsControllerTest:testAfterDistributionPeriodEnd() (gas: 1574412) -RewardsControllerTest:testAllClaimableOnlyWithUSDCOps() (gas: 1019234) -RewardsControllerTest:testAllClaimableUSDCWithAnotherAccountInPool() (gas: 4805081) -RewardsControllerTest:testAllClaimableUSDCWithDeposit() (gas: 4352506) -RewardsControllerTest:testAllClaimableUSDCWithFixedDeposit() (gas: 3333056) -RewardsControllerTest:testAllClaimableUSDCWithFloatingBorrow() (gas: 3338085) -RewardsControllerTest:testAllClaimableUSDCWithFloatingRefund() (gas: 2708112) -RewardsControllerTest:testAllClaimableUSDCWithFloatingRepay() (gas: 2740751) -RewardsControllerTest:testAllClaimableUSDCWithMint() (gas: 3066727) -RewardsControllerTest:testAllClaimableUSDCWithRedeem() (gas: 2709819) -RewardsControllerTest:testAllClaimableUSDCWithTransfer() (gas: 4697609) -RewardsControllerTest:testAllClaimableUSDCWithTransferFrom() (gas: 4347894) -RewardsControllerTest:testAllClaimableUSDCWithWithdraw() (gas: 2754386) -RewardsControllerTest:testAllClaimableWETH() (gas: 2937613) -RewardsControllerTest:testAllClaimableWithMaturedFixedPool() (gas: 1726272) -RewardsControllerTest:testAllClaimableWithMultipleMarketOps() (gas: 2659940) -RewardsControllerTest:testAllClaimableWithTimeElapsedZero() (gas: 1878097) +RewardsControllerTest:testAccountKeeperClaimOnBehalfOf() (gas: 1443204) +RewardsControllerTest:testAccrueRewardsForWholeDistributionPeriod() (gas: 1150965) +RewardsControllerTest:testAccrueRewardsWithBadDebtClearingOfFixedBorrow() (gas: 3598804) +RewardsControllerTest:testAccrueRewardsWithFixedWithdraw() (gas: 1368448) +RewardsControllerTest:testAccrueRewardsWithRepayOfBorrowBalance() (gas: 1611508) +RewardsControllerTest:testAccrueRewardsWithRepayOfFixedBorrowBalance() (gas: 1880762) +RewardsControllerTest:testAccrueRewardsWithSeizeOfAllDepositShares() (gas: 2048416) +RewardsControllerTest:testAfterDistributionPeriodEnd() (gas: 1574490) +RewardsControllerTest:testAllClaimableOnlyWithUSDCOps() (gas: 1019312) +RewardsControllerTest:testAllClaimableUSDCWithAnotherAccountInPool() (gas: 4805237) +RewardsControllerTest:testAllClaimableUSDCWithDeposit() (gas: 4352584) +RewardsControllerTest:testAllClaimableUSDCWithFixedDeposit() (gas: 3333134) +RewardsControllerTest:testAllClaimableUSDCWithFloatingBorrow() (gas: 3340388) +RewardsControllerTest:testAllClaimableUSDCWithFloatingRefund() (gas: 2710415) +RewardsControllerTest:testAllClaimableUSDCWithFloatingRepay() (gas: 2743054) +RewardsControllerTest:testAllClaimableUSDCWithMint() (gas: 3066805) +RewardsControllerTest:testAllClaimableUSDCWithRedeem() (gas: 2709842) +RewardsControllerTest:testAllClaimableUSDCWithTransfer() (gas: 4697632) +RewardsControllerTest:testAllClaimableUSDCWithTransferFrom() (gas: 4347917) +RewardsControllerTest:testAllClaimableUSDCWithWithdraw() (gas: 2754409) +RewardsControllerTest:testAllClaimableWETH() (gas: 2937691) +RewardsControllerTest:testAllClaimableWithMaturedFixedPool() (gas: 1726350) +RewardsControllerTest:testAllClaimableWithMultipleMarketOps() (gas: 2660681) +RewardsControllerTest:testAllClaimableWithTimeElapsedZero() (gas: 1878253) RewardsControllerTest:testAllRewards() (gas: 229708) -RewardsControllerTest:testClaim() (gas: 1365537) -RewardsControllerTest:testClaimAll() (gas: 2839239) -RewardsControllerTest:testClaimFixedDepositRewards() (gas: 1501966) -RewardsControllerTest:testClaimMarketWithoutRewards() (gas: 1386672) -RewardsControllerTest:testClaimWithAllClaimableArgs() (gas: 3085888) -RewardsControllerTest:testClaimWithNotEnabledRewardAsset() (gas: 1521001) +RewardsControllerTest:testClaim() (gas: 1365615) +RewardsControllerTest:testClaimAll() (gas: 2839538) +RewardsControllerTest:testClaimFixedDepositRewards() (gas: 1502044) +RewardsControllerTest:testClaimMarketWithoutRewards() (gas: 1386971) +RewardsControllerTest:testClaimWithAllClaimableArgs() (gas: 3086629) +RewardsControllerTest:testClaimWithNotEnabledRewardAsset() (gas: 1521079) RewardsControllerTest:testConfigSettingNewStartWithOnGoingDistributionShouldNotUpdate() (gas: 450871) RewardsControllerTest:testConfigWithDistributionNotYetStartedShouldNotFail() (gas: 599033) RewardsControllerTest:testConfigWithTransitionFactorHigherOrEqThanCap() (gas: 111158) RewardsControllerTest:testConfigWithZeroDepositAllocationWeightFactorShouldRevert() (gas: 75666) -RewardsControllerTest:testDifferentDistributionTimeForDifferentRewards() (gas: 2404108) -RewardsControllerTest:testEmitAccrue() (gas: 1428228) -RewardsControllerTest:testEmitClaimRewards() (gas: 1134160) +RewardsControllerTest:testDifferentDistributionTimeForDifferentRewards() (gas: 2406632) +RewardsControllerTest:testEmitAccrue() (gas: 1428306) +RewardsControllerTest:testEmitClaimRewards() (gas: 1134238) RewardsControllerTest:testEmitConfigUpdate() (gas: 439022) -RewardsControllerTest:testEmitIndexUpdate() (gas: 1512926) -RewardsControllerTest:testLastUndistributed() (gas: 2017749) -RewardsControllerTest:testLastUpdateAfterDistributionPeriodEnd() (gas: 1608576) +RewardsControllerTest:testEmitIndexUpdate() (gas: 1513004) +RewardsControllerTest:testLastUndistributed() (gas: 2017827) +RewardsControllerTest:testLastUpdateAfterDistributionPeriodEnd() (gas: 1608654) RewardsControllerTest:testNotKeeperClaimOnBehalfOf() (gas: 184609) -RewardsControllerTest:testOperationAfterDistributionEnded() (gas: 722552) -RewardsControllerTest:testOperationsBeforeDistributionStart() (gas: 1696407) -RewardsControllerTest:testPermitClaim() (gas: 1403145) -RewardsControllerTest:testSetDistributionConfigWithDifferentDecimals() (gas: 14896313) +RewardsControllerTest:testOperationAfterDistributionEnded() (gas: 722630) +RewardsControllerTest:testOperationsBeforeDistributionStart() (gas: 1696485) +RewardsControllerTest:testPermitClaim() (gas: 1403223) +RewardsControllerTest:testSetDistributionConfigWithDifferentDecimals() (gas: 14897549) RewardsControllerTest:testSetDistributionOperationShouldUpdateIndex() (gas: 144923) -RewardsControllerTest:testSetDistributionWithOnGoingMarketOperations() (gas: 1377875) -RewardsControllerTest:testSetHigherTotalDistribution() (gas: 1637384) +RewardsControllerTest:testSetDistributionWithOnGoingMarketOperations() (gas: 1377953) +RewardsControllerTest:testSetHigherTotalDistribution() (gas: 1637462) RewardsControllerTest:testSetKeeperOnlyAdminRole() (gas: 117586) -RewardsControllerTest:testSetLowerAndEqualDistributionPeriodThanCurrentTimestampShouldRevert() (gas: 1240428) -RewardsControllerTest:testSetLowerAndEqualTotalDistributionThanReleasedShouldRevert() (gas: 1233496) -RewardsControllerTest:testSetLowerDistributionPeriod() (gas: 1990695) -RewardsControllerTest:testSetLowerDistributionPeriodAndLowerTotalDistribution() (gas: 1993508) -RewardsControllerTest:testSetLowerTotalDistribution() (gas: 1637407) -RewardsControllerTest:testSetNewDistributionPeriod() (gas: 2343107) -RewardsControllerTest:testSetNewDistributionPeriodAfterDistributionEnds() (gas: 1327111) -RewardsControllerTest:testSetNewTargetDebt() (gas: 1507473) -RewardsControllerTest:testSetNewTargetDebtAfterDistributionEnds() (gas: 1545944) -RewardsControllerTest:testSetNewTargetDebtWithClaimOnlyAtEnd() (gas: 1290501) -RewardsControllerTest:testSetNewTreasuryFeeShouldImpactAllocation() (gas: 776024) -RewardsControllerTest:testSetTargetDebtMultipleTimes() (gas: 2290880) -RewardsControllerTest:testSetTargetDebtMultipleTimesAfterEnd() (gas: 2249634) -RewardsControllerTest:testSetTotalDistributionMultipleTimes() (gas: 1756890) -RewardsControllerTest:testTriggerHandleBorrowHookBeforeUpdatingFloatingDebt() (gas: 2170234) -RewardsControllerTest:testUpdateConfig() (gas: 1449802) -RewardsControllerTest:testUpdateIndexesWithUtilizationEqualToOne() (gas: 1206429) -RewardsControllerTest:testUpdateIndexesWithUtilizationHigherThanOne() (gas: 1301012) +RewardsControllerTest:testSetLowerAndEqualDistributionPeriodThanCurrentTimestampShouldRevert() (gas: 1240506) +RewardsControllerTest:testSetLowerAndEqualTotalDistributionThanReleasedShouldRevert() (gas: 1233574) +RewardsControllerTest:testSetLowerDistributionPeriod() (gas: 1990773) +RewardsControllerTest:testSetLowerDistributionPeriodAndLowerTotalDistribution() (gas: 1993586) +RewardsControllerTest:testSetLowerTotalDistribution() (gas: 1637485) +RewardsControllerTest:testSetNewDistributionPeriod() (gas: 2343185) +RewardsControllerTest:testSetNewDistributionPeriodAfterDistributionEnds() (gas: 1327189) +RewardsControllerTest:testSetNewTargetDebt() (gas: 1507551) +RewardsControllerTest:testSetNewTargetDebtAfterDistributionEnds() (gas: 1546022) +RewardsControllerTest:testSetNewTargetDebtWithClaimOnlyAtEnd() (gas: 1290579) +RewardsControllerTest:testSetNewTreasuryFeeShouldImpactAllocation() (gas: 776102) +RewardsControllerTest:testSetTargetDebtMultipleTimes() (gas: 2291036) +RewardsControllerTest:testSetTargetDebtMultipleTimesAfterEnd() (gas: 2249790) +RewardsControllerTest:testSetTotalDistributionMultipleTimes() (gas: 1756968) +RewardsControllerTest:testTriggerHandleBorrowHookBeforeUpdatingFloatingDebt() (gas: 2174840) +RewardsControllerTest:testUpdateConfig() (gas: 1449880) +RewardsControllerTest:testUpdateIndexesWithUtilizationEqualToOne() (gas: 1208732) +RewardsControllerTest:testUpdateIndexesWithUtilizationHigherThanOne() (gas: 1303315) RewardsControllerTest:testUpdateWithTotalDebtZeroShouldUpdateLastUndistributed() (gas: 752988) -RewardsControllerTest:testUtilizationEqualZero() (gas: 922942) -RewardsControllerTest:testWithTwelveFixedPools() (gas: 6259365) +RewardsControllerTest:testUtilizationEqualZero() (gas: 923020) +RewardsControllerTest:testWithTwelveFixedPools() (gas: 6260379) RewardsControllerTest:testWithdrawAllRewardBalance() (gas: 91314) RewardsControllerTest:testWithdrawOnlyAdminRole() (gas: 121375) -RewardsControllerTest:testWithdrawUndistributed() (gas: 1704125) +RewardsControllerTest:testWithdrawUndistributed() (gas: 1704281) RewardsControllerTest:testWithdrawUndistributedOnlyAdminRole() (gas: 180933) RewardsControllerTest:testWithdrawUndistributedOnlyEndedDistribution() (gas: 155130) StakedEXATest:invariantAvgIndexUpOnly() (runs: 10, calls: 5000, reverts: 0) @@ -540,8 +577,8 @@ StakedEXATest:invariantNoDuplicatedReward() (runs: 10, calls: 5000, reverts: 0) StakedEXATest:invariantRewardsUpOnly() (runs: 10, calls: 5000, reverts: 0) StakedEXATest:invariantShareValueIsOne() (runs: 10, calls: 5000, reverts: 0) StakedEXATest:testAlreadyListedError() (gas: 43677) -StakedEXATest:testAvgIndex(uint256[3],uint256[2]) (runs: 256, μ: 1321279, ~: 1370843) -StakedEXATest:testAvgStartTime(uint256[3],uint256[2]) (runs: 256, μ: 1249345, ~: 1298909) +StakedEXATest:testAvgIndex(uint256[3],uint256[2]) (runs: 256, μ: 1322338, ~: 1370842) +StakedEXATest:testAvgStartTime(uint256[3],uint256[2]) (runs: 256, μ: 1250404, ~: 1298908) StakedEXATest:testBalanceOfDeposit(uint80) (runs: 256, μ: 331003, ~: 339807) StakedEXATest:testBalanceOfWithdraw(uint256) (runs: 256, μ: 82393, ~: 82407) StakedEXATest:testCanChangeRewardsDurationWhenDisabled() (gas: 176331) @@ -550,12 +587,12 @@ StakedEXATest:testClaimAndUnstake() (gas: 1622618) StakedEXATest:testClaimAndWithdrawAfterRefTime() (gas: 1029325) StakedEXATest:testClaimBeforeFirstHarvest() (gas: 527334) StakedEXATest:testDepositClaimsRewardsToReceiver() (gas: 1121108) -StakedEXATest:testDepositEvent(uint256) (runs: 256, μ: 335234, ~: 334978) -StakedEXATest:testDepositShouldClaim(uint256[2],uint32) (runs: 256, μ: 821450, ~: 754892) +StakedEXATest:testDepositEvent(uint256) (runs: 256, μ: 335230, ~: 334966) +StakedEXATest:testDepositShouldClaim(uint256[2],uint32) (runs: 256, μ: 822456, ~: 754904) StakedEXATest:testDepositToAnotherWithAllowance() (gas: 375284) StakedEXATest:testDepositToAnotherWithoutAllowanceShouldFail() (gas: 121959) -StakedEXATest:testDepositWithdrawAvgStartTimeAndIndex(uint256[3],uint256,uint256[5]) (runs: 256, μ: 1886121, ~: 1905249) -StakedEXATest:testEarnedWithTime(uint256) (runs: 256, μ: 86132, ~: 86326) +StakedEXATest:testDepositWithdrawAvgStartTimeAndIndex(uint256[3],uint256,uint256[5]) (runs: 256, μ: 1878831, ~: 1904635) +StakedEXATest:testEarnedWithTime(uint256) (runs: 256, μ: 86134, ~: 86326) StakedEXATest:testEmergencyAdminCanPauseNotUnpause() (gas: 175029) StakedEXATest:testFinishDistributionEmitEvent() (gas: 240752) StakedEXATest:testFinishDistributionLetsClaimUnclaimed() (gas: 1784877) @@ -566,9 +603,9 @@ StakedEXATest:testGrantRevokeEmergencyAdmin() (gas: 117824) StakedEXATest:testGrantRevokePauser() (gas: 117836) StakedEXATest:testHandlerClaim(uint8) (runs: 256, μ: 485340, ~: 485340) StakedEXATest:testHandlerDeposit(uint80) (runs: 256, μ: 1240331, ~: 1276242) -StakedEXATest:testHandlerHarvest(uint64) (runs: 256, μ: 402354, ~: 411143) -StakedEXATest:testHandlerNotifyRewardAmount(uint64) (runs: 256, μ: 165017, ~: 156797) -StakedEXATest:testHandlerSetDuration(uint32) (runs: 256, μ: 195324, ~: 218711) +StakedEXATest:testHandlerHarvest(uint64) (runs: 256, μ: 402356, ~: 411143) +StakedEXATest:testHandlerNotifyRewardAmount(uint64) (runs: 256, μ: 166012, ~: 156797) +StakedEXATest:testHandlerSetDuration(uint32) (runs: 256, μ: 196224, ~: 218711) StakedEXATest:testHandlerSetMarket() (gas: 144553) StakedEXATest:testHandlerWithdraw(uint256) (runs: 256, μ: 91497, ~: 91511) StakedEXATest:testHarvestAmountWithReducedAllowance() (gas: 184605) @@ -579,13 +616,13 @@ StakedEXATest:testHarvestWhenFinished() (gas: 326352) StakedEXATest:testHarvestX() (gas: 180952) StakedEXATest:testHarvestZero() (gas: 255436) StakedEXATest:testInitialValues() (gas: 196484) -StakedEXATest:testInsufficientBalanceError(uint256) (runs: 256, μ: 63922, ~: 64151) +StakedEXATest:testInsufficientBalanceError(uint256) (runs: 256, μ: 63929, ~: 64151) StakedEXATest:testMaxRewardsGasConsumption() (gas: 144674963) StakedEXATest:testMultipleClaimsVsOne() (gas: 21936440) StakedEXATest:testMultipleHarvests() (gas: 284083) -StakedEXATest:testNoRewardsAfterPeriod(uint256) (runs: 256, μ: 1809475, ~: 1804427) +StakedEXATest:testNoRewardsAfterPeriod(uint256) (runs: 256, μ: 1809884, ~: 1804427) StakedEXATest:testNotPausingRoleError() (gas: 39124) -StakedEXATest:testNotifyRewardAmount(uint256,uint256) (runs: 256, μ: 155635, ~: 155564) +StakedEXATest:testNotifyRewardAmount(uint256,uint256) (runs: 256, μ: 155618, ~: 155564) StakedEXATest:testNotifyRewardWithUnderlyingAsset() (gas: 460608) StakedEXATest:testOnlyAdminEnableReward() (gas: 1297302) StakedEXATest:testOnlyAdminFinishDistribution() (gas: 213640) @@ -603,13 +640,13 @@ StakedEXATest:testPenaltyThresholdRange() (gas: 36989) StakedEXATest:testPermitAndDeposit() (gas: 452146) StakedEXATest:testPermitFailKeepsFlow() (gas: 484480) StakedEXATest:testRemoveDepositAllowance() (gas: 466913) -StakedEXATest:testResetDepositAfterRefTime(uint256) (runs: 256, μ: 1033248, ~: 1032994) -StakedEXATest:testRewardAmountNotifiedEvent(uint256) (runs: 256, μ: 105543, ~: 105293) +StakedEXATest:testResetDepositAfterRefTime(uint256) (runs: 256, μ: 1033243, ~: 1032994) +StakedEXATest:testRewardAmountNotifiedEvent(uint256) (runs: 256, μ: 105549, ~: 105281) StakedEXATest:testRewardNotListedError() (gas: 1177184) -StakedEXATest:testRewardPaidEvent(uint256,uint256) (runs: 256, μ: 811252, ~: 854321) -StakedEXATest:testRewardsAmounts(uint256) (runs: 256, μ: 1707520, ~: 1707287) +StakedEXATest:testRewardPaidEvent(uint256,uint256) (runs: 256, μ: 810714, ~: 854321) +StakedEXATest:testRewardsAmounts(uint256) (runs: 256, μ: 1707512, ~: 1707287) StakedEXATest:testRewardsDurationSetEvent(uint40) (runs: 256, μ: 52173, ~: 52149) -StakedEXATest:testSetDuration(uint256,uint40) (runs: 256, μ: 66028, ~: 64453) +StakedEXATest:testSetDuration(uint256,uint40) (runs: 256, μ: 66005, ~: 64453) StakedEXATest:testSetMarketAddressZero() (gas: 36979) StakedEXATest:testSetMarketOnlyAdmin() (gas: 1345554) StakedEXATest:testSetMaxRewardsTokensExceeded() (gas: 111958823) @@ -621,12 +658,12 @@ StakedEXATest:testSetSavingsZeroAddressError() (gas: 37006) StakedEXATest:testTotalSupplyDeposit(uint80) (runs: 256, μ: 330389, ~: 339193) StakedEXATest:testTotalSupplyWithdraw(uint256) (runs: 256, μ: 81753, ~: 81767) StakedEXATest:testUntransferable(uint80) (runs: 256, μ: 338580, ~: 348672) -StakedEXATest:testWithdrawEvent(uint256) (runs: 256, μ: 483401, ~: 483158) +StakedEXATest:testWithdrawEvent(uint256) (runs: 256, μ: 483396, ~: 483140) StakedEXATest:testWithdrawRewardUnderlyingAsset() (gas: 456617) StakedEXATest:testWithdrawRewardsOnlyAdmin() (gas: 242200) StakedEXATest:testWithdrawRewardsOnlyReward() (gas: 1177174) -StakedEXATest:testWithdrawSameAmountRewardsShouldEqual(uint256,uint256) (runs: 256, μ: 1065854, ~: 1107874) -StakedEXATest:testWithdrawWithRewards(uint256) (runs: 256, μ: 895625, ~: 895382) +StakedEXATest:testWithdrawSameAmountRewardsShouldEqual(uint256,uint256) (runs: 256, μ: 1065777, ~: 1107874) +StakedEXATest:testWithdrawWithRewards(uint256) (runs: 256, μ: 895620, ~: 895364) StakedEXATest:testZeroRateError() (gas: 58274) StakingPreviewerTest:testAllClaimable() (gas: 769205) StakingPreviewerTest:testAllClaimed() (gas: 818952) @@ -641,209 +678,224 @@ SwapperTest:testSwapWithKeepEqualToValue() (gas: 74368) SwapperTest:testSwapWithKeepHigherThanValue() (gas: 74397) SwapperTest:testSwapWithPermit() (gas: 517429) SwapperTest:testSwapWithPermit2() (gas: 522021) -VerifiedAuditorTest:test_lock_reverts_whenTargetIsAllowed() (gas: 49063) -VerifiedAuditorTest:test_lock_reverts_withNotAllowed_whenSenderNotAllowed() (gas: 50315) -VerifiedAuditorTest:test_setFirewall_emitsFirewallSet() (gas: 750850) -VerifiedAuditorTest:test_setFirewall_reverts_whenNotAdmin() (gas: 771425) -VerifiedAuditorTest:test_setFirewall_sets_whenAdmin() (gas: 50939) -VerifiedAuditorTest:test_unlock_reverts_withInvalidOperation_whenAccountIsNotAllowed() (gas: 100837) -VerifiedAuditorTest:test_unlock_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 181052) -VerifiedMarketTest:testAccountLiquidityAdjustedDebt() (gas: 689747) -VerifiedMarketTest:testAccountsFixedConsolidated() (gas: 2283365) -VerifiedMarketTest:testAccountsFixedConsolidatedWhenClearBadDebt() (gas: 2116715) -VerifiedMarketTest:testAccountsFixedConsolidatedWhenSenderNotOwner() (gas: 1059108) -VerifiedMarketTest:testAccountsFixedConsolidatedWithPartialRepayAndWithdraw() (gas: 1616607) -VerifiedMarketTest:testAccrueEarningsBeforeLiquidation() (gas: 1647563) -VerifiedMarketTest:testAnotherUserRedeemWhenOwnerHasShortfall() (gas: 980056) -VerifiedMarketTest:testAnotherUserWithdrawWhenOwnerHasShortfall() (gas: 926232) -VerifiedMarketTest:testBorrowAfterFreezing() (gas: 528855) -VerifiedMarketTest:testBorrowAtMaturity() (gas: 581264) -VerifiedMarketTest:testBorrowAtMaturityAfterFreezing() (gas: 654853) -VerifiedMarketTest:testBorrowAtMaturityUpdatesFloatingDebtAndFloatingAssets() (gas: 908483) -VerifiedMarketTest:testBorrowAtMaturityWhenFrozen() (gas: 93517) -VerifiedMarketTest:testBorrowAtMaturityWithZeroAssets() (gas: 40291) -VerifiedMarketTest:testBorrowDisagreement() (gas: 320351) -VerifiedMarketTest:testBorrowFromFreeLunchShouldNotRevertWithFloatingFullUtilization() (gas: 1406767) -VerifiedMarketTest:testBorrowWhenFrozen() (gas: 93043) -VerifiedMarketTest:testBorrowWithZeroAssets() (gas: 39700) -VerifiedMarketTest:testCappedLiquidation() (gas: 1348206) -VerifiedMarketTest:testChargeTreasuryToEarlyWithdraws() (gas: 1567819) -VerifiedMarketTest:testChargeTreasuryToFixedBorrows() (gas: 1899094) -VerifiedMarketTest:testClearBadDebtAvoidingFixedBorrowsIfAccumulatorLower() (gas: 2397802) -VerifiedMarketTest:testClearBadDebtBeforeMaturity() (gas: 2490534) -VerifiedMarketTest:testClearBadDebtCalledByAccount() (gas: 34934) -VerifiedMarketTest:testClearBadDebtEmptiesUnassignedEarnings() (gas: 2729324) -VerifiedMarketTest:testClearBadDebtExactlyRepaysFixedBorrowWithAccumulatorAmount() (gas: 2188136) -VerifiedMarketTest:testClearBadDebtPartiallyRepaysEachFixedBorrow() (gas: 2119707) -VerifiedMarketTest:testClearBadDebtPartiallyRepaysFloatingDebt() (gas: 2464438) -VerifiedMarketTest:testClearBadDebtShouldAccrueAccumulatedEarningsBeforeSpreadingLosses() (gas: 2449611) -VerifiedMarketTest:testClearBadDebtWithEmptyAccumulatorShouldNotRevert() (gas: 1114998) -VerifiedMarketTest:testClearMaturity() (gas: 1968868) -VerifiedMarketTest:testCollectTreasuryFreeLunchToEarlyWithdraws() (gas: 2234745) -VerifiedMarketTest:testCollectTreasuryFreeLunchToEarlyWithdrawsWithZeroFees() (gas: 714784) -VerifiedMarketTest:testCollectTreasuryFreeLunchToFixedBorrows() (gas: 2216397) -VerifiedMarketTest:testCollectTreasuryFreeLunchToFixedBorrowsWithZeroFees() (gas: 922901) -VerifiedMarketTest:testCrossMaturityLiquidation() (gas: 2799891) -VerifiedMarketTest:testDepositAfterFreezing() (gas: 281178) -VerifiedMarketTest:testDepositAtMaturity() (gas: 261138) -VerifiedMarketTest:testDepositAtMaturityAfterFreezing() (gas: 335035) -VerifiedMarketTest:testDepositAtMaturityWhenFrozen() (gas: 116211) -VerifiedMarketTest:testDepositAtMaturityWithZeroAssets() (gas: 63077) -VerifiedMarketTest:testDepositDisagreement() (gas: 97017) -VerifiedMarketTest:testDepositShouldUpdateFlexibleBorrowVariables() (gas: 880943) -VerifiedMarketTest:testDepositToSmartPool() (gas: 206984) -VerifiedMarketTest:testDepositWhenFrozen() (gas: 199362) -VerifiedMarketTest:testDistributeMultipleAccumulatedEarnings() (gas: 1859748) -VerifiedMarketTest:testDistributionOfLossesShouldReduceFromFloatingBackupBorrowedAccordingly() (gas: 6782317) -VerifiedMarketTest:testEarlyRepayLiquidationUnassignedEarnings() (gas: 2710828) -VerifiedMarketTest:testEarlyRepaymentWithExcessiveAmountOfFees() (gas: 3559577) -VerifiedMarketTest:testEarlyWithdrawFromFreeLunchShouldNotRevertWithFloatingFullUtilization() (gas: 1153836) +VerifiedAuditorTest:test_lock_reverts_whenTargetIsAllowed() (gas: 49121) +VerifiedAuditorTest:test_lock_reverts_withNotAllowed_whenSenderNotAllowed() (gas: 50344) +VerifiedAuditorTest:test_setFirewall_emitsFirewallSet() (gas: 750894) +VerifiedAuditorTest:test_setFirewall_reverts_whenNotAdmin() (gas: 771469) +VerifiedAuditorTest:test_setFirewall_sets_whenAdmin() (gas: 51005) +VerifiedAuditorTest:test_unlock_reverts_withInvalidOperation_whenAccountIsNotAllowed() (gas: 100887) +VerifiedAuditorTest:test_unlock_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 181073) +VerifiedMarketTest:testAccountLiquidityAdjustedDebt() (gas: 690603) +VerifiedMarketTest:testAccountsFixedConsolidated() (gas: 2284226) +VerifiedMarketTest:testAccountsFixedConsolidatedWhenClearBadDebt() (gas: 2119891) +VerifiedMarketTest:testAccountsFixedConsolidatedWhenSenderNotOwner() (gas: 1059437) +VerifiedMarketTest:testAccountsFixedConsolidatedWithPartialRepayAndWithdraw() (gas: 1617208) +VerifiedMarketTest:testAccrueEarningsBeforeLiquidation() (gas: 1648295) +VerifiedMarketTest:testAnotherUserRedeemWhenOwnerHasShortfall() (gas: 981256) +VerifiedMarketTest:testAnotherUserWithdrawWhenOwnerHasShortfall() (gas: 927431) +VerifiedMarketTest:testBorrowAfterFreezing() (gas: 529235) +VerifiedMarketTest:testBorrowAtMaturity() (gas: 581733) +VerifiedMarketTest:testBorrowAtMaturityAfterFreezing() (gas: 655256) +VerifiedMarketTest:testBorrowAtMaturityUpdatesFloatingDebtAndFloatingAssets() (gas: 911036) +VerifiedMarketTest:testBorrowAtMaturityWhenFrozen() (gas: 93475) +VerifiedMarketTest:testBorrowAtMaturityWithZeroAssets() (gas: 40355) +VerifiedMarketTest:testBorrowDisagreement() (gas: 320395) +VerifiedMarketTest:testBorrowFromFreeLunchShouldNotRevertWithFloatingFullUtilization() (gas: 1409721) +VerifiedMarketTest:testBorrowWhenFrozen() (gas: 93020) +VerifiedMarketTest:testBorrowWithZeroAssets() (gas: 39699) +VerifiedMarketTest:testCappedLiquidation() (gas: 1349604) +VerifiedMarketTest:testChargeTreasuryToEarlyWithdraws() (gas: 1568332) +VerifiedMarketTest:testChargeTreasuryToFixedBorrows() (gas: 1900259) +VerifiedMarketTest:testClearBadDebtAvoidingFixedBorrowsIfAccumulatorLower() (gas: 2401458) +VerifiedMarketTest:testClearBadDebtBeforeMaturity() (gas: 2492399) +VerifiedMarketTest:testClearBadDebtCalledByAccount() (gas: 34999) +VerifiedMarketTest:testClearBadDebtEmptiesUnassignedEarnings() (gas: 2733135) +VerifiedMarketTest:testClearBadDebtExactlyRepaysFixedBorrowWithAccumulatorAmount() (gas: 2191477) +VerifiedMarketTest:testClearBadDebtPartiallyRepaysEachFixedBorrow() (gas: 2122964) +VerifiedMarketTest:testClearBadDebtPartiallyRepaysFloatingDebt() (gas: 2468094) +VerifiedMarketTest:testClearBadDebtShouldAccrueAccumulatedEarningsBeforeSpreadingLosses() (gas: 2452952) +VerifiedMarketTest:testClearBadDebtWithEmptyAccumulatorShouldNotRevert() (gas: 1118027) +VerifiedMarketTest:testClearMaturity() (gas: 1970059) +VerifiedMarketTest:testCollectTreasuryFreeLunchToEarlyWithdraws() (gas: 2235641) +VerifiedMarketTest:testCollectTreasuryFreeLunchToEarlyWithdrawsWithZeroFees() (gas: 714871) +VerifiedMarketTest:testCollectTreasuryFreeLunchToFixedBorrows() (gas: 2217694) +VerifiedMarketTest:testCollectTreasuryFreeLunchToFixedBorrowsWithZeroFees() (gas: 923325) +VerifiedMarketTest:testCrossMaturityLiquidation() (gas: 2802367) +VerifiedMarketTest:testDepositAfterFreezing() (gas: 281266) +VerifiedMarketTest:testDepositAtMaturity() (gas: 261137) +VerifiedMarketTest:testDepositAtMaturityAfterFreezing() (gas: 335057) +VerifiedMarketTest:testDepositAtMaturityWhenFrozen() (gas: 116299) +VerifiedMarketTest:testDepositAtMaturityWithZeroAssets() (gas: 63121) +VerifiedMarketTest:testDepositBorrowRepayWithdrawOnNonCollateralMarket() (gas: 2395167) +VerifiedMarketTest:testDepositDisagreement() (gas: 97084) +VerifiedMarketTest:testDepositShouldUpdateFlexibleBorrowVariables() (gas: 881478) +VerifiedMarketTest:testDepositToSmartPool() (gas: 207006) +VerifiedMarketTest:testDepositWhenFrozen() (gas: 199384) +VerifiedMarketTest:testDistributeMultipleAccumulatedEarnings() (gas: 1860371) +VerifiedMarketTest:testDistributionOfLossesShouldReduceFromFloatingBackupBorrowedAccordingly() (gas: 6787223) +VerifiedMarketTest:testEarlyRepayLiquidationUnassignedEarnings() (gas: 2712132) +VerifiedMarketTest:testEarlyRepaymentWithExcessiveAmountOfFees() (gas: 3562576) +VerifiedMarketTest:testEarlyWithdrawFromFreeLunchShouldNotRevertWithFloatingFullUtilization() (gas: 1156476) VerifiedMarketTest:testEmergencyAdminRole() (gas: 373392) -VerifiedMarketTest:testEmitFrozen() (gas: 90739) -VerifiedMarketTest:testFixedBorrowFailingWhenFlexibleBorrowAccruesDebt() (gas: 1717675) -VerifiedMarketTest:testFixedBorrowRateToMaturity() (gas: 641898) -VerifiedMarketTest:testFixedOperationsUpdateFloatingDebt() (gas: 1088701) -VerifiedMarketTest:testFlexibleBorrow() (gas: 496462) -VerifiedMarketTest:testFlexibleBorrowAccountingDebt() (gas: 832761) -VerifiedMarketTest:testFlexibleBorrowChargingDebtToTreasury() (gas: 895622) -VerifiedMarketTest:testFlexibleBorrowExceedingReserve() (gas: 925870) -VerifiedMarketTest:testFlexibleBorrowExceedingReserveIncludingFixedBorrow() (gas: 1335518) -VerifiedMarketTest:testFlexibleBorrowExceedingReserveWithNewDebt() (gas: 1004130) -VerifiedMarketTest:testFlexibleBorrowFromAnotherUserSubtractsAllowance() (gas: 515682) -VerifiedMarketTest:testFlexibleBorrowFromAnotherUserWithAllowance() (gas: 496259) -VerifiedMarketTest:testFlexibleBorrowFromAnotherUserWithoutAllowance() (gas: 277581) -VerifiedMarketTest:testFrontRunSmartPoolEarningsDistributionWithBigPenaltyRepayment() (gas: 1805440) -VerifiedMarketTest:testFullPause() (gas: 6350099) +VerifiedMarketTest:testEmitFrozen() (gas: 90721) +VerifiedMarketTest:testExitNonCollateralMarketWithBadHealthFactor() (gas: 1250087) +VerifiedMarketTest:testFixedBorrowFailingWhenFlexibleBorrowAccruesDebt() (gas: 1721581) +VerifiedMarketTest:testFixedBorrowRateToMaturity() (gas: 644428) +VerifiedMarketTest:testFixedOperationsUpdateFloatingDebt() (gas: 1089259) +VerifiedMarketTest:testFlexibleBorrow() (gas: 496842) +VerifiedMarketTest:testFlexibleBorrowAccountingDebt() (gas: 833252) +VerifiedMarketTest:testFlexibleBorrowChargingDebtToTreasury() (gas: 896383) +VerifiedMarketTest:testFlexibleBorrowExceedingReserve() (gas: 926383) +VerifiedMarketTest:testFlexibleBorrowExceedingReserveIncludingFixedBorrow() (gas: 1336434) +VerifiedMarketTest:testFlexibleBorrowExceedingReserveWithNewDebt() (gas: 1004914) +VerifiedMarketTest:testFlexibleBorrowFromAnotherUserSubtractsAllowance() (gas: 515942) +VerifiedMarketTest:testFlexibleBorrowFromAnotherUserWithAllowance() (gas: 496497) +VerifiedMarketTest:testFlexibleBorrowFromAnotherUserWithoutAllowance() (gas: 277647) +VerifiedMarketTest:testFrontRunSmartPoolEarningsDistributionWithBigPenaltyRepayment() (gas: 1805975) +VerifiedMarketTest:testFullPause() (gas: 6358558) +VerifiedMarketTest:testHandleBadDebtWithNonCollateralMarketSupply() (gas: 2336080) VerifiedMarketTest:testInitiallyUnfrozen() (gas: 15542) -VerifiedMarketTest:testInsufficientProtocolLiquidity() (gas: 1935169) -VerifiedMarketTest:testLiquidateAndChargeIncentiveForLenders() (gas: 2476332) -VerifiedMarketTest:testLiquidateAndDistributeLosses() (gas: 3458888) -VerifiedMarketTest:testLiquidateAndSeizeExactAmountWithDustAsCollateral() (gas: 2946749) -VerifiedMarketTest:testLiquidateAndSeizeFromEmptyCollateral() (gas: 1097476) -VerifiedMarketTest:testLiquidateAndSubtractLossesFromAccumulator() (gas: 4230800) -VerifiedMarketTest:testLiquidateFlexibleAndFixedBorrowPositionsInSingleCall() (gas: 2663141) -VerifiedMarketTest:testLiquidateFlexibleBorrow() (gas: 2690784) -VerifiedMarketTest:testLiquidateFlexibleBorrowChargeLendersAssetsToLiquidator() (gas: 1190671) -VerifiedMarketTest:testLiquidateFlexibleBorrowConsideringDebtOverTime() (gas: 1266497) -VerifiedMarketTest:testLiquidateLeavingDustAsCollateral() (gas: 3787244) -VerifiedMarketTest:testLiquidateTransferRepayAssetsBeforeSeize() (gas: 1274261) -VerifiedMarketTest:testLiquidateUpdateFloatingDebt() (gas: 1922719) -VerifiedMarketTest:testLiquidateWhenFrozen() (gas: 1223473) -VerifiedMarketTest:testLiquidateWithTwoUnitsAsMaxAssets() (gas: 1739239) -VerifiedMarketTest:testLiquidateWithZeroAsMaxAssets() (gas: 1097449) -VerifiedMarketTest:testLiquidationClearingDebtOfAllAccountMarkets() (gas: 3349493) -VerifiedMarketTest:testLiquidationResultingInZeroCollateralAndZeroDebt() (gas: 2215841) -VerifiedMarketTest:testMaturityInsufficientProtocolLiquidity() (gas: 1656273) -VerifiedMarketTest:testMultipleBorrowsForMultipleAssets() (gas: 2495111543) -VerifiedMarketTest:testMultipleDepositsToSmartPool() (gas: 1092039) -VerifiedMarketTest:testMultipleFixedBorrowsRepays() (gas: 1584214) -VerifiedMarketTest:testMultipleLiquidationSameUser() (gas: 3456531) -VerifiedMarketTest:testNotEnteredMarketShouldNotBeSeized() (gas: 11342600) -VerifiedMarketTest:testOnlyAdminCanFreezeUnfreeze() (gas: 228441) -VerifiedMarketTest:testOperationsShouldUpdateFloatingAssetsAverage() (gas: 1792829) -VerifiedMarketTest:testOperationsWithBtcWbtcRate() (gas: 11098193) -VerifiedMarketTest:testOperationsWithStEthAsset() (gas: 10987532) -VerifiedMarketTest:testPausable() (gas: 183171) -VerifiedMarketTest:testPauserRole() (gas: 88366) -VerifiedMarketTest:testPreviewOperationsWithSmartPoolCorrectlyAccountingEarnings() (gas: 2447098) -VerifiedMarketTest:testRepayAtMaturity() (gas: 687233) -VerifiedMarketTest:testRepayDisagreement() (gas: 696997) -VerifiedMarketTest:testRepayFlexibleBorrow() (gas: 1389567) -VerifiedMarketTest:testRepayWhenFrozen() (gas: 593929) -VerifiedMarketTest:testRoundingDownAssetsToValidateShortfallWhenTransferring() (gas: 9609849) -VerifiedMarketTest:testRoundingDownAssetsToValidateShortfallWhenTransferringFrom() (gas: 9665305) -VerifiedMarketTest:testRoundingDownAssetsWhenTransferingFromAnAccountWithoutShortfall() (gas: 1196378) -VerifiedMarketTest:testRoundingDownAssetsWhenTransferingWithAnAccountWithoutShortfall() (gas: 1138384) -VerifiedMarketTest:testRoundingUpAllowanceWhenBorrowingAtMaturity() (gas: 949746) -VerifiedMarketTest:testRoundingUpAllowanceWhenWithdrawingAtMaturity() (gas: 962141) -VerifiedMarketTest:testSetDampSpeedFactorShouldUpdateFloatingAssetsAverage() (gas: 456993) -VerifiedMarketTest:testSetEarningsAccumulatorSmoothFactorShouldDistributeEarnings() (gas: 964219) -VerifiedMarketTest:testSetInterestRateModelShouldUpdateFloatingDebt() (gas: 2889089) -VerifiedMarketTest:testSetInterestRateModelWithAddressZeroShouldNotUpdateFloatingDebt() (gas: 2677966) -VerifiedMarketTest:testShareValueNotDecreasingAfterDeposit() (gas: 855320) -VerifiedMarketTest:testShareValueNotDecreasingWhenMintingToTreasury() (gas: 893914) -VerifiedMarketTest:testSingleFloatingBorrow() (gas: 454962) -VerifiedMarketTest:testSingleFloatingRepay() (gas: 543249) -VerifiedMarketTest:testSmartPoolEarningsDistribution() (gas: 1316759) -VerifiedMarketTest:testSmartPoolSharesDoNotAccountUnassignedEarningsFromMoreThanOneIntervalPastMaturities() (gas: 715892) -VerifiedMarketTest:testSumDebtPlusEffectsShouldntRoundUpWhenWithdrawing() (gas: 1169221) -VerifiedMarketTest:testTotalAssetsProjectingBackupEarningsCorrectly() (gas: 629669) -VerifiedMarketTest:testTotalAssetsProjectingFloatingDebtCorrectly() (gas: 902594) -VerifiedMarketTest:testUpdateAccumulatedEarningsFactorToZero() (gas: 1917398) -VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenDepositingAndBorrowingContinuously() (gas: 468027) -VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenDepositingRightBeforeBorrow() (gas: 799808) -VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenDepositingRightBeforeEarlyWithdraw() (gas: 782962) -VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenDepositingSomeSecondsBeforeBorrow() (gas: 1130209) -VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenWithdrawingRightBeforeBorrow() (gas: 803847) -VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenWithdrawingRightBeforeEarlyWithdraw() (gas: 787001) -VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenWithdrawingSomeSecondsBeforeBorrow() (gas: 572766) -VerifiedMarketTest:testUpdateFloatingAssetsAverageWithDampSpeedDown() (gas: 477760) -VerifiedMarketTest:testUpdateFloatingAssetsAverageWithDampSpeedUp() (gas: 341543) -VerifiedMarketTest:testUpdateFloatingDebtBeforeSettingTreasury() (gas: 115218) -VerifiedMarketTest:testWithdrawAtMaturity() (gas: 383850) -VerifiedMarketTest:testWithdrawFromSmartPool() (gas: 323363) -VerifiedMarketTest:testWithdrawShouldUpdateFlexibleBorrowVariables() (gas: 997995) -VerifiedMarketTest:testWithdrawWhenFrozen() (gas: 361252) -VerifiedMarketTest:test_borrowAtMaturity_borrows_whenBorrowerIsAllowed() (gas: 563239) -VerifiedMarketTest:test_borrowAtMaturity_reverts_withNotAllowed_whenBorrowerIsNotAllowed() (gas: 519554) -VerifiedMarketTest:test_borrow_borrows_whenBorrowerIsAllowed() (gas: 437133) -VerifiedMarketTest:test_borrow_reverts_withNotAllowed_whenBorrowerIsNotAllowed() (gas: 387641) -VerifiedMarketTest:test_depositAtMaturity_deposits_whenSenderAndReceiverAreAllowed() (gas: 487677) -VerifiedMarketTest:test_depositAtMaturity_reverts_withNotAllowed_whenReceiverIsNotAllowed() (gas: 92609) -VerifiedMarketTest:test_depositAtMaturity_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 88627) -VerifiedMarketTest:test_deposit_allowsDepositUpToMaxSupply() (gas: 396186) -VerifiedMarketTest:test_deposit_deposits_whenSenderAndReceiverAreAllowed() (gas: 471344) -VerifiedMarketTest:test_deposit_deposits_whenTotalSupplyLowerThanMaxSupply() (gas: 247811) -VerifiedMarketTest:test_deposit_revert_withNotAllowed_whenBothSenderAndReceiverAreNotAllowed() (gas: 173676) -VerifiedMarketTest:test_deposit_reverts_whenTotalSupplyHigherThanMaxSupply() (gas: 183866) -VerifiedMarketTest:test_deposit_reverts_withNotAllowed_whenReceiverIsNotAllowed() (gas: 92280) -VerifiedMarketTest:test_deposit_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 173895) -VerifiedMarketTest:test_liquidateAllowedAccount_liquidates_withIncentives() (gas: 1444801) -VerifiedMarketTest:test_liquidateNotAllowedAccount_liquidates_withoutIncentives() (gas: 1401171) -VerifiedMarketTest:test_liquidateNotAllowedAccount_underwater_liquidates_withoutIncentives() (gas: 1401157) -VerifiedMarketTest:test_liquidate_reverts_withNotAllowed_whenLiquidatorIsNotAllowed() (gas: 1060759) -VerifiedMarketTest:test_lock_emits_locked() (gas: 433915) -VerifiedMarketTest:test_lock_emits_seize() (gas: 434440) -VerifiedMarketTest:test_lock_locks_whenAccountHasFixedDeposits() (gas: 575744) -VerifiedMarketTest:test_lock_locks_whenAccountHasNoDebt() (gas: 556499) -VerifiedMarketTest:test_lock_reverts_withNotAuditor_whenNotCalledByAuditor() (gas: 34982) -VerifiedMarketTest:test_lock_reverts_withRemainingDebt_whenAccountHasDebt() (gas: 596562) -VerifiedMarketTest:test_mint_mints_whenSenderAndReceiverAreAllowed() (gas: 395143) +VerifiedMarketTest:testInsufficientProtocolLiquidity() (gas: 1938257) +VerifiedMarketTest:testLiquidateAccountWithOnlyNonCollateralSupply() (gas: 1272742) +VerifiedMarketTest:testLiquidateAndChargeIncentiveForLenders() (gas: 2478545) +VerifiedMarketTest:testLiquidateAndDistributeLosses() (gas: 3461361) +VerifiedMarketTest:testLiquidateAndSeizeExactAmountWithDustAsCollateral() (gas: 2951188) +VerifiedMarketTest:testLiquidateAndSeizeFromEmptyCollateral() (gas: 1098264) +VerifiedMarketTest:testLiquidateAndSubtractLossesFromAccumulator() (gas: 4233533) +VerifiedMarketTest:testLiquidateFlexibleAndFixedBorrowPositionsInSingleCall() (gas: 2666414) +VerifiedMarketTest:testLiquidateFlexibleBorrow() (gas: 2693405) +VerifiedMarketTest:testLiquidateFlexibleBorrowChargeLendersAssetsToLiquidator() (gas: 1191805) +VerifiedMarketTest:testLiquidateFlexibleBorrowConsideringDebtOverTime() (gas: 1267632) +VerifiedMarketTest:testLiquidateLeavingDustAsCollateral() (gas: 3792595) +VerifiedMarketTest:testLiquidateTransferRepayAssetsBeforeSeize() (gas: 1274893) +VerifiedMarketTest:testLiquidateUpdateFloatingDebt() (gas: 1924572) +VerifiedMarketTest:testLiquidateWhenFrozen() (gas: 1226757) +VerifiedMarketTest:testLiquidateWithTwoUnitsAsMaxAssets() (gas: 1740999) +VerifiedMarketTest:testLiquidateWithZeroAsMaxAssets() (gas: 1098195) +VerifiedMarketTest:testLiquidationClearingDebtOfAllAccountMarkets() (gas: 3351695) +VerifiedMarketTest:testLiquidationResultingInZeroCollateralAndZeroDebt() (gas: 2217521) +VerifiedMarketTest:testMaturityInsufficientProtocolLiquidity() (gas: 1656808) +VerifiedMarketTest:testMultipleBorrowsForMultipleAssets() (gas: 2495813629) +VerifiedMarketTest:testMultipleDepositsToSmartPool() (gas: 1092391) +VerifiedMarketTest:testMultipleFixedBorrowsRepays() (gas: 1585506) +VerifiedMarketTest:testMultipleLiquidationSameUser() (gas: 3460071) +VerifiedMarketTest:testNonCollateralMarketAddsNoBorrowingPower() (gas: 1137287) +VerifiedMarketTest:testNonCollateralMarketShouldNotBeSeized() (gas: 1834385) +VerifiedMarketTest:testNotEnteredMarketShouldNotBeSeized() (gas: 11346770) +VerifiedMarketTest:testOnlyAdminCanFreezeUnfreeze() (gas: 228418) +VerifiedMarketTest:testOperationsShouldUpdateFloatingAssetsAverage() (gas: 1793851) +VerifiedMarketTest:testOperationsWithBtcWbtcRate() (gas: 11099851) +VerifiedMarketTest:testOperationsWithStEthAsset() (gas: 10989190) +VerifiedMarketTest:testPausable() (gas: 183215) +VerifiedMarketTest:testPauserRole() (gas: 88344) +VerifiedMarketTest:testPreviewOperationsWithSmartPoolCorrectlyAccountingEarnings() (gas: 2449487) +VerifiedMarketTest:testRepayAtMaturity() (gas: 687640) +VerifiedMarketTest:testRepayDisagreement() (gas: 697422) +VerifiedMarketTest:testRepayFlexibleBorrow() (gas: 1390318) +VerifiedMarketTest:testRepayWhenFrozen() (gas: 594354) +VerifiedMarketTest:testRoundingDownAssetsToValidateShortfallWhenTransferring() (gas: 9611594) +VerifiedMarketTest:testRoundingDownAssetsToValidateShortfallWhenTransferringFrom() (gas: 9666962) +VerifiedMarketTest:testRoundingDownAssetsWhenTransferingFromAnAccountWithoutShortfall() (gas: 1199606) +VerifiedMarketTest:testRoundingDownAssetsWhenTransferingWithAnAccountWithoutShortfall() (gas: 1141678) +VerifiedMarketTest:testRoundingUpAllowanceWhenBorrowingAtMaturity() (gas: 950563) +VerifiedMarketTest:testRoundingUpAllowanceWhenWithdrawingAtMaturity() (gas: 962632) +VerifiedMarketTest:testSetDampSpeedFactorShouldUpdateFloatingAssetsAverage() (gas: 457126) +VerifiedMarketTest:testSetEarningsAccumulatorSmoothFactorShouldDistributeEarnings() (gas: 964666) +VerifiedMarketTest:testSetInterestRateModelShouldUpdateFloatingDebt() (gas: 2889427) +VerifiedMarketTest:testSetInterestRateModelWithAddressZeroShouldNotUpdateFloatingDebt() (gas: 2678347) +VerifiedMarketTest:testShareValueNotDecreasingAfterDeposit() (gas: 855789) +VerifiedMarketTest:testShareValueNotDecreasingWhenMintingToTreasury() (gas: 894698) +VerifiedMarketTest:testSingleFloatingBorrow() (gas: 455343) +VerifiedMarketTest:testSingleFloatingRepay() (gas: 543696) +VerifiedMarketTest:testSmartPoolEarningsDistribution() (gas: 1317107) +VerifiedMarketTest:testSmartPoolSharesDoNotAccountUnassignedEarningsFromMoreThanOneIntervalPastMaturities() (gas: 716361) +VerifiedMarketTest:testSumDebtPlusEffectsShouldntRoundUpWhenWithdrawing() (gas: 1172470) +VerifiedMarketTest:testTotalAssetsProjectingBackupEarningsCorrectly() (gas: 630050) +VerifiedMarketTest:testTotalAssetsProjectingFloatingDebtCorrectly() (gas: 903019) +VerifiedMarketTest:testTransferNonCollateralMarketWithBadHealthFactor() (gas: 1683401) +VerifiedMarketTest:testUpdateAccumulatedEarningsFactorToZero() (gas: 1917955) +VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenDepositingAndBorrowingContinuously() (gas: 468159) +VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenDepositingRightBeforeBorrow() (gas: 800255) +VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenDepositingRightBeforeEarlyWithdraw() (gas: 783160) +VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenDepositingSomeSecondsBeforeBorrow() (gas: 1131081) +VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenWithdrawingRightBeforeBorrow() (gas: 804311) +VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenWithdrawingRightBeforeEarlyWithdraw() (gas: 787239) +VerifiedMarketTest:testUpdateFloatingAssetsAverageWhenWithdrawingSomeSecondsBeforeBorrow() (gas: 572872) +VerifiedMarketTest:testUpdateFloatingAssetsAverageWithDampSpeedDown() (gas: 477866) +VerifiedMarketTest:testUpdateFloatingAssetsAverageWithDampSpeedUp() (gas: 341631) +VerifiedMarketTest:testUpdateFloatingDebtBeforeSettingTreasury() (gas: 115175) +VerifiedMarketTest:testWithdrawAtMaturity() (gas: 383893) +VerifiedMarketTest:testWithdrawFromSmartPool() (gas: 323535) +VerifiedMarketTest:testWithdrawNonCollateralMarketWithBadHealthFactor() (gas: 1614071) +VerifiedMarketTest:testWithdrawShouldUpdateFlexibleBorrowVariables() (gas: 999904) +VerifiedMarketTest:testWithdrawWhenFrozen() (gas: 361337) +VerifiedMarketTest:test_borrowAtMaturity_borrows_whenBorrowerIsAllowed() (gas: 563456) +VerifiedMarketTest:test_borrowAtMaturity_reverts_withNotAllowed_whenBorrowerIsNotAllowed() (gas: 519670) +VerifiedMarketTest:test_borrow_borrows_whenBorrowerIsAllowed() (gas: 437415) +VerifiedMarketTest:test_borrow_reverts_withNotAllowed_whenBorrowerIsNotAllowed() (gas: 387780) +VerifiedMarketTest:test_depositAtMaturity_deposits_whenSenderAndReceiverAreAllowed() (gas: 487788) +VerifiedMarketTest:test_depositAtMaturity_reverts_withNotAllowed_whenReceiverIsNotAllowed() (gas: 92719) +VerifiedMarketTest:test_depositAtMaturity_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 88649) +VerifiedMarketTest:test_deposit_allowsDepositUpToMaxSupply() (gas: 396252) +VerifiedMarketTest:test_deposit_deposits_whenSenderAndReceiverAreAllowed() (gas: 471498) +VerifiedMarketTest:test_deposit_deposits_whenTotalSupplyLowerThanMaxSupply() (gas: 247877) +VerifiedMarketTest:test_deposit_revert_withNotAllowed_whenBothSenderAndReceiverAreNotAllowed() (gas: 173743) +VerifiedMarketTest:test_deposit_reverts_whenTotalSupplyHigherThanMaxSupply() (gas: 183868) +VerifiedMarketTest:test_deposit_reverts_withNotAllowed_whenReceiverIsNotAllowed() (gas: 92301) +VerifiedMarketTest:test_deposit_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 173917) +VerifiedMarketTest:test_liquidateAllowedAccount_liquidates_withIncentives() (gas: 1447902) +VerifiedMarketTest:test_liquidateNotAllowedAccount_liquidates_withoutIncentives() (gas: 1404300) +VerifiedMarketTest:test_liquidateNotAllowedAccount_underwater_liquidates_withoutIncentives() (gas: 1404242) +VerifiedMarketTest:test_liquidate_reverts_withNotAllowed_whenLiquidatorIsNotAllowed() (gas: 1063420) +VerifiedMarketTest:test_lock_emits_locked() (gas: 433972) +VerifiedMarketTest:test_lock_emits_seize() (gas: 434520) +VerifiedMarketTest:test_lock_locks_whenAccountHasFixedDeposits() (gas: 575868) +VerifiedMarketTest:test_lock_locks_whenAccountHasNoDebt() (gas: 556645) +VerifiedMarketTest:test_lock_reverts_withNotAuditor_whenNotCalledByAuditor() (gas: 35026) +VerifiedMarketTest:test_lock_reverts_withRemainingDebt_whenAccountHasDebt() (gas: 596886) +VerifiedMarketTest:test_mint_mints_whenSenderAndReceiverAreAllowed() (gas: 395275) VerifiedMarketTest:test_mint_revert_withNotAllowed_whenBothSenderAndReceiverAreNotAllowed() (gas: 173698) -VerifiedMarketTest:test_mint_reverts_withNotAllowed_whenReceiverIsNotAllowed() (gas: 92191) -VerifiedMarketTest:test_mint_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 173828) -VerifiedMarketTest:test_redeem_redeems_whenSenderIsAllowed() (gas: 361059) -VerifiedMarketTest:test_redeem_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 322930) -VerifiedMarketTest:test_refund_refunds_whenBorrowerAndRefunderAreAllowed() (gas: 600100) -VerifiedMarketTest:test_refund_reverts_withNotAllowed_whenBorrowerIsNotAllowed() (gas: 537771) -VerifiedMarketTest:test_refund_reverts_withNotAllowed_whenBothBorrowerAndRefunderAreNotAllowed() (gas: 560515) -VerifiedMarketTest:test_refund_reverts_withNotAllowed_whenRefunderIsNotAllowed() (gas: 533333) -VerifiedMarketTest:test_repayAtMaturity_repays_whenBorrowerAndRepayerAreAllowed() (gas: 769565) -VerifiedMarketTest:test_repayAtMaturity_reverts_withNotAllowed_whenBorrowerIsNotAllowed() (gas: 664851) -VerifiedMarketTest:test_repayAtMaturity_reverts_withNotAllowed_whenBothBorrowerAndRepayerAreNotAllowed() (gas: 728476) -VerifiedMarketTest:test_repayAtMaturity_reverts_withNotAllowed_whenRepayerIsNotAllowed() (gas: 701087) -VerifiedMarketTest:test_repay_repays_whenBorrowerAndRepayerAreAllowed() (gas: 574142) -VerifiedMarketTest:test_repay_reverts_withNotAllowed_whenBorrowerIsNotAllowed() (gas: 537763) -VerifiedMarketTest:test_repay_reverts_withNotAllowed_whenBothBorrowerAndRepayerAreNotAllowed() (gas: 520694) -VerifiedMarketTest:test_repay_reverts_withNotAllowed_whenRepayerIsNotAllowed() (gas: 573958) +VerifiedMarketTest:test_mint_reverts_withNotAllowed_whenReceiverIsNotAllowed() (gas: 92257) +VerifiedMarketTest:test_mint_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 173894) +VerifiedMarketTest:test_redeem_redeems_whenSenderIsAllowed() (gas: 361780) +VerifiedMarketTest:test_redeem_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 323069) +VerifiedMarketTest:test_refund_refunds_whenBorrowerAndRefunderAreAllowed() (gas: 600382) +VerifiedMarketTest:test_refund_reverts_withNotAllowed_whenBorrowerIsNotAllowed() (gas: 538031) +VerifiedMarketTest:test_refund_reverts_withNotAllowed_whenBothBorrowerAndRefunderAreNotAllowed() (gas: 560797) +VerifiedMarketTest:test_refund_reverts_withNotAllowed_whenRefunderIsNotAllowed() (gas: 533679) +VerifiedMarketTest:test_repayAtMaturity_repays_whenBorrowerAndRepayerAreAllowed() (gas: 769806) +VerifiedMarketTest:test_repayAtMaturity_reverts_withNotAllowed_whenBorrowerIsNotAllowed() (gas: 665199) +VerifiedMarketTest:test_repayAtMaturity_reverts_withNotAllowed_whenBothBorrowerAndRepayerAreNotAllowed() (gas: 728735) +VerifiedMarketTest:test_repayAtMaturity_reverts_withNotAllowed_whenRepayerIsNotAllowed() (gas: 701369) +VerifiedMarketTest:test_repay_repays_whenBorrowerAndRepayerAreAllowed() (gas: 574490) +VerifiedMarketTest:test_repay_reverts_withNotAllowed_whenBorrowerIsNotAllowed() (gas: 538067) +VerifiedMarketTest:test_repay_reverts_withNotAllowed_whenBothBorrowerAndRepayerAreNotAllowed() (gas: 520953) +VerifiedMarketTest:test_repay_reverts_withNotAllowed_whenRepayerIsNotAllowed() (gas: 574306) VerifiedMarketTest:test_setMaxSupply_emits_maxSupplySet() (gas: 44327) VerifiedMarketTest:test_setMaxSupply_reverts_whenNotAdmin() (gas: 65094) -VerifiedMarketTest:test_setMaxSupply_sets_whenAdmin() (gas: 66315) -VerifiedMarketTest:test_transferFrom_reverts_withNotAllowed_whenBothSenderAndReceiverAreNotAllowed() (gas: 310068) -VerifiedMarketTest:test_transferFrom_reverts_withNotAllowed_whenReceiverIsNotAllowed() (gas: 282658) -VerifiedMarketTest:test_transferFrom_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 333302) -VerifiedMarketTest:test_transferFrom_transfers_whenSenderAndReceiverAreAllowed() (gas: 447227) -VerifiedMarketTest:test_transfer_reverts_withNotAllowed_whenBothSenderAndReceiverAreNotAllowed() (gas: 309569) -VerifiedMarketTest:test_transfer_reverts_withNotAllowed_whenReceiverIsNotAllowed() (gas: 282182) -VerifiedMarketTest:test_transfer_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 332909) -VerifiedMarketTest:test_transfer_transfers_whenSenderAndReceiverAreAllowed() (gas: 396372) -VerifiedMarketTest:test_unlock_afterOperating_unlocks() (gas: 1964663) -VerifiedMarketTest:test_unlock_emits_unlocked() (gas: 662641) -VerifiedMarketTest:test_unlock_reverts_withNotAuditor_whenNotCalledByAuditor() (gas: 35009) -VerifiedMarketTest:test_unlock_unlocks_whenAccountIsAllowedBack() (gas: 858967) -VerifiedMarketTest:test_unlock_updatesFloatingAssets() (gas: 693742) -VerifiedMarketTest:test_withdrawAtMaturity_reverts_withNotAllowed_whenOwnerIsNotAllowed() (gas: 364077) +VerifiedMarketTest:test_setMaxSupply_sets_whenAdmin() (gas: 66272) +VerifiedMarketTest:test_transferFrom_reverts_withNotAllowed_whenBothSenderAndReceiverAreNotAllowed() (gas: 310178) +VerifiedMarketTest:test_transferFrom_reverts_withNotAllowed_whenReceiverIsNotAllowed() (gas: 282769) +VerifiedMarketTest:test_transferFrom_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 333463) +VerifiedMarketTest:test_transferFrom_transfers_whenSenderAndReceiverAreAllowed() (gas: 447881) +VerifiedMarketTest:test_transfer_reverts_withNotAllowed_whenBothSenderAndReceiverAreNotAllowed() (gas: 309657) +VerifiedMarketTest:test_transfer_reverts_withNotAllowed_whenReceiverIsNotAllowed() (gas: 282226) +VerifiedMarketTest:test_transfer_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 333092) +VerifiedMarketTest:test_transfer_transfers_whenSenderAndReceiverAreAllowed() (gas: 397048) +VerifiedMarketTest:test_unlock_afterOperating_unlocks() (gas: 1965660) +VerifiedMarketTest:test_unlock_emits_unlocked() (gas: 662713) +VerifiedMarketTest:test_unlock_reverts_withNotAuditor_whenNotCalledByAuditor() (gas: 34987) +VerifiedMarketTest:test_unlock_unlocks_whenAccountIsAllowedBack() (gas: 859060) +VerifiedMarketTest:test_unlock_updatesFloatingAssets() (gas: 693817) +VerifiedMarketTest:test_unwind_isStranded_whenDisallowedHasDebtAndOnlyNonCollateralSupply() (gas: 1629927) +VerifiedMarketTest:test_unwind_liquidatesAndLocks_whenDisallowedIsHealthyWithCollateralOnly() (gas: 1796322) +VerifiedMarketTest:test_unwind_liquidatesAndLocks_whenDisallowedIsHealthyWithMixedSupply() (gas: 1272095) +VerifiedMarketTest:test_unwind_liquidatesClearsBadDebtAndLocks_whenDisallowedIsUnderwaterWithCollateralOnly() (gas: 2982736) +VerifiedMarketTest:test_unwind_liquidatesClearsBadDebtAndLocks_whenDisallowedIsUnderwaterWithMixedSupply() (gas: 3309488) +VerifiedMarketTest:test_unwind_locks_whenDisallowedHasOnlyNonCollateralSupplyAndNoDebt() (gas: 772575) +VerifiedMarketTest:test_unwind_socializesDebt_whenDisallowedHasDebtAndOnlyNonCollateralSupply() (gas: 2309596) +VerifiedMarketTest:test_withdrawAtMaturity_reverts_withNotAllowed_whenOwnerIsNotAllowed() (gas: 364208) VerifiedMarketTest:test_withdrawAtMaturity_withdraws_whenOwnerIsAllowed() (gas: 407904) -VerifiedMarketTest:test_withdraw_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 284908) -VerifiedMarketTest:test_withdraw_withdraws_whenSenderIsAllowed() (gas: 367585) +VerifiedMarketTest:test_withdraw_reverts_withNotAllowed_whenSenderIsNotAllowed() (gas: 285025) +VerifiedMarketTest:test_withdraw_withdraws_whenSenderIsAllowed() (gas: 368216) VotePreviewerTest:testExternalVotes() (gas: 428373) \ No newline at end of file diff --git a/contracts/Auditor.sol b/contracts/Auditor.sol index 669962bde..bc09e0210 100644 --- a/contracts/Auditor.sol +++ b/contracts/Auditor.sol @@ -65,6 +65,7 @@ contract Auditor is Initializable, AccessControlUpgradeable { function enterMarket(Market market) external { MarketData storage m = markets[market]; if (!m.isListed) revert MarketNotListed(); + if (m.nonCollateral) revert MarketNotCollateral(); uint256 marketMap = accountMarkets[msg.sender]; uint256 marketMask = 1 << m.index; @@ -129,7 +130,9 @@ contract Auditor is Initializable, AccessControlUpgradeable { vars.price = assetPrice(m.priceFeed); // sum all the collateral prices - sumCollateral += vars.balance.mulDivDown(vars.price, baseUnit).mulWadDown(adjustFactor); + if (!m.nonCollateral) { + sumCollateral += vars.balance.mulDivDown(vars.price, baseUnit).mulWadDown(adjustFactor); + } // sum all the debt sumDebtPlusEffects += vars.borrowBalance.mulDivUp(vars.price, baseUnit).divWadUp(adjustFactor); @@ -138,7 +141,7 @@ contract Auditor is Initializable, AccessControlUpgradeable { if (market == marketToSimulate) { // calculate the effects of redeeming markets // (having less collateral is the same as having more debt for this calculation) - if (withdrawAmount != 0) { + if (withdrawAmount != 0 && !m.nonCollateral) { sumDebtPlusEffects += withdrawAmount.mulDivDown(vars.price, baseUnit).mulWadDown(adjustFactor); } } @@ -179,8 +182,8 @@ contract Auditor is Initializable, AccessControlUpgradeable { /// @param account address of the account to check for possible shortfall. /// @param amount amount that the account wants to withdraw or transfer. function checkShortfall(Market market, address account, uint256 amount) public view virtual { - // if the account is not 'in' the market, bypass the liquidity check - if ((accountMarkets[account] & (1 << markets[market].index)) == 0) return; + // bypass the liquidity check if the account is not 'in' the market or it is disabled as collateral + if (accountMarkets[account] & (1 << markets[market].index) == 0 || markets[market].nonCollateral) return; // otherwise, perform a hypothetical liquidity check to guard against shortfall (uint256 collateral, uint256 debt) = accountLiquidity(account, market, amount); @@ -224,10 +227,12 @@ contract Auditor is Initializable, AccessControlUpgradeable { base.totalDebt += value; base.adjustedDebt += value.divWadUp(m.adjustFactor); - value = collateral.mulDivDown(m.price, m.baseUnit); - base.totalCollateral += value; - base.adjustedCollateral += value.mulWadDown(m.adjustFactor); - if (market == seizeMarket) base.seizeAvailable = value; + if (!marketData.nonCollateral) { + value = collateral.mulDivDown(m.price, m.baseUnit); + base.totalCollateral += value; + base.adjustedCollateral += value.mulWadDown(m.adjustFactor); + if (market == seizeMarket) base.seizeAvailable = value; + } } unchecked { ++i; @@ -329,8 +334,10 @@ contract Auditor is Initializable, AccessControlUpgradeable { if (marketMap & 1 != 0) { Market market = marketList[i]; MarketData storage m = markets[market]; - uint256 assets = market.maxWithdraw(account); - if (assets.mulDivDown(assetPrice(m.priceFeed), 10 ** m.decimals).mulWadDown(m.adjustFactor) > 0) return; + if (!m.nonCollateral) { + uint256 assets = market.maxWithdraw(account); + if (assets.mulDivDown(assetPrice(m.priceFeed), 10 ** m.decimals).mulWadDown(m.adjustFactor) > 0) return; + } } unchecked { ++i; @@ -368,10 +375,12 @@ contract Auditor is Initializable, AccessControlUpgradeable { /// @param market market to add to the protocol. /// @param priceFeed address of Chainlink's Price Feed aggregator used to query the asset price in base. /// @param adjustFactor market's adjust factor for the underlying asset. + /// @param nonCollateral true to list the market as ineligible as collateral. function enableMarket( Market market, IPriceFeed priceFeed, - uint128 adjustFactor + uint128 adjustFactor, + bool nonCollateral ) external onlyRole(DEFAULT_ADMIN_ROLE) { if (market.auditor() != this) revert AuditorMismatch(); if (markets[market].isListed) revert MarketAlreadyListed(); @@ -383,7 +392,8 @@ contract Auditor is Initializable, AccessControlUpgradeable { adjustFactor: adjustFactor, decimals: decimals, index: uint8(marketList.length), - priceFeed: priceFeed + priceFeed: priceFeed, + nonCollateral: nonCollateral }); marketList.push(market); @@ -391,6 +401,8 @@ contract Auditor is Initializable, AccessControlUpgradeable { emit MarketListed(market, decimals); emit PriceFeedSet(market, priceFeed); emit AdjustFactorSet(market, adjustFactor); + + if (nonCollateral) emit NonCollateralSet(market, true); } /// @notice Sets the adjust factor for a certain market. @@ -403,6 +415,17 @@ contract Auditor is Initializable, AccessControlUpgradeable { emit AdjustFactorSet(market, adjustFactor); } + /// @notice Sets whether a market's floating supply is ineligible as collateral. + /// @dev Making a market that accounts use as collateral non-collateral may leave their positions liquidatable. + /// @param market address of the market to change collateral eligibility for. + /// @param nonCollateral true to make the market ineligible as collateral. + function setNonCollateral(Market market, bool nonCollateral) external onlyRole(DEFAULT_ADMIN_ROLE) { + if (!markets[market].isListed) revert MarketNotListed(); + + markets[market].nonCollateral = nonCollateral; + emit NonCollateralSet(market, nonCollateral); + } + /// @notice Sets the Chainlink Price Feed Aggregator source for a market. /// @param market market address of the asset. /// @param priceFeed address of Chainlink's Price Feed aggregator used to query the asset price in base. @@ -437,6 +460,11 @@ contract Auditor is Initializable, AccessControlUpgradeable { /// @param account address of the account that just left a market. event MarketExited(Market indexed market, address indexed account); + /// @notice Emitted when a market's collateral eligibility is changed by admin. + /// @param market address of the market whose collateral eligibility changed. + /// @param nonCollateral true if the market is now ineligible as collateral. + event NonCollateralSet(Market indexed market, bool nonCollateral); + /// @notice Emitted when a adjust factor is changed by admin. /// @param market address of the market that has a new adjust factor. /// @param adjustFactor adjust factor for the underlying asset. @@ -457,12 +485,15 @@ contract Auditor is Initializable, AccessControlUpgradeable { /// @param index index of the market in the `marketList`. /// @param isListed true if the market is enabled. /// @param priceFeed address of the price feed used to query the asset's price. + /// @param nonCollateral true if the market's floating supply is ineligible as collateral. + // solhint-disable-next-line gas-struct-packing struct MarketData { uint128 adjustFactor; uint8 decimals; uint8 index; bool isListed; IPriceFeed priceFeed; + bool nonCollateral; } /// @notice Stores the liquidator and lenders factors used in liquidations to calculate the amount to seize. @@ -490,6 +521,7 @@ error InsufficientShortfall(); error InvalidPrice(); error InvalidPriceFeed(); error MarketAlreadyListed(); +error MarketNotCollateral(); error MarketNotListed(); error NotMarket(); error RemainingDebt(); diff --git a/contracts/periphery/DebtManager.sol b/contracts/periphery/DebtManager.sol index d49084b3e..ad004a098 100644 --- a/contracts/periphery/DebtManager.sol +++ b/contracts/periphery/DebtManager.sol @@ -496,7 +496,7 @@ contract DebtManager is Initializable { } function checkMarket(Market market) internal view { - (, , , bool listed, ) = auditor.markets(market); + (, , , bool listed, , ) = auditor.markets(market); if (!listed) revert MarketNotListed(); } diff --git a/contracts/periphery/DebtPreviewer.sol b/contracts/periphery/DebtPreviewer.sol index afbd70dd2..8608f4576 100644 --- a/contracts/periphery/DebtPreviewer.sol +++ b/contracts/periphery/DebtPreviewer.sol @@ -65,8 +65,9 @@ contract DebtPreviewer { ) internal view returns (uint256) { MinDepositVars memory vars; Auditor auditor = debtManager.auditor(); - (vars.adjustFactorIn, vars.decimalsIn, , , vars.priceFeedIn) = auditor.markets(marketDeposit); - (vars.adjustFactorOut, vars.decimalsOut, , , vars.priceFeedOut) = auditor.markets(marketBorrow); + (vars.adjustFactorIn, vars.decimalsIn, , , vars.priceFeedIn, vars.nonCollateral) = auditor.markets(marketDeposit); + if (vars.nonCollateral) return 0; + (vars.adjustFactorOut, vars.decimalsOut, , , vars.priceFeedOut, ) = auditor.markets(marketBorrow); return minHealthFactor @@ -182,8 +183,8 @@ contract DebtPreviewer { uint256 amountIn ) internal view returns (uint256) { Auditor auditor = debtManager.auditor(); - (, uint256 decimalsOut, , , IPriceFeed priceFeedOut) = auditor.markets(marketBorrow); - (, uint256 decimalsIn, , , IPriceFeed priceFeedIn) = auditor.markets(marketDeposit); + (, uint256 decimalsOut, , , IPriceFeed priceFeedOut, ) = auditor.markets(marketBorrow); + (, uint256 decimalsIn, , , IPriceFeed priceFeedIn, ) = auditor.markets(marketDeposit); return amountIn.mulDivDown(auditor.assetPrice(priceFeedIn), 10 ** decimalsIn).mulDivDown( 10 ** decimalsOut, @@ -206,8 +207,9 @@ contract DebtPreviewer { ) internal view returns (uint256) { Auditor auditor = debtManager.auditor(); MaxRatioVars memory mr; - (mr.adjustFactorIn, , , , mr.priceFeedIn) = auditor.markets(marketDeposit); - (mr.adjustFactorOut, , , , ) = auditor.markets(marketBorrow); + (mr.adjustFactorIn, , , , mr.priceFeedIn, mr.nonCollateral) = auditor.markets(marketDeposit); + if (mr.nonCollateral) return 1e18; + (mr.adjustFactorOut, , , , , ) = auditor.markets(marketBorrow); uint256 isolatedMaxRatio = minHealthFactor.divWadDown( minHealthFactor - mr.adjustFactorIn.mulWadDown(mr.adjustFactorOut) ); @@ -220,7 +222,7 @@ contract DebtPreviewer { Auditor.MarketData memory md; Auditor.AccountLiquidity memory vars; mr.market = auditor.marketList(mr.i); - (md.adjustFactor, md.decimals, , , md.priceFeed) = auditor.markets(mr.market); + (md.adjustFactor, md.decimals, , , md.priceFeed, md.nonCollateral) = auditor.markets(mr.market); (vars.balance, vars.borrowBalance) = mr.market.accountSnapshot(account); vars.price = auditor.assetPrice(md.priceFeed); mr.baseUnit = 10 ** md.decimals; @@ -232,7 +234,7 @@ contract DebtPreviewer { } else { mr.adjustedDebt += vars.borrowBalance.mulDivUp(vars.price, mr.baseUnit).divWadUp(md.adjustFactor); } - if (mr.market != marketDeposit) { + if (mr.market != marketDeposit && !md.nonCollateral) { mr.adjustedCollateral += vars.balance.mulDivDown(vars.price, mr.baseUnit).mulWadDown(md.adjustFactor); } } @@ -241,14 +243,19 @@ contract DebtPreviewer { } } + uint256 collateral = mr.adjustedCollateral.mulWadDown(mr.adjustFactorOut) + + minHealthFactor.mulWadDown(mr.principalUSD); + uint256 debt = minHealthFactor.mulWadDown(mr.adjustedDebt.mulWadDown(mr.adjustFactorOut)); + if (debt >= collateral) return 1e18; return - Math.min( - (mr.adjustedCollateral.mulWadDown(mr.adjustFactorOut) + - minHealthFactor.mulWadDown(mr.principalUSD) - - minHealthFactor.mulWadDown(mr.adjustedDebt.mulWadDown(mr.adjustFactorOut))).divWadDown( + Math.max( + Math.min( + (collateral - debt).divWadDown( mr.principalUSD.mulWadDown(minHealthFactor - mr.adjustFactorIn.mulWadDown(mr.adjustFactorOut)) ), - isolatedMaxRatio + isolatedMaxRatio + ), + 1e18 ); } @@ -275,6 +282,8 @@ contract DebtPreviewer { if (mw.principal <= 0) return 0; mw.auditor = debtManager.auditor(); + (, , , , , mw.nonCollateral) = mw.auditor.markets(marketDeposit); + if (mw.nonCollateral) return uint256(mw.principal); Auditor.MarketData memory md; Auditor.AccountLiquidity memory vars; mw.marketMap = mw.auditor.accountMarkets(account); @@ -282,12 +291,14 @@ contract DebtPreviewer { for (mw.i = 0; mw.marketMap != 0; mw.marketMap >>= 1) { if (mw.marketMap & 1 != 0) { mw.market = mw.auditor.marketList(mw.i); - (md.adjustFactor, md.decimals, , , md.priceFeed) = mw.auditor.markets(mw.market); + (md.adjustFactor, md.decimals, , , md.priceFeed, md.nonCollateral) = mw.auditor.markets(mw.market); uint256 baseUnit = 10 ** md.decimals; (vars.balance, vars.borrowBalance) = mw.market.accountSnapshot(account); vars.price = mw.auditor.assetPrice(md.priceFeed); { - mw.memAdjColl = vars.balance.mulDivDown(vars.price, baseUnit).mulWadDown(md.adjustFactor); + mw.memAdjColl = md.nonCollateral + ? 0 + : vars.balance.mulDivDown(vars.price, baseUnit).mulWadDown(md.adjustFactor); mw.memAdjDebt = vars.borrowBalance.mulDivDown(vars.price, baseUnit).divWadDown(md.adjustFactor); mw.adjustedCollateral += mw.memAdjColl; @@ -312,13 +323,14 @@ contract DebtPreviewer { } } { - (mw.adjustFactorIn, , , , mw.priceFeedIn) = mw.auditor.markets(marketDeposit); - (mw.adjustFactorOut, , , , ) = mw.auditor.markets(marketBorrow); + (mw.adjustFactorIn, , , , mw.priceFeedIn, ) = mw.auditor.markets(marketDeposit); + (mw.adjustFactorOut, , , , , ) = mw.auditor.markets(marketBorrow); mw.memOtherDebt = mw.otherDebt.mulWadDown(mw.adjustFactorOut).mulWadDown(minHealthFactor); mw.memOtherCollateral = (mw.otherCollateral).mulWadDown(mw.adjustFactorOut); } if (mw.memOtherDebt <= mw.memOtherCollateral) { + if (mw.adjustedDebt + mw.adjPrincipalForRepay >= mw.adjustedCollateral + mw.adjustedRepay) return 0; return Math.min( Math @@ -332,15 +344,14 @@ contract DebtPreviewer { ); } - return - uint256(mw.principal) - - (mw.memOtherDebt - mw.memOtherCollateral) - .divWadDown( - mw.adjustFactorIn.mulWadDown(ratio).mulWadDown(mw.adjustFactorOut) + - minHealthFactor - - ratio.mulWadDown(minHealthFactor) - ) - .mulDivDown(10 ** marketDeposit.decimals(), mw.auditor.assetPrice(mw.priceFeedIn)); + uint256 support = mw.adjustFactorIn.mulWadDown(ratio).mulWadDown(mw.adjustFactorOut) + minHealthFactor; + uint256 ratioDebt = ratio.mulWadDown(minHealthFactor); + if (ratioDebt >= support) return 0; + uint256 requiredPrincipal = (mw.memOtherDebt - mw.memOtherCollateral).divWadDown(support - ratioDebt).mulDivDown( + 10 ** marketDeposit.decimals(), + mw.auditor.assetPrice(mw.priceFeedIn) + ); + return uint256(mw.principal) > requiredPrincipal ? uint256(mw.principal) - requiredPrincipal : 0; } /// @notice Calculates the crossed principal amount for a given `account` in the input and output markets. @@ -353,8 +364,8 @@ contract DebtPreviewer { IPriceFeed priceFeedIn; IPriceFeed priceFeedOut; Auditor auditor = debtManager.auditor(); - (, decimalsIn, , , priceFeedIn) = auditor.markets(marketDeposit); - (, decimalsOut, , , priceFeedOut) = auditor.markets(marketBorrow); + (, decimalsIn, , , priceFeedIn, ) = auditor.markets(marketDeposit); + (, decimalsOut, , , priceFeedOut, ) = auditor.markets(marketBorrow); return int256(marketDeposit.maxWithdraw(account)) - @@ -469,7 +480,7 @@ contract DebtPreviewer { r.controller = market.rewardsController(); Auditor auditor = debtManager.auditor(); if (address(r.controller) != address(0)) { - (, r.underlyingDecimals, , , r.underlyingPriceFeed) = auditor.markets(market); + (, r.underlyingDecimals, , , r.underlyingPriceFeed, ) = auditor.markets(market); unchecked { r.underlyingBaseUnit = 10 ** r.underlyingDecimals; } @@ -578,6 +589,7 @@ struct Limit { struct MaxRatioVars { uint256 i; + bool nonCollateral; uint256 baseUnit; uint256 marketMap; uint256 principalUSD; @@ -592,6 +604,7 @@ struct MaxRatioVars { struct MaxWithdrawVars { uint256 i; int256 principal; + bool nonCollateral; uint256 marketMap; uint256 otherDebt; uint256 memAdjDebt; @@ -619,6 +632,7 @@ struct MinDepositVars { IPriceFeed priceFeedIn; uint256 adjustFactorOut; IPriceFeed priceFeedOut; + bool nonCollateral; } struct Rates { diff --git a/contracts/periphery/DebtRoller.sol b/contracts/periphery/DebtRoller.sol index 16c6fe9af..351f0f670 100644 --- a/contracts/periphery/DebtRoller.sol +++ b/contracts/periphery/DebtRoller.sol @@ -99,7 +99,7 @@ contract DebtRoller is IFlashLoanRecipient, Initializable, AccessControlUpgradea } function _checkMarket(Market market) internal view { - (, , , bool listed, ) = auditor.markets(market); + (, , , bool listed, , ) = auditor.markets(market); if (!listed) revert NotMarket(); } } diff --git a/contracts/periphery/InstallmentsRouter.sol b/contracts/periphery/InstallmentsRouter.sol index 012faecba..19ef75a5b 100644 --- a/contracts/periphery/InstallmentsRouter.sol +++ b/contracts/periphery/InstallmentsRouter.sol @@ -128,7 +128,7 @@ contract InstallmentsRouter { /// @notice Reverts if the Market is not listed by the Auditor. /// @param market The Market to check. function checkMarket(Market market) internal view { - (, , , bool listed, ) = auditor.markets(market); + (, , , bool listed, , ) = auditor.markets(market); if (!listed) revert MarketNotListed(); } diff --git a/contracts/periphery/IntegrationPreviewer.sol b/contracts/periphery/IntegrationPreviewer.sol index 53d7af5b2..9e250b44d 100644 --- a/contracts/periphery/IntegrationPreviewer.sol +++ b/contracts/periphery/IntegrationPreviewer.sol @@ -50,12 +50,18 @@ contract IntegrationPreviewer { int256 debtDelta ) public view returns (uint256) { (uint256 adjustedCollateral, uint256 adjustedDebt) = auditor.accountLiquidity(account, market, 0); - (uint256 adjustFactor, uint256 decimals, , , IPriceFeed priceFeed) = auditor.markets(market); - uint256 price = auditor.assetPrice(priceFeed); - uint256 absAdjustedCollateralDelta = collateralDelta.abs().mulDiv(price, 10 ** decimals).mulWad(adjustFactor); - if (collateralDelta < 0) adjustedCollateral -= absAdjustedCollateralDelta; - else adjustedCollateral += absAdjustedCollateralDelta; - uint256 absAdjustedDebtDelta = debtDelta.abs().mulDivUp(price, 10 ** decimals).divWadUp(adjustFactor); + (uint256 adjustFactor, uint256 decimals, , , IPriceFeed priceFeed, bool nonCollateral) = auditor.markets(market); + if (!nonCollateral) { + uint256 absAdjustedCollateralDelta = collateralDelta + .abs() + .mulDiv(auditor.assetPrice(priceFeed), 10 ** decimals) + .mulWad(adjustFactor); + if (collateralDelta < 0) adjustedCollateral -= absAdjustedCollateralDelta; + else adjustedCollateral += absAdjustedCollateralDelta; + } + uint256 absAdjustedDebtDelta = debtDelta.abs().mulDivUp(auditor.assetPrice(priceFeed), 10 ** decimals).divWadUp( + adjustFactor + ); if (debtDelta < 0) adjustedDebt -= absAdjustedDebtDelta; else adjustedDebt += absAdjustedDebtDelta; if (adjustedDebt == 0) return type(uint256).max; @@ -73,7 +79,7 @@ contract IntegrationPreviewer { /// @return maxAssets The maximum extra underlying assets that can be borrowed safely. function borrowLimit(address account, Market market, uint256 targetHealthFactor) external view returns (uint256) { (uint256 adjustedCollateral, uint256 adjustedDebt) = auditor.accountLiquidity(account, market, 0); - (uint256 adjustFactor, uint256 decimals, , , IPriceFeed priceFeed) = auditor.markets(market); + (uint256 adjustFactor, uint256 decimals, , , IPriceFeed priceFeed, ) = auditor.markets(market); uint256 maxAdjustedDebt = adjustedCollateral.divWad(targetHealthFactor); if (adjustedDebt >= maxAdjustedDebt) return 0; uint256 maxExtraDebt = maxAdjustedDebt - adjustedDebt; diff --git a/contracts/periphery/Previewer.sol b/contracts/periphery/Previewer.sol index fb77217b3..caed5636e 100644 --- a/contracts/periphery/Previewer.sol +++ b/contracts/periphery/Previewer.sol @@ -130,7 +130,7 @@ contract Previewer { Market.Account memory a; Auditor.MarketData memory m; (a.fixedDeposits, a.fixedBorrows, a.floatingBorrowShares) = market.accounts(account); - (m.adjustFactor, m.decimals, m.index, m.isListed, m.priceFeed) = auditor.markets(market); + (m.adjustFactor, m.decimals, m.index, m.isListed, m.priceFeed, m.nonCollateral) = auditor.markets(market); IRM irm = market.interestRateModel(); data[i] = MarketAccount({ // market @@ -161,7 +161,7 @@ contract Previewer { totalFloatingBorrowShares: market.totalFloatingBorrowShares(), totalFloatingDepositShares: market.totalSupply(), // account - isCollateral: markets & (1 << i) != 0 ? true : false, + isCollateral: markets & (1 << i) != 0 && !m.nonCollateral, maxBorrowAssets: adjustedCollateral >= adjustedDebt ? (adjustedCollateral - adjustedDebt).mulDivUp(10 ** m.decimals, auditor.assetPrice(m.priceFeed)).mulWadUp( m.adjustFactor @@ -462,7 +462,7 @@ contract Previewer { RewardsVars memory r; r.controller = market.rewardsController(); if (address(r.controller) != address(0)) { - (, r.underlyingDecimals, , , r.underlyingPriceFeed) = auditor.markets(market); + (, r.underlyingDecimals, , , r.underlyingPriceFeed, ) = auditor.markets(market); unchecked { r.underlyingBaseUnit = 10 ** r.underlyingDecimals; } diff --git a/deploy/Markets.ts b/deploy/Markets.ts index 383de6e43..802017bff 100644 --- a/deploy/Markets.ts +++ b/deploy/Markets.ts @@ -185,8 +185,18 @@ const func: DeployFunction = async ({ await (await market.deposit(fiveUSDAssets, DEAD_ADDRESS)).wait(); } const adjustFactor = parseUnits(String(config.adjustFactor)); + const nonCollateral = config.nonCollateral ?? false; + const supportsNonCollateral = auditor.interface.hasFunction("setNonCollateral"); + if (config.nonCollateral !== undefined && !supportsNonCollateral) { + throw new Error(`${marketName}: nonCollateral is configured but the auditor deployment predates it`); + } if (!(await auditor.allMarkets()).includes(market.target as string)) { - await executeOrPropose(auditor, "enableMarket", [market.target, priceFeed, adjustFactor]); + await executeOrPropose(auditor, "enableMarket", [ + market.target, + priceFeed, + adjustFactor, + ...(supportsNonCollateral ? [nonCollateral] : []), + ]); } else { if ((await auditor.markets(market.target)).priceFeed.toLowerCase() !== priceFeed.toLowerCase()) { await executeOrPropose(auditor, "setPriceFeed", [market.target, priceFeed]); @@ -194,6 +204,9 @@ const func: DeployFunction = async ({ if ((await auditor.markets(market.target)).adjustFactor !== adjustFactor) { await executeOrPropose(auditor, "setAdjustFactor", [market.target, adjustFactor]); } + if (supportsNonCollateral && (await auditor.markets(market.target)).nonCollateral !== nonCollateral) { + await executeOrPropose(auditor, "setNonCollateral", [market.target, nonCollateral]); + } } const marketRewards = await market.rewardsController?.().catch(() => undefined); diff --git a/hardhat.config.ts b/hardhat.config.ts index 7f604d869..38099af04 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -36,6 +36,10 @@ const hardhatConfig: Config = { ...compiler, settings: { ...compiler.settings, optimizer: { ...compiler.settings.optimizer, runs: 70 } }, }, + "contracts/periphery/Previewer.sol": { + ...compiler, + settings: { ...compiler.settings, optimizer: { ...compiler.settings.optimizer, runs: 70 } }, + }, }, }, networks: { @@ -547,6 +551,7 @@ declare module "hardhat/types/config" { export interface MarketConfig { frozen?: boolean; + nonCollateral?: boolean; adjustFactor: number; priceFeed?: "double" | { wrapper: string; fn: string; baseUnit: bigint }; penaltyRatePerDay: number; diff --git a/test/Auditor.t.sol b/test/Auditor.t.sol index 3fe1b1cad..4accc062c 100644 --- a/test/Auditor.t.sol +++ b/test/Auditor.t.sol @@ -12,7 +12,10 @@ import { RemainingDebt, AuditorMismatch, InvalidPriceFeed, + MarketNotListed, MarketAlreadyListed, + MarketNotCollateral, + InsufficientShortfall, InsufficientAccountLiquidity } from "../contracts/Auditor.sol"; @@ -28,6 +31,7 @@ contract AuditorTest is Test { event MarketListed(Market indexed market, uint8 decimals); event MarketEntered(Market indexed market, address indexed account); event MarketExited(Market indexed market, address indexed account); + event NonCollateralSet(Market indexed market, bool disabled); function setUp() external { auditor = Auditor(address(new ERC1967Proxy(address(new Auditor(18)), ""))); @@ -42,13 +46,19 @@ contract AuditorTest is Test { vm.expectEmit(true, true, true, true, address(auditor)); emit MarketListed(Market(address(market)), 18); - auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); - (uint256 adjustFactor, uint8 decimals, uint8 index, bool isListed, IPriceFeed oraclePriceFeed) = auditor.markets( - Market(address(market)) - ); + ( + uint256 adjustFactor, + uint8 decimals, + uint8 index, + bool isListed, + IPriceFeed oraclePriceFeed, + bool nonCollateral + ) = auditor.markets(Market(address(market))); Market[] memory markets = auditor.allMarkets(); assertTrue(isListed); + assertFalse(nonCollateral); assertEq(address(oraclePriceFeed), address(priceFeed)); assertEq(index, 0); assertEq(decimals, 18); @@ -60,12 +70,12 @@ contract AuditorTest is Test { function testEnableMarketShouldRevertWithInvalidPriceFeed() external { MockPriceFeed invalidPriceFeed = new MockPriceFeed(8, 1e8); vm.expectRevert(InvalidPriceFeed.selector); - auditor.enableMarket(Market(address(market)), invalidPriceFeed, 0.8e18); + auditor.enableMarket(Market(address(market)), invalidPriceFeed, 0.8e18, false); } function testEnterExitMarket() external { market.setCollateral(1 ether); - auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); vm.expectEmit(true, false, false, true, address(auditor)); emit MarketEntered(Market(address(market)), address(this)); @@ -86,7 +96,7 @@ contract AuditorTest is Test { Market[] memory markets = new Market[](4); for (uint8 i = 0; i < markets.length; i++) { markets[i] = Market(address(new MockMarket(auditor, 18))); - auditor.enableMarket(markets[i], priceFeed, 0.8e18); + auditor.enableMarket(markets[i], priceFeed, 0.8e18, false); auditor.enterMarket(markets[i]); } @@ -98,7 +108,7 @@ contract AuditorTest is Test { } function testExitMarketOwning() external { - auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); auditor.enterMarket(Market(address(market))); market.setDebt(1); vm.expectRevert(RemainingDebt.selector); @@ -106,25 +116,63 @@ contract AuditorTest is Test { } function testEnableMarketAlreadyListed() external { - auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); vm.expectRevert(MarketAlreadyListed.selector); - auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); } function testEnableMarketAuditorMismatch() external { market.setAuditor(address(0)); vm.expectRevert(AuditorMismatch.selector); - auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); + } + + function testEnableMarketNonCollateral() external { + vm.expectEmit(true, true, true, true, address(auditor)); + emit NonCollateralSet(Market(address(market)), true); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, true); + + (, , , , , bool nonCollateral) = auditor.markets(Market(address(market))); + assertTrue(nonCollateral); + vm.expectRevert(MarketNotCollateral.selector); + auditor.enterMarket(Market(address(market))); + } + + function testSetNonCollateral() external { + market.setCollateral(1 ether); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); + auditor.enterMarket(Market(address(market))); + + vm.expectEmit(true, true, true, true, address(auditor)); + emit NonCollateralSet(Market(address(market)), true); + auditor.setNonCollateral(Market(address(market)), true); + (, , , , , bool nonCollateral) = auditor.markets(Market(address(market))); + assertTrue(nonCollateral); + (uint256 collateral, ) = auditor.accountLiquidity(address(this), Market(address(0)), 0); + assertEq(collateral, 0); + + vm.expectEmit(true, true, true, true, address(auditor)); + emit NonCollateralSet(Market(address(market)), false); + auditor.setNonCollateral(Market(address(market)), false); + (, , , , , nonCollateral) = auditor.markets(Market(address(market))); + assertFalse(nonCollateral); + (collateral, ) = auditor.accountLiquidity(address(this), Market(address(0)), 0); + assertEq(collateral, uint256(1 ether).mulWadDown(0.8e18)); + } + + function testSetNonCollateralNotListed() external { + vm.expectRevert(MarketNotListed.selector); + auditor.setNonCollateral(Market(address(market)), true); } function testBorrowMPValidation() external { - auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); auditor.enterMarket(Market(address(market))); auditor.checkBorrow(Market(address(market)), address(this)); } function testBorrowMPValidationRevert() external { - auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); auditor.enterMarket(Market(address(market))); market.setDebt(1); vm.expectRevert(InsufficientAccountLiquidity.selector); @@ -132,13 +180,13 @@ contract AuditorTest is Test { } function testAccountShortfall() external { - auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); auditor.enterMarket(Market(address(market))); auditor.checkShortfall(Market(address(market)), address(this), 1); } function testAccountShortfallRevert() external { - auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); auditor.enterMarket(Market(address(market))); market.setDebt(1); vm.expectRevert(InsufficientAccountLiquidity.selector); @@ -149,7 +197,7 @@ contract AuditorTest is Test { Market[] memory markets = new Market[](4); for (uint8 i = 0; i < markets.length; i++) { markets[i] = Market(address(new MockMarket(auditor, 18 - (i * 3)))); - auditor.enableMarket(markets[i], priceFeed, 0.9e18 - (i * 0.1e18)); + auditor.enableMarket(markets[i], priceFeed, 0.9e18 - (i * 0.1e18), false); vm.prank(BOB); auditor.enterMarket(markets[i]); @@ -159,12 +207,154 @@ contract AuditorTest is Test { MockMarket(address(markets[3])).setCollateral(1e9); auditor.checkLiquidation(markets[1], markets[3], BOB, type(uint256).max); } + + function testSetNonCollateralOnlyAdmin() external { + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); + vm.prank(BOB); + vm.expectRevert(bytes("")); + auditor.setNonCollateral(Market(address(market)), true); + } + + function testEnableSecondMarketNonCollateral() external { + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); + MockMarket secondMarket = new MockMarket(auditor, 18); + auditor.enableMarket(Market(address(secondMarket)), priceFeed, 0.8e18, true); + (, , , , , bool firstDisabled) = auditor.markets(Market(address(market))); + (, , , , , bool secondDisabled) = auditor.markets(Market(address(secondMarket))); + assertFalse(firstDisabled); + assertTrue(secondDisabled); + + auditor.enterMarket(Market(address(market))); + vm.expectRevert(MarketNotCollateral.selector); + auditor.enterMarket(Market(address(secondMarket))); + } + + function testCheckShortfallBypassesNonCollateralMarket() external { + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); + auditor.enterMarket(Market(address(market))); + market.setCollateral(1 ether); + market.setDebt(1 ether); + + vm.expectRevert(InsufficientAccountLiquidity.selector); + auditor.checkShortfall(Market(address(market)), address(this), 1); + + auditor.setNonCollateral(Market(address(market)), true); + auditor.checkShortfall(Market(address(market)), address(this), 1); + } + + function testAccountLiquiditySkipsWithdrawSimulationOfNonCollateralMarket() external { + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); + auditor.enterMarket(Market(address(market))); + market.setCollateral(1 ether); + + (uint256 collateral, uint256 debt) = auditor.accountLiquidity(address(this), Market(address(market)), 0.5 ether); + assertEq(collateral, uint256(1 ether).mulWadDown(0.8e18)); + assertEq(debt, uint256(0.5 ether).mulWadDown(0.8e18)); + + auditor.setNonCollateral(Market(address(market)), true); + (collateral, debt) = auditor.accountLiquidity(address(this), Market(address(market)), 0.5 ether); + assertEq(collateral, 0); + assertEq(debt, 0); + } + + function testExitNonCollateralMarketWithShortfall() external { + MockMarket debtMarket = new MockMarket(auditor, 18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); + auditor.enableMarket(Market(address(debtMarket)), priceFeed, 0.8e18, false); + auditor.enterMarket(Market(address(market))); + auditor.enterMarket(Market(address(debtMarket))); + market.setCollateral(1 ether); + debtMarket.setDebt(0.5 ether); + + vm.expectRevert(InsufficientAccountLiquidity.selector); + auditor.exitMarket(Market(address(market))); + + auditor.setNonCollateral(Market(address(market)), true); + auditor.exitMarket(Market(address(market))); + assertEq(auditor.accountMarkets(address(this)) & 1, 0); + } + + function testCheckLiquidationSkipsNonCollateralMarkets() external { + MockMarket debtMarket = new MockMarket(auditor, 18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); + auditor.enableMarket(Market(address(debtMarket)), priceFeed, 0.8e18, false); + auditor.enterMarket(Market(address(market))); + auditor.enterMarket(Market(address(debtMarket))); + market.setCollateral(1 ether); + debtMarket.setDebt(0.5 ether); + + // account is healthy while the collateral is eligible + vm.expectRevert(InsufficientShortfall.selector); + auditor.checkLiquidation(Market(address(debtMarket)), Market(address(market)), address(this), type(uint256).max); + + // with its only collateral disabled the account has no total collateral and can't be liquidated + auditor.setNonCollateral(Market(address(market)), true); + vm.expectRevert(bytes("")); + auditor.checkLiquidation(Market(address(debtMarket)), Market(address(market)), address(this), type(uint256).max); + } + + function testCheckLiquidationZeroRepayForNonCollateralSeizeMarket() external { + MockMarket seizeMarket = new MockMarket(auditor, 18); + MockMarket debtMarket = new MockMarket(auditor, 18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); + auditor.enableMarket(Market(address(seizeMarket)), priceFeed, 0.8e18, false); + auditor.enableMarket(Market(address(debtMarket)), priceFeed, 0.8e18, false); + auditor.enterMarket(Market(address(market))); + auditor.enterMarket(Market(address(seizeMarket))); + auditor.enterMarket(Market(address(debtMarket))); + market.setCollateral(0.1 ether); + seizeMarket.setCollateral(1 ether); + debtMarket.setDebt(1 ether); + + auditor.setNonCollateral(Market(address(seizeMarket)), true); + + // shortfall exists but nothing can be seized from the disabled market + uint256 maxRepay = auditor.checkLiquidation( + Market(address(debtMarket)), + Market(address(seizeMarket)), + address(this), + type(uint256).max + ); + assertEq(maxRepay, 0); + + // seizing from the enabled market is still allowed + maxRepay = auditor.checkLiquidation( + Market(address(debtMarket)), + Market(address(market)), + address(this), + type(uint256).max + ); + assertGt(maxRepay, 0); + } + + function testHandleBadDebtIgnoresNonCollateralMarkets() external { + MockMarket debtMarket = new MockMarket(auditor, 18); + auditor.enableMarket(Market(address(market)), priceFeed, 0.8e18, false); + auditor.enableMarket(Market(address(debtMarket)), priceFeed, 0.8e18, false); + auditor.enterMarket(Market(address(market))); + auditor.enterMarket(Market(address(debtMarket))); + market.setCollateral(1 ether); + debtMarket.setDebt(1 ether); + + // positive eligible collateral blocks bad debt clearing + auditor.handleBadDebt(address(this)); + assertEq(debtMarket.clearBadDebtCalls(), 0); + + // once disabled as collateral, the supply no longer blocks and is not consumed by the clearing + auditor.setNonCollateral(Market(address(market)), true); + auditor.handleBadDebt(address(this)); + assertEq(debtMarket.clearBadDebtCalls(), 1); + assertEq(market.clearBadDebtCalls(), 1); + (uint256 collateral, ) = market.accountSnapshot(address(this)); + assertEq(collateral, 1 ether); + } } contract MockMarket { Auditor public auditor; uint256 internal collateral; uint256 internal debt; + uint256 public clearBadDebtCalls; uint8 public immutable decimals; constructor(Auditor auditor_, uint8 decimals_) { @@ -187,4 +377,13 @@ contract MockMarket { function accountSnapshot(address) external view returns (uint256, uint256) { return (collateral, debt); } + + function maxWithdraw(address) external view returns (uint256) { + return collateral; + } + + function clearBadDebt(address) external { + debt = 0; + ++clearBadDebtCalls; + } } diff --git a/test/AuditorUpgrade.t.sol b/test/AuditorUpgrade.t.sol new file mode 100644 index 000000000..be745f93f --- /dev/null +++ b/test/AuditorUpgrade.t.sol @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.17; + +import { FixedPointMathLib } from "solmate/src/utils/FixedPointMathLib.sol"; + +import { ForkTest } from "./Fork.t.sol"; +import { Market, ERC20, FixedLib } from "../contracts/Market.sol"; +import { Auditor, IPriceFeed, MarketNotCollateral } from "../contracts/Auditor.sol"; + +contract AuditorUpgradeForkTest is ForkTest { + using FixedPointMathLib for uint256; + + Auditor internal auditor; + address internal timelock; + Market[] internal markets; + MarketState[] internal states; + uint256 internal liquidatorIncentive; + uint256 internal lendersIncentive; + + struct MarketState { + uint128 adjustFactor; + uint8 decimals; + uint8 index; + bool isListed; + IPriceFeed priceFeed; + uint256 totalAssets; + uint256 totalSupply; + uint256 totalFloatingBorrowAssets; + } + + function setUp() public { + vm.createSelectFork(vm.envString("OPTIMISM_NODE"), 155_130_000); + auditor = Auditor(deployment("Auditor")); + timelock = deployment("TimelockController"); + + Market[] memory allMarkets = auditor.allMarkets(); + for (uint256 i = 0; i < allMarkets.length; ++i) { + Market market = allMarkets[i]; + markets.push(market); + // the deployed auditor still has the 5-field getter, so state is captured through the legacy ABI + (uint128 adjustFactor, uint8 decimals, uint8 index, bool isListed, IPriceFeed priceFeed) = ILegacyAuditor( + address(auditor) + ).markets(market); + states.push( + MarketState({ + adjustFactor: adjustFactor, + decimals: decimals, + index: index, + isListed: isListed, + priceFeed: priceFeed, + totalAssets: market.totalAssets(), + totalSupply: market.totalSupply(), + totalFloatingBorrowAssets: market.totalFloatingBorrowAssets() + }) + ); + } + (liquidatorIncentive, lendersIncentive) = auditor.liquidationIncentive(); + + upgrade(address(auditor), address(new Auditor(auditor.priceDecimals()))); + } + + function testUpgradePreservesState() external view { + Market[] memory allMarkets = auditor.allMarkets(); + assertEq(allMarkets.length, markets.length); + (uint256 liquidator, uint256 lenders) = auditor.liquidationIncentive(); + assertEq(liquidator, liquidatorIncentive); + assertEq(lenders, lendersIncentive); + + for (uint256 i = 0; i < markets.length; ++i) { + assertEq(address(allMarkets[i]), address(markets[i])); + ( + uint128 adjustFactor, + uint8 decimals, + uint8 index, + bool isListed, + IPriceFeed priceFeed, + bool nonCollateral + ) = auditor.markets(markets[i]); + assertEq(adjustFactor, states[i].adjustFactor); + assertEq(decimals, states[i].decimals); + assertEq(index, states[i].index); + assertTrue(isListed); + assertEq(address(priceFeed), address(states[i].priceFeed)); + assertFalse(nonCollateral); + assertEq(markets[i].totalAssets(), states[i].totalAssets); + assertEq(markets[i].totalSupply(), states[i].totalSupply); + assertEq(markets[i].totalFloatingBorrowAssets(), states[i].totalFloatingBorrowAssets); + } + } + + function testLegacyMarketsGetterAfterUpgrade() external view { + // integrators compiled before `nonCollateral` was appended keep decoding the getter + for (uint256 i = 0; i < markets.length; ++i) { + (uint128 adjustFactor, uint8 decimals, uint8 index, bool isListed, IPriceFeed priceFeed) = ILegacyAuditor( + address(auditor) + ).markets(markets[i]); + assertEq(adjustFactor, states[i].adjustFactor); + assertEq(decimals, states[i].decimals); + assertEq(index, states[i].index); + assertTrue(isListed); + assertEq(address(priceFeed), address(states[i].priceFeed)); + } + } + + function testMarketsOperateAfterUpgrade() external { + for (uint256 i = 0; i < markets.length; ++i) { + Market market = markets[i]; + if (market.isFrozen()) continue; // exaUSDC.e is frozen at the pinned block + + ERC20 asset = market.asset(); + uint256 baseUnit = 10 ** states[i].decimals; + address account = makeAddr(string.concat("account", vm.toString(i))); + deal(address(asset), account, 2_000 * baseUnit); + + vm.startPrank(account); + asset.approve(address(market), type(uint256).max); + market.deposit(1_000 * baseUnit, account); + auditor.enterMarket(market); + // let `floatingAssetsAverage` catch up with the deposit so fixed rates stay sane + vm.warp(block.timestamp + 1 hours); + uint256 maturity = block.timestamp - (block.timestamp % FixedLib.INTERVAL) + FixedLib.INTERVAL; + + (uint256 collateral, uint256 debt) = auditor.accountLiquidity(account, Market(address(0)), 0); + assertGt(collateral, 0, "deposit not counted as collateral"); + assertEq(debt, 0, "debt before borrowing"); + + uint256 adjustFactor = states[i].adjustFactor; + uint256 borrowAssets = (1_000 * baseUnit).mulWadDown(adjustFactor).mulWadDown(adjustFactor) / 4; + market.borrow(borrowAssets, account, account); + market.borrowAtMaturity(maturity, borrowAssets, type(uint256).max, account, account); + (, debt) = auditor.accountLiquidity(account, Market(address(0)), 0); + assertGt(debt, 0, "debt after borrowing"); + + (uint256 principal, uint256 fee) = market.fixedBorrowPositions(maturity, account); + deal(address(asset), account, asset.balanceOf(account) + principal + fee); + (, , uint256 floatingBorrowShares) = market.accounts(account); + market.refund(floatingBorrowShares, account); + market.repayAtMaturity(maturity, type(uint256).max, type(uint256).max, account); + market.redeem(market.balanceOf(account), account, account); + auditor.exitMarket(market); + vm.stopPrank(); + + (collateral, debt) = auditor.accountLiquidity(account, Market(address(0)), 0); + assertEq(collateral, 0, "collateral after exiting"); + assertEq(debt, 0, "debt after repaying"); + assertEq(market.balanceOf(account), 0, "shares after redeeming"); + } + } + + function testSetNonCollateralAfterUpgrade() external { + Market market = markets[0]; // exaWETH + uint256 baseUnit = 10 ** states[0].decimals; + address account = makeAddr("depositor"); + deal(address(market.asset()), account, 1_000 * baseUnit); + + vm.startPrank(account); + market.asset().approve(address(market), type(uint256).max); + market.deposit(1_000 * baseUnit, account); + auditor.enterMarket(market); + vm.stopPrank(); + + (uint256 collateral, ) = auditor.accountLiquidity(account, Market(address(0)), 0); + assertGt(collateral, 0, "deposit not counted as collateral"); + + vm.prank(timelock); + auditor.setNonCollateral(market, true); + (, , , , , bool nonCollateral) = auditor.markets(market); + assertTrue(nonCollateral); + + (collateral, ) = auditor.accountLiquidity(account, Market(address(0)), 0); + assertEq(collateral, 0, "non-collateral market still counted as collateral"); + + address stranger = makeAddr("stranger"); + vm.prank(stranger); + vm.expectRevert(MarketNotCollateral.selector); + auditor.enterMarket(market); + + // supply stays freely withdrawable while the account is still in the market + vm.startPrank(account); + market.redeem(market.balanceOf(account) / 2, account, account); + vm.stopPrank(); + + vm.prank(timelock); + auditor.setNonCollateral(market, false); + (, , , , , nonCollateral) = auditor.markets(market); + assertFalse(nonCollateral); + (collateral, ) = auditor.accountLiquidity(account, Market(address(0)), 0); + assertGt(collateral, 0, "restored market not counted as collateral"); + } + + function testBorrowFromNonCollateralMarketAfterUpgrade() external { + Market collateralMarket = markets[0]; // exaWETH + Market borrowMarket = markets[2]; // exaOP + + vm.prank(timelock); + auditor.setNonCollateral(borrowMarket, true); + + address account = makeAddr("borrower"); + deal(address(collateralMarket.asset()), account, 1_000 * 10 ** states[0].decimals); + deal(address(borrowMarket.asset()), account, 10 * 10 ** states[2].decimals); + + vm.startPrank(account); + collateralMarket.asset().approve(address(collateralMarket), type(uint256).max); + collateralMarket.deposit(1_000 * 10 ** states[0].decimals, account); + auditor.enterMarket(collateralMarket); + (uint256 collateral, ) = auditor.accountLiquidity(account, Market(address(0)), 0); + + // depositing in the non-collateral market adds no borrowing power + borrowMarket.asset().approve(address(borrowMarket), type(uint256).max); + borrowMarket.deposit(10 * 10 ** states[2].decimals, account); + (uint256 newCollateral, ) = auditor.accountLiquidity(account, Market(address(0)), 0); + assertEq(newCollateral, collateral, "non-collateral market deposit added borrowing power"); + + // the non-collateral market stays fully borrowable against regular collateral + uint256 borrowAssets = collateral.mulWadDown(states[2].adjustFactor).mulDivDown( + 10 ** states[2].decimals, + auditor.assetPrice(states[2].priceFeed) + ) / 2; + if (borrowAssets > borrowMarket.totalAssets() / 10) borrowAssets = borrowMarket.totalAssets() / 10; + borrowMarket.borrow(borrowAssets, account, account); + vm.stopPrank(); + + assertNotEq(auditor.accountMarkets(account) & (1 << states[2].index), 0, "borrow did not auto-enter the market"); + (, uint256 debt) = auditor.accountLiquidity(account, Market(address(0)), 0); + assertGt(debt, 0, "borrowed debt not accounted"); + } +} + +/// @dev The `markets` getter as integrators compiled before `MarketData.nonCollateral` was appended see it, +/// which is also the ABI of the deployments before the upgrade. +interface ILegacyAuditor { + function markets(Market market) external view returns (uint128, uint8, uint8, bool, IPriceFeed); +} diff --git a/test/DebtManager.t.sol b/test/DebtManager.t.sol index 6f8701c58..da445f0b1 100644 --- a/test/DebtManager.t.sol +++ b/test/DebtManager.t.sol @@ -15,7 +15,7 @@ import { InvalidOperation, AllowanceSurplus } from "../contracts/periphery/DebtManager.sol"; -import { Auditor, InsufficientAccountLiquidity, MarketNotListed, IPriceFeed } from "../contracts/Auditor.sol"; +import { Auditor, IPriceFeed, MarketNotListed, InsufficientAccountLiquidity } from "../contracts/Auditor.sol"; import { FixedLib, UnmatchedPoolState } from "../contracts/utils/FixedLib.sol"; import { MockBalancerVault } from "../contracts/mocks/MockBalancerVault.sol"; import { Market, Disagreement, ZeroRepay } from "../contracts/Market.sol"; @@ -56,6 +56,7 @@ contract DebtManagerTest is ForkTest { marketWETH = Market(deployment("MarketWETH")); marketwstETH = Market(deployment("MarketwstETH")); auditor = Auditor(deployment("Auditor")); + upgrade(address(auditor), address(new Auditor(auditor.priceDecimals()))); permit2 = IPermit2(deployment("Permit2")); debtManager = DebtManager( address( @@ -184,6 +185,61 @@ contract DebtManagerTest is ForkTest { assertApproxEqAbs(marketUSDC.previewRefund(floatingBorrowShares), principal.mulWadDown(ratio - 1e18), 1); } + function testLeverageWithNonCollateralMarket() external _checkBalances { + vm.prank(deployment("TimelockController")); + auditor.setNonCollateral(marketUSDC, true); + + // the deposit provides no borrowing power, so the borrow leg reverts in the auditor + vm.expectRevert(InsufficientAccountLiquidity.selector); + debtManager.leverage(marketUSDC, 100_000e6, 4e18); + } + + function testDeleverageWithNonCollateralMarket() external _checkBalances { + uint256 principal = 100_000e6; + debtManager.leverage(marketUSDC, principal, 4e18); + + vm.prank(deployment("TimelockController")); + auditor.setNonCollateral(marketUSDC, true); + + // partial deleverage leaves debt unbacked by the non-collateral deposit and still succeeds + debtManager.deleverage(marketUSDC, 0, 2e18); + (, , uint256 floatingBorrowShares) = marketUSDC.accounts(address(this)); + assertApproxEqAbs(marketUSDC.previewRefund(floatingBorrowShares), principal, 3); + assertApproxEqAbs(marketUSDC.maxWithdraw(address(this)), principal.mulWadDown(2e18), 9); + + debtManager.deleverage(marketUSDC, 0, 1e18); + (, , floatingBorrowShares) = marketUSDC.accounts(address(this)); + assertEq(marketUSDC.previewRefund(floatingBorrowShares), 0); + assertApproxEqAbs(marketUSDC.maxWithdraw(address(this)), principal, 7); + + // non-collateral supply is freely withdrawable once the debt is repaid + marketUSDC.withdraw(marketUSDC.maxWithdraw(address(this)), address(this), address(this)); + assertEq(marketUSDC.maxWithdraw(address(this)), 0); + } + + function testRollBorrowFromNonCollateralMarket() external _checkBalances { + weth.approve(address(marketWETH), type(uint256).max); + marketWETH.deposit(100 ether, address(this)); + auditor.enterMarket(marketWETH); + marketUSDC.borrow(50_000e6, address(this), address(this)); + + vm.prank(deployment("TimelockController")); + auditor.setNonCollateral(marketUSDC, true); + + // roll operations are debt-side only and stay available on non-collateral markets + debtManager.rollFloatingToFixed(marketUSDC, maturity, type(uint256).max, 1e18); + (uint256 principal, ) = marketUSDC.fixedBorrowPositions(maturity, address(this)); + assertGt(principal, 0); + + debtManager.rollFixed(marketUSDC, maturity, targetMaturity, type(uint256).max, type(uint256).max, 1e18); + (principal, ) = marketUSDC.fixedBorrowPositions(targetMaturity, address(this)); + assertGt(principal, 0); + + debtManager.rollFixedToFloating(marketUSDC, targetMaturity, type(uint256).max, 1e18); + (principal, ) = marketUSDC.fixedBorrowPositions(targetMaturity, address(this)); + assertEq(principal, 0); + } + function testLeverageWithNegativePrincipal() external _checkBalances { weth.approve(address(marketWETH), type(uint256).max); marketWETH.deposit(10 ether, address(this)); @@ -1074,8 +1130,8 @@ contract DebtManagerTest is ForkTest { } function crossPrincipal(Market marketIn, Market marketOut, address sender) internal view returns (uint256) { - (, , , , IPriceFeed priceFeedIn) = auditor.markets(marketIn); - (, , , , IPriceFeed priceFeedOut) = auditor.markets(marketOut); + (, , , , IPriceFeed priceFeedIn, ) = auditor.markets(marketIn); + (, , , , IPriceFeed priceFeedOut, ) = auditor.markets(marketOut); (, , uint256 floatingBorrowShares) = marketOut.accounts(sender); uint256 collateral = marketIn.maxWithdraw(sender); @@ -1087,8 +1143,8 @@ contract DebtManagerTest is ForkTest { } function previewAssetsOut(Market marketIn, Market marketOut, uint256 amountIn) internal view returns (uint256) { - (, , , , IPriceFeed priceFeedIn) = auditor.markets(marketIn); - (, , , , IPriceFeed priceFeedOut) = auditor.markets(marketOut); + (, , , , IPriceFeed priceFeedIn, ) = auditor.markets(marketIn); + (, , , , IPriceFeed priceFeedOut, ) = auditor.markets(marketOut); return amountIn.mulDivDown(auditor.assetPrice(priceFeedIn), 10 ** marketIn.decimals()).mulDivDown( diff --git a/test/DebtPreviewer.t.sol b/test/DebtPreviewer.t.sol index e5466a838..cb861b39c 100644 --- a/test/DebtPreviewer.t.sol +++ b/test/DebtPreviewer.t.sol @@ -6,6 +6,7 @@ import { FixedPointMathLib } from "solmate/src/utils/FixedPointMathLib.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts-v4/proxy/ERC1967/ERC1967Proxy.sol"; import { Pool, Limit, Rates, Leverage, DebtPreviewer } from "../contracts/periphery/DebtPreviewer.sol"; import { FixedLib } from "../contracts/utils/FixedLib.sol"; +import { MockPriceFeed } from "../contracts/mocks/MockPriceFeed.sol"; import { Market, ERC20, IPermit2, DebtManager, IBalancerVault } from "../contracts/periphery/DebtManager.sol"; import { Auditor, IPriceFeed, InsufficientAccountLiquidity } from "../contracts/Auditor.sol"; @@ -30,6 +31,7 @@ contract DebtPreviewerTest is ForkTest { function setUp() external { vm.createSelectFork(vm.envString("OPTIMISM_NODE"), 99_811_375); auditor = Auditor(deployment("Auditor")); + upgrade(address(auditor), address(new Auditor(auditor.priceDecimals()))); IPermit2 permit2 = IPermit2(deployment("Permit2")); marketOP = Market(deployment("MarketOP")); marketWETH = Market(deployment("MarketWETH")); @@ -72,14 +74,149 @@ contract DebtPreviewerTest is ForkTest { maturity = block.timestamp - (block.timestamp % FixedLib.INTERVAL) + FixedLib.INTERVAL; } + function testLeveragePreviewsWithNonCollateralMarket() external { + debtManager.leverage(marketUSDC, 10_000e6, 2e18); + + vm.prank(deployment("TimelockController")); + auditor.setNonCollateral(marketUSDC, true); + + // previews stay callable and report no leverage capacity instead of reverting + Limit memory limit = debtPreviewer.previewLeverage(marketUSDC, marketUSDC, address(this), 0, 2e18, 1e18); + assertEq(limit.maxRatio, 1e18); + assertApproxEqAbs(uint256(limit.principal), 10_000e6, 3); + assertEq(limit.maxWithdraw, uint256(limit.principal)); + + // `leverageRates` is a pure rate calculation and also serves deleverage previews + Rates memory rates = debtPreviewer.leverageRates(marketUSDC, marketUSDC, address(this), 0, 2e18, 1e17, 0, 0); + assertEq(rates.deposit, uint256(1e17).mulWadDown(2e18)); + assertGt(rates.borrow, 0); + } + + function testLeverageWithNonCollateralMarket() external { + debtManager.leverage(marketUSDC, 10_000e6, 2e18); + + vm.prank(deployment("TimelockController")); + auditor.setNonCollateral(marketUSDC, true); + + // position data stays readable, with zero leverage capacity and freely withdrawable principal + Leverage memory leverage = debtPreviewer.leverage(marketUSDC, marketUSDC, address(this), 1e18); + assertApproxEqAbs(uint256(leverage.principal), 10_000e6, 3); + assertApproxEqAbs(leverage.deposit, 20_000e6, 3); + assertApproxEqAbs(leverage.borrow, 10_000e6, 1); + assertApproxEqAbs(leverage.ratio, 2e18, 3e10); + assertEq(leverage.maxRatio, 1e18); + assertEq(leverage.maxWithdraw, uint256(leverage.principal)); + assertEq(leverage.minDeposit, 0); + } + + function testMinDepositWithNonCollateralMarket() external { + debtManager.leverage(marketUSDC, 10_000e6, 2e18); + + vm.prank(deployment("TimelockController")); + auditor.setNonCollateral(marketUSDC, true); + + marketUSDC.withdraw(15_000e6, address(this), address(this)); + + Leverage memory leverage = debtPreviewer.leverage(marketUSDC, marketUSDC, address(this), 1e18); + assertLt(leverage.principal, 0); + assertEq(leverage.minDeposit, 0); + assertEq(leverage.maxRatio, 1e18); + assertEq(leverage.maxWithdraw, 0); + } + + function testPreviewDeleverageWithNonCollateralMarket() external { + debtManager.leverage(marketUSDC, 100_000e6, 4e18); + + vm.prank(deployment("TimelockController")); + auditor.setNonCollateral(marketUSDC, true); + + Limit memory limit = debtPreviewer.previewDeleverage(marketUSDC, marketUSDC, address(this), 10_000e6, 2e18, 1e18); + assertEq(limit.ratio, 1e18); + assertEq(limit.maxRatio, 1e18); + assertEq(limit.borrow, 0); + assertApproxEqAbs(limit.maxWithdraw, 100_000e6, 5); + + debtManager.deleverage(marketUSDC, 10_000e6, 1e18); + assertApproxEqAbs(marketUSDC.maxWithdraw(address(this)), limit.deposit, 2); + assertEq(floatingBorrowAssets(marketUSDC, address(this)), limit.borrow); + } + + function testPreviewsUnderwaterAccountAfterNonCollateral() external { + marketUSDC.deposit(20_000e6, address(this)); + marketWETH.deposit(1 ether, address(this)); + auditor.enterMarket(marketWETH); + marketOP.borrow(4_000e18, address(this), address(this)); + + vm.prank(deployment("TimelockController")); + auditor.setNonCollateral(marketUSDC, true); + + // the OP debt is no longer covered: previews saturate instead of reverting with underflow + Leverage memory leverage = debtPreviewer.leverage(marketWETH, marketWETH, address(this), 1e18); + assertApproxEqAbs(uint256(leverage.principal), 1 ether, 1); + assertEq(leverage.maxRatio, 1e18); + assertEq(leverage.maxWithdraw, 0); + + Limit memory limit = debtPreviewer.previewDeleverage(marketWETH, marketWETH, address(this), 0, 1e18, 1e18); + assertEq(limit.maxRatio, 1e18); + assertEq(limit.maxWithdraw, 0); + + limit = debtPreviewer.previewLeverage(marketWETH, marketWETH, address(this), 0, 2e18, 1e18); + assertEq(limit.maxRatio, 1e18); + assertEq(limit.maxWithdraw, 0); + } + + function testPreviewsUnderwaterAccountByPrice() external { + marketUSDC.deposit(20_000e6, address(this)); + debtManager.leverage(marketWETH, 1 ether, 2e18); + marketOP.borrow(4_000e18, address(this), address(this)); + + (, , , , IPriceFeed opPriceFeed, ) = auditor.markets(marketOP); + MockPriceFeed newPriceFeed = new MockPriceFeed(opPriceFeed.decimals(), opPriceFeed.latestAnswer() * 10); + vm.prank(deployment("TimelockController")); + auditor.setPriceFeed(marketOP, newPriceFeed); + + // underwater by market conditions, without any collateral toggle: previews saturate instead of reverting + Leverage memory leverage = debtPreviewer.leverage(marketWETH, marketWETH, address(this), 1e18); + assertApproxEqAbs(uint256(leverage.principal), 1 ether, 2); + assertEq(leverage.maxRatio, 1e18); + assertEq(leverage.maxWithdraw, 0); + + Limit memory limit = debtPreviewer.previewDeleverage(marketWETH, marketWETH, address(this), 0, 1e18, 1e18); + assertEq(limit.ratio, 1e18); + assertEq(limit.maxWithdraw, 0); + assertApproxEqAbs(limit.borrow, 0, 1); + + // the auditor still blocks the withdraw leg of an underwater deleverage on a collateral market + vm.expectRevert(InsufficientAccountLiquidity.selector); + debtManager.deleverage(marketWETH, 0, 1e18); + } + + function testPreviewLeverageExcludesNonCollateralMarketBalances() external { + // account holds USDC collateral and debt while depositing on WETH + marketUSDC.deposit(10_000e6, address(this)); + marketUSDC.borrow(2_000e6, address(this), address(this)); + marketWETH.deposit(5 ether, address(this)); + auditor.enterMarket(marketWETH); + + Leverage memory before = debtPreviewer.leverage(marketWETH, marketWETH, address(this), 1e18); + + vm.prank(deployment("TimelockController")); + auditor.setNonCollateral(marketUSDC, true); + + // once USDC is non-collateral its balance must be excluded from maxRatio and maxWithdraw + Leverage memory leverage = debtPreviewer.leverage(marketWETH, marketWETH, address(this), 1e18); + assertLt(leverage.maxRatio, before.maxRatio); + assertLt(leverage.maxWithdraw, before.maxWithdraw); + } + function testPreviewLeverage() external { uint256 ratio = 2e18; uint256 principal = 10_000e6; debtManager.leverage(marketUSDC, principal, ratio); Leverage memory leverage = debtPreviewer.leverage(marketUSDC, marketUSDC, address(this), 1e18); - (uint256 collateralAdjustFactor, , , , ) = auditor.markets(marketUSDC); - (uint256 debtAdjustFactor, , , , ) = auditor.markets(marketUSDC); + (uint256 collateralAdjustFactor, , , , , ) = auditor.markets(marketUSDC); + (uint256 debtAdjustFactor, , , , , ) = auditor.markets(marketUSDC); assertApproxEqAbs(uint256(leverage.principal), principal, 2e18); assertApproxEqAbs(leverage.deposit, principal.mulWadDown(ratio), 1); assertApproxEqAbs(leverage.ratio, ratio, 0.0003e18); @@ -92,8 +229,8 @@ contract DebtPreviewerTest is ForkTest { function testPreviewEmptyLeverage() external view { Leverage memory leverage = debtPreviewer.leverage(marketUSDC, marketUSDC, address(this), 1e18); - (uint256 collateralAdjustFactor, , , , ) = auditor.markets(marketUSDC); - (uint256 debtAdjustFactor, , , , ) = auditor.markets(marketUSDC); + (uint256 collateralAdjustFactor, , , , , ) = auditor.markets(marketUSDC); + (uint256 debtAdjustFactor, , , , , ) = auditor.markets(marketUSDC); assertEq(leverage.principal, 0); assertEq(leverage.deposit, 0); @@ -104,7 +241,7 @@ contract DebtPreviewerTest is ForkTest { function testPreviewLeverageSameAsset() external { Leverage memory leverage = debtPreviewer.leverage(marketUSDC, marketUSDC, address(this), 1e18); - (uint256 adjustFactor, , , , ) = auditor.markets(marketUSDC); + (uint256 adjustFactor, , , , , ) = auditor.markets(marketUSDC); uint256 principal = 1_000e6; uint256 ratio = leverage.maxRatio - 0.0001e18; @@ -431,8 +568,8 @@ contract DebtPreviewerTest is ForkTest { } function crossPrincipal(Market marketDeposit, Market marketBorrow, address account) internal view returns (int256) { - (, , , , IPriceFeed priceFeedIn) = debtManager.auditor().markets(marketDeposit); - (, , , , IPriceFeed priceFeedOut) = debtManager.auditor().markets(marketBorrow); + (, , , , IPriceFeed priceFeedIn, ) = debtManager.auditor().markets(marketDeposit); + (, , , , IPriceFeed priceFeedOut, ) = debtManager.auditor().markets(marketBorrow); uint256 collateral = marketDeposit.maxWithdraw(account); uint256 debt = floatingBorrowAssets(marketBorrow, account) diff --git a/test/DebtRoller.t.sol b/test/DebtRoller.t.sol index b5d78d9fa..f5e228de6 100644 --- a/test/DebtRoller.t.sol +++ b/test/DebtRoller.t.sol @@ -51,7 +51,7 @@ contract DebtRollerTest is Test { vm.label(address(exaUSDC), "exaUSDC"); vm.label(address(usdc), "usdc"); - auditor.enableMarket(exaUSDC, new MockPriceFeed(18, 1e18), 0.8e18); + auditor.enableMarket(exaUSDC, new MockPriceFeed(18, 1e18), 0.8e18, false); mockBalancerVault = new MockBalancerVault(); vm.label(address(mockBalancerVault), "mockBalancerVault"); diff --git a/test/InstallmentsRouter.t.sol b/test/InstallmentsRouter.t.sol index 65cc672cb..668ba6df9 100644 --- a/test/InstallmentsRouter.t.sol +++ b/test/InstallmentsRouter.t.sol @@ -68,8 +68,8 @@ contract InstallmentsRouterTest is Test { ); vm.label(address(marketWETH), "marketWETH"); - auditor.enableMarket(market, new MockPriceFeed(18, 1e18), 0.8e18); - auditor.enableMarket(marketWETH, new MockPriceFeed(18, 1e18), 0.8e18); + auditor.enableMarket(market, new MockPriceFeed(18, 1e18), 0.8e18, false); + auditor.enableMarket(marketWETH, new MockPriceFeed(18, 1e18), 0.8e18, false); router = new InstallmentsRouter(auditor, marketWETH); diff --git a/test/IntegrationPreviewer.t.sol b/test/IntegrationPreviewer.t.sol index f49030fa7..09ca66314 100644 --- a/test/IntegrationPreviewer.t.sol +++ b/test/IntegrationPreviewer.t.sol @@ -109,6 +109,28 @@ contract IntegrationPreviewerTest is ForkTest { exaUSDC.repayAtMaturity(MATURITY, assets, type(uint256).max, USER); assertEq(healthFactor, previewer.healthFactor(USER), "bad health factor after fixed repay"); } + function test_previewHealthFactor_nonCollateralMarket() external { + vm.prank(timelock); + auditor.setNonCollateral(exaUSDC, true); + + uint256 assets = 10e6; + deal(address(usdc), USER, 1_000_000e6); + vm.startPrank(USER); + usdc.approve(address(exaUSDC), type(uint256).max); + + // the collateral delta of a non-collateral market is ignored, the preview must match the actual health factor + uint256 healthFactor = previewer.healthFactor(USER); + assertEq(previewer.previewHealthFactor(USER, exaUSDC, int256(assets), 0), healthFactor); + exaUSDC.deposit(assets, USER); + assertEq(previewer.healthFactor(USER), healthFactor, "bad health factor after deposit"); + + assertEq(previewer.previewHealthFactor(USER, exaUSDC, -int256(assets), 0), healthFactor); + exaUSDC.withdraw(assets, USER, USER); + assertEq(previewer.healthFactor(USER), healthFactor, "bad health factor after withdraw"); + + // the debt delta is still accounted with the ordinary factor + assertLt(previewer.previewHealthFactor(USER, exaUSDC, 0, int256(assets)), healthFactor); + } // #endregion // #region preview operations diff --git a/test/Market.t.sol b/test/Market.t.sol index 6917db2ce..8dcbff679 100644 --- a/test/Market.t.sol +++ b/test/Market.t.sol @@ -7,7 +7,7 @@ import { Test, stdError } from "forge-std/Test.sol"; import { FixedPointMathLib } from "solmate/src/utils/FixedPointMathLib.sol"; import { MockInterestRateModel } from "../contracts/mocks/MockInterestRateModel.sol"; import { MockBorrowRate } from "../contracts/mocks/MockBorrowRate.sol"; -import { Auditor, IPriceFeed, InsufficientAccountLiquidity } from "../contracts/Auditor.sol"; +import { Auditor, IPriceFeed, MarketNotCollateral, InsufficientAccountLiquidity } from "../contracts/Auditor.sol"; import { InterestRateModel, Parameters } from "../contracts/InterestRateModel.sol"; import { PriceFeedWrapper } from "../contracts/PriceFeedWrapper.sol"; import { PriceFeedDouble } from "../contracts/PriceFeedDouble.sol"; @@ -90,8 +90,8 @@ contract MarketTest is Test { ); vm.label(address(marketWETH), "MarketWETH"); - auditor.enableMarket(market, daiPriceFeed, 0.8e18); - auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.9e18); + auditor.enableMarket(market, daiPriceFeed, 0.8e18, false); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.9e18, false); auditor.enterMarket(marketWETH); vm.label(BOB, "Bob"); @@ -569,7 +569,7 @@ contract MarketTest is Test { asset.mint(address(this), 50_000 ether); asset.approve(address(marketHarness), 50_000 ether); marketHarness.approve(BOB, 50_000 ether); - auditor.enableMarket(marketHarness, daiPriceFeed, 0.8e18); + auditor.enableMarket(marketHarness, daiPriceFeed, 0.8e18, false); marketHarness.setFloatingAssets(500 ether); marketHarness.setSupply(2000 ether); @@ -602,7 +602,7 @@ contract MarketTest is Test { uint256 maturity = FixedLib.INTERVAL * 2; asset.mint(address(this), 50_000 ether); asset.approve(address(marketHarness), 50_000 ether); - auditor.enableMarket(marketHarness, daiPriceFeed, 0.8e18); + auditor.enableMarket(marketHarness, daiPriceFeed, 0.8e18, false); marketHarness.setFloatingAssets(500 ether); marketHarness.setSupply(2000 ether); @@ -625,7 +625,7 @@ contract MarketTest is Test { market.borrowAtMaturity(FixedLib.INTERVAL, 100 ether, 100 ether, address(this), address(this)); (uint256 collateral, uint256 debt) = auditor.accountLiquidity(address(this), Market(address(0)), 0); - (uint256 adjustFactor, , , , ) = auditor.markets(market); + (uint256 adjustFactor, , , , , ) = auditor.markets(market); assertEq(collateral, uint256(1_000 ether).mulDivDown(1e18, 10 ** 18).mulWadDown(adjustFactor)); assertEq(collateral, 800 ether); @@ -761,7 +761,7 @@ contract MarketTest is Test { ); vm.label(address(marketUSDC), "MarketUSDC"); MockPriceFeed usdcPriceFeed = new MockPriceFeed(18, 1e18); - auditor.enableMarket(marketUSDC, usdcPriceFeed, 0.8e18); + auditor.enableMarket(marketUSDC, usdcPriceFeed, 0.8e18, false); usdc.mint(address(this), 1_000_000e6); usdc.approve(address(marketUSDC), type(uint256).max); marketUSDC.deposit(10_000e6, address(this)); @@ -786,6 +786,173 @@ contract MarketTest is Test { market.liquidate(address(this), type(uint256).max, marketWETH); } + function testNonCollateralMarketAddsNoBorrowingPower() external { + auditor.enterMarket(market); + market.deposit(1_000 ether, address(this)); + + (uint256 collateral, ) = auditor.accountLiquidity(address(this), Market(address(0)), 0); + assertEq(collateral, uint256(1_000 ether).mulWadDown(0.8e18)); + + auditor.setNonCollateral(market, true); + (collateral, ) = auditor.accountLiquidity(address(this), Market(address(0)), 0); + assertEq(collateral, 0); + + vm.expectRevert(InsufficientAccountLiquidity.selector); + market.borrow(1 ether, address(this), address(this)); + + auditor.setNonCollateral(market, false); + (collateral, ) = auditor.accountLiquidity(address(this), Market(address(0)), 0); + assertEq(collateral, uint256(1_000 ether).mulWadDown(0.8e18)); + market.borrow(1 ether, address(this), address(this)); + } + + function testDepositBorrowRepayWithdrawOnNonCollateralMarket() external { + auditor.setNonCollateral(market, true); + vm.prank(ALICE); + market.deposit(10_000 ether, ALICE); + marketWETH.deposit(10 ether, address(this)); + + // depositing on a non-collateral market works but can't be entered nor adds borrowing power + market.deposit(1_000 ether, address(this)); + vm.expectRevert(MarketNotCollateral.selector); + auditor.enterMarket(market); + (uint256 collateral, uint256 debt) = auditor.accountLiquidity(address(this), Market(address(0)), 0); + assertEq(collateral, uint256(10 ether).mulWadDown(0.9e18)); + assertEq(debt, 0); + + // floating borrow auto-enters the market, setting the bit without adding collateral + market.borrow(2 ether, address(this), address(this)); + assertEq(auditor.accountMarkets(address(this)) & 1, 1); + (collateral, debt) = auditor.accountLiquidity(address(this), Market(address(0)), 0); + assertEq(collateral, uint256(10 ether).mulWadDown(0.9e18)); + assertGt(debt, 0); + + // fixed pool operations still work + market.depositAtMaturity(FixedLib.INTERVAL, 2 ether, 0, address(this)); + market.borrowAtMaturity(FixedLib.INTERVAL, 2 ether, type(uint256).max, address(this), address(this)); + + // repay everything and withdraw both floating and fixed deposits + (, , uint256 floatingBorrowShares) = market.accounts(address(this)); + market.refund(floatingBorrowShares, address(this)); + market.repayAtMaturity(FixedLib.INTERVAL, type(uint256).max, type(uint256).max, address(this)); + market.withdrawAtMaturity(FixedLib.INTERVAL, 2 ether, 0, address(this), address(this)); + market.withdraw(1_000 ether, address(this), address(this)); + (, debt) = auditor.accountLiquidity(address(this), Market(address(0)), 0); + assertEq(debt, 0); + } + + function setUpUnderwaterWithNonCollateralMarket(uint256 wethDeposit) internal { + auditor.enterMarket(market); + market.deposit(1_000 ether, address(this)); + if (wethDeposit > 0) marketWETH.deposit(wethDeposit, address(this)); + weth.mint(BOB, 500 ether); + vm.prank(BOB); + marketWETH.deposit(500 ether, BOB); + marketWETH.borrow(400 ether, address(this), address(this)); + + auditor.setNonCollateral(market, true); + (uint256 collateral, uint256 debt) = auditor.accountLiquidity(address(this), Market(address(0)), 0); + assertLt(collateral, debt); + } + + function testWithdrawNonCollateralMarketWithBadHealthFactor() external { + setUpUnderwaterWithNonCollateralMarket(1 ether); + + // non-collateral supply stays freely withdrawable while underwater + market.withdraw(100 ether, address(this), address(this)); + market.redeem(100 ether, address(this), address(this)); + + // withdrawing actual collateral is still blocked + vm.expectRevert(InsufficientAccountLiquidity.selector); + marketWETH.withdraw(0.1 ether, address(this), address(this)); + } + + function testTransferNonCollateralMarketWithBadHealthFactor() external { + setUpUnderwaterWithNonCollateralMarket(1 ether); + + // non-collateral shares stay freely transferable while underwater + market.transfer(BOB, 100 ether); + market.approve(ALICE, type(uint256).max); + vm.prank(ALICE); + market.transferFrom(address(this), ALICE, 100 ether); + assertEq(market.balanceOf(BOB), 100 ether); + assertEq(market.balanceOf(ALICE), 100 ether); + + // transferring actual collateral is still blocked + vm.expectRevert(InsufficientAccountLiquidity.selector); + marketWETH.transfer(BOB, 0.1 ether); + } + + function testExitNonCollateralMarketWithBadHealthFactor() external { + setUpUnderwaterWithNonCollateralMarket(1 ether); + + auditor.exitMarket(market); + assertEq(auditor.accountMarkets(address(this)) & 1, 0); + } + + function testNonCollateralMarketShouldNotBeSeized() external { + setUpUnderwaterWithNonCollateralMarket(1 ether); + uint256 shares = market.balanceOf(address(this)); + + weth.mint(liquidator, 1_000 ether); + vm.startPrank(liquidator); + weth.approve(address(marketWETH), type(uint256).max); + + // seizing from the non-collateral market is not allowed + vm.expectRevert(ZeroRepay.selector); + marketWETH.liquidate(address(this), type(uint256).max, market); + + // seizing from the enabled collateral market still works + marketWETH.liquidate(address(this), type(uint256).max, marketWETH); + vm.stopPrank(); + + assertEq(market.balanceOf(address(this)), shares); + assertLt(marketWETH.maxWithdraw(address(this)), 1 ether); + } + + function testLiquidateAccountWithOnlyNonCollateralSupply() external { + setUpUnderwaterWithNonCollateralMarket(0); + + weth.mint(liquidator, 1_000 ether); + vm.startPrank(liquidator); + weth.approve(address(marketWETH), type(uint256).max); + + // with no eligible collateral at all the liquidation can't be performed + vm.expectRevert(bytes("")); + marketWETH.liquidate(address(this), type(uint256).max, market); + vm.stopPrank(); + } + + function testHandleBadDebtWithNonCollateralMarketSupply() external { + auditor.enterMarket(market); + market.deposit(1_000 ether, address(this)); + weth.mint(BOB, 500 ether); + vm.prank(BOB); + marketWETH.deposit(500 ether, BOB); + + // route fixed fees to the earnings accumulator so it can cover the bad debt later + marketWETH.setBackupFeeRate(1e18); + marketWETH.setInterestRateModel(InterestRateModel(address(new MockBorrowRate(1e18)))); + marketWETH.borrowAtMaturity(FixedLib.INTERVAL, 250 ether, 500 ether, address(this), address(this)); + marketWETH.depositAtMaturity(FixedLib.INTERVAL, 250 ether, 250 ether, address(this)); + marketWETH.repayAtMaturity(FixedLib.INTERVAL, type(uint256).max, type(uint256).max, address(this)); + + marketWETH.borrow(200 ether, address(this), address(this)); + auditor.setNonCollateral(market, true); + (uint256 collateral, uint256 debt) = auditor.accountLiquidity(address(this), Market(address(0)), 0); + assertLt(collateral, debt); + + // non-collateral supply doesn't block bad debt clearing and is not consumed by it + uint256 shares = market.balanceOf(address(this)); + auditor.handleBadDebt(address(this)); + + (, , uint256 floatingBorrowShares) = marketWETH.accounts(address(this)); + assertEq(floatingBorrowShares, 0); + (, debt) = auditor.accountLiquidity(address(this), Market(address(0)), 0); + assertEq(debt, 0); + assertEq(market.balanceOf(address(this)), shares); + } + function testLiquidateLeavingDustAsCollateral() external { vm.prank(ALICE); marketWETH.approve(address(this), type(uint256).max); @@ -1182,7 +1349,7 @@ contract MarketTest is Test { market.deposit(1_000 ether, BOB); market.borrow(500 ether, address(this), address(this)); daiPriceFeed.setPrice(1); - (uint256 adjustFactor, uint8 decimals, , , IPriceFeed priceFeed) = auditor.markets(market); + (uint256 adjustFactor, uint8 decimals, , , IPriceFeed priceFeed, ) = auditor.markets(market); uint256 floatingAssetsBefore = market.floatingAssets(); assertEq( market.maxWithdraw(address(this)).mulDivDown(auditor.assetPrice(priceFeed), 10 ** decimals).mulWadDown( @@ -2715,7 +2882,7 @@ contract MarketTest is Test { MockStETH.getPooledEthByShares.selector, 1e18 ); - auditor.enableMarket(marketStETH, priceFeedWrapper, 0.8e18); + auditor.enableMarket(marketStETH, priceFeedWrapper, 0.8e18, false); stETH.mint(address(this), 50_000 ether); stETH.approve(address(marketStETH), type(uint256).max); @@ -2748,7 +2915,7 @@ contract MarketTest is Test { new MockPriceFeed(18, 14 ether), new MockPriceFeed(8, 99000000) ); - auditor.enableMarket(marketWBTC, priceFeedDouble, 0.8e18); + auditor.enableMarket(marketWBTC, priceFeedDouble, 0.8e18, false); wbtc.mint(address(this), 50_000e8); wbtc.approve(address(marketWBTC), type(uint256).max); @@ -2783,7 +2950,7 @@ contract MarketTest is Test { 0.42e18 ); - auditor.enableMarket(markets[i], daiPriceFeed, 0.8e18); + auditor.enableMarket(markets[i], daiPriceFeed, 0.8e18, false); asset_.mint(BOB, 50_000 ether); asset_.mint(address(this), 50_000 ether); vm.prank(BOB); diff --git a/test/Pauser.t.sol b/test/Pauser.t.sol index 5d4562f39..4d989531e 100644 --- a/test/Pauser.t.sol +++ b/test/Pauser.t.sol @@ -42,7 +42,7 @@ contract PauserTest is Test { ); marketA.grantRole(keccak256("PAUSER_ROLE"), address(this)); marketA.grantRole(keccak256("PAUSER_ROLE"), address(pauser)); - auditor.enableMarket(marketA, new MockPriceFeed(18, 1e18), 0.8e18); + auditor.enableMarket(marketA, new MockPriceFeed(18, 1e18), 0.8e18, false); vm.label(address(marketA), "MarketA"); marketB = Market(address(new ERC1967Proxy(address(new Market(new MockERC20("B", "B", 18), auditor)), ""))); @@ -60,7 +60,7 @@ contract PauserTest is Test { ); marketB.grantRole(keccak256("PAUSER_ROLE"), address(this)); marketB.grantRole(keccak256("PAUSER_ROLE"), address(pauser)); - auditor.enableMarket(marketB, new MockPriceFeed(18, 1e18), 0.8e18); + auditor.enableMarket(marketB, new MockPriceFeed(18, 1e18), 0.8e18, false); vm.label(address(marketB), "MarketB"); vm.label(BOB, "bob"); diff --git a/test/Previewer.t.sol b/test/Previewer.t.sol index 14ec1eec8..13369fe1f 100644 --- a/test/Previewer.t.sol +++ b/test/Previewer.t.sol @@ -62,7 +62,7 @@ contract PreviewerTest is Test { ); market.initialize("", 12, type(uint256).max, 1e18, irm, 0.02e18 / uint256(1 days), 0.1e18, 0, 0.0046e18, 0.42e18); vm.label(address(market), "MarketDAI"); - auditor.enableMarket(market, daiPriceFeed, 0.8e18); + auditor.enableMarket(market, daiPriceFeed, 0.8e18, false); vm.label(BOB, "Bob"); vm.label(ALICE, "Alice"); @@ -471,7 +471,7 @@ contract PreviewerTest is Test { 0.0046e18, 0.42e18 ); - auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18, false); ethPriceFeed.setPrice(2800e18); daiPriceFeed.setPrice(0.0003571428571e18); weth.mint(address(this), 50_000 ether); @@ -586,7 +586,7 @@ contract PreviewerTest is Test { 0.0046e18, 0.42e18 ); - auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18, false); ethPriceFeed.setPrice(2800e18); daiPriceFeed.setPrice(0.0003571428571e18); weth.mint(address(this), 50_000 ether); @@ -738,7 +738,7 @@ contract PreviewerTest is Test { 0.0046e18, 0.42e18 ); - auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18, false); weth.approve(address(marketWETH), type(uint256).max); RewardsController rewardsController = RewardsController( @@ -964,7 +964,7 @@ contract PreviewerTest is Test { 0.42e18 ); ethPriceFeed = new MockPriceFeed(18, 1_000e18); - auditor.enableMarket(marketWETH, ethPriceFeed, 0.7e18); + auditor.enableMarket(marketWETH, ethPriceFeed, 0.7e18, false); weth.mint(address(this), 50_000 ether); weth.approve(address(marketWETH), 50_000 ether); RewardsController rewardsController = RewardsController( @@ -1299,7 +1299,7 @@ contract PreviewerTest is Test { 0.0046e18, 0.42e18 ); - auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18, false); ethPriceFeed.setPrice(2_800e8); daiPriceFeed.setPrice(0.0003571428571e18); weth.mint(address(this), 50_000 ether); @@ -1381,7 +1381,7 @@ contract PreviewerTest is Test { 0.0046e18, 0.42e18 ); - auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18, false); ethPriceFeed.setPrice(2_800e8); daiPriceFeed.setPrice(0.0003571428571e18); weth.mint(address(this), 50_000 ether); @@ -1435,7 +1435,7 @@ contract PreviewerTest is Test { 0.0046e18, 0.42e18 ); - auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18, false); ethPriceFeed.setPrice(2_800e8); daiPriceFeed.setPrice(0.0003571428571e18); weth.mint(address(this), 50_000 ether); @@ -1501,7 +1501,7 @@ contract PreviewerTest is Test { 0.0046e18, 0.42e18 ); - auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18, false); ethPriceFeed.setPrice(2_800e8); daiPriceFeed.setPrice(0.0003571428571e18); weth.mint(address(this), 50_000 ether); @@ -1622,7 +1622,7 @@ contract PreviewerTest is Test { 0.0046e18, 0.42e18 ); - auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18, false); ethPriceFeed.setPrice(1_000e8); daiPriceFeed.setPrice(0.001e18); weth.mint(address(this), 1 ether); @@ -1673,7 +1673,7 @@ contract PreviewerTest is Test { 0.0046e18, 0.42e18 ); - auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18, false); ethPriceFeed.setPrice(1_000e8); daiPriceFeed.setPrice(0.001e18); weth.mint(address(this), 1 ether); @@ -1996,7 +1996,7 @@ contract PreviewerTest is Test { 0.0046e18, 0.42e18 ); - auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18, false); ethPriceFeed.setPrice(2_000e8); daiPriceFeed.setPrice(0.0005e18); @@ -2036,7 +2036,7 @@ contract PreviewerTest is Test { 0.0046e18, 0.42e18 ); - auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18, false); ethPriceFeed.setPrice(2_000e8); daiPriceFeed.setPrice(0.0005e18); weth.mint(address(this), 50_000 ether); @@ -2251,6 +2251,68 @@ contract PreviewerTest is Test { assertEq(data[0].isCollateral, false); } + function testExactlyIsCollateralWithNonCollateralMarket() external { + market.deposit(10 ether, address(this)); + auditor.enterMarket(market); + Previewer.MarketAccount[] memory data = previewer.exactly(address(this)); + assertEq(data[0].isCollateral, true); + + auditor.setNonCollateral(market, true); + data = previewer.exactly(address(this)); + assertEq(data[0].isCollateral, false); + + auditor.setNonCollateral(market, false); + data = previewer.exactly(address(this)); + assertEq(data[0].isCollateral, true); + } + + function testExactlyIsCollateralFalseWhenBorrowingFromNonCollateralMarket() external { + MockERC20 weth = new MockERC20("WETH", "WETH", 18); + Market marketWETH = Market(address(new ERC1967Proxy(address(new Market(weth, auditor)), ""))); + marketWETH.initialize( + "WETH", + 12, + type(uint256).max, + 1e18, + new InterestRateModel( + Parameters({ + minRate: 3.5e16, + naturalRate: 8e16, + maxUtilization: 1.1e18, + naturalUtilization: 0.75e18, + growthSpeed: 1.1e18, + sigmoidSpeed: 2.5e18, + spreadFactor: 0.2e18, + maturitySpeed: 0.5e18, + timePreference: 0.01e18, + fixedAllocation: 0.6e18, + maxRate: 15_000e16 + }), + marketWETH + ), + 0.02e18 / uint256(1 days), + 0.1e18, + 0, + 0.0046e18, + 0.42e18 + ); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.7e18, false); + weth.mint(address(this), 100 ether); + weth.approve(address(marketWETH), 100 ether); + marketWETH.deposit(100 ether, address(this)); + auditor.enterMarket(marketWETH); + + auditor.setNonCollateral(market, true); + market.deposit(100 ether, address(this)); + market.borrow(10 ether, address(this), address(this)); + assertEq(auditor.accountMarkets(address(this)) & 1, 1); + + // the bit set by borrowing must not be reported as collateral + Previewer.MarketAccount[] memory data = previewer.exactly(address(this)); + assertEq(data[0].isCollateral, false); + assertEq(data[1].isCollateral, true); + } + function testReserveFactor() external { market.setReserveFactor(0.05e18); Previewer.MarketAccount[] memory data = previewer.exactly(address(this)); diff --git a/test/Protocol.t.sol b/test/Protocol.t.sol index ccb8198be..358c3f09d 100644 --- a/test/Protocol.t.sol +++ b/test/Protocol.t.sol @@ -18,7 +18,13 @@ import { import { InterestRateModel, UtilizationExceeded, Parameters } from "../contracts/InterestRateModel.sol"; import { MockPriceFeed } from "../contracts/mocks/MockPriceFeed.sol"; import { FixedLib } from "../contracts/utils/FixedLib.sol"; -import { Auditor, InsufficientAccountLiquidity, InsufficientShortfall, RemainingDebt } from "../contracts/Auditor.sol"; +import { + Auditor, + RemainingDebt, + MarketNotCollateral, + InsufficientShortfall, + InsufficientAccountLiquidity +} from "../contracts/Auditor.sol"; import { RewardsController } from "../contracts/RewardsController.sol"; contract ProtocolTest is Test { @@ -89,7 +95,7 @@ contract ProtocolTest is Test { vm.label(address(market), string.concat("Market", i.toString())); MockPriceFeed priceFeed = new MockPriceFeed(18, 1e18); // market.setTreasury(address(this), 0.1e18); - auditor.enableMarket(market, priceFeed, 0.9e18); + auditor.enableMarket(market, priceFeed, 0.9e18, false); asset.approve(address(market), type(uint256).max); for (uint256 j = 0; j < accounts.length; ++j) { @@ -141,6 +147,7 @@ contract ProtocolTest is Test { for (uint256 i = 0; i < N * 2; ++i) { if (values[i * K + 0] % 2 == 0) enterMarket(i); if (values[i * K + 0] % 2 == 1) exitMarket(i); + if (values[i * K + 1] % 4 == 0) setNonCollateral(i); vm.warp(block.timestamp + timing[i * K + 0]); deposit(i, values[i * K + 0]); @@ -218,17 +225,7 @@ contract ProtocolTest is Test { if (assets == 0) { vm.expectRevert(ZeroWithdraw.selector); - } else if (block.timestamp < maturity && supplied + backupAssets == 0) { - vm.expectRevert(bytes("")); - } else if ( - (block.timestamp < maturity && positionAssets > backupAssets + supplied) || - (borrowed + positionAssets).divWadUp(backupAssets + supplied) > 1e18 - ) { - vm.expectRevert(UtilizationExceeded.selector); - } else if ( - block.timestamp < maturity && - ((supplied + previewFloatingAssetsAverage(market, maturity) == 0) || principal + fee == 0) - ) { + } else if (principal + fee == 0) { vm.expectRevert(bytes("")); } else if ( market.floatingBackupBorrowed() + @@ -238,6 +235,13 @@ contract ProtocolTest is Test { market.floatingAssets() + previewNewFloatingDebt(market) ) { vm.expectRevert(InsufficientProtocolLiquidity.selector); + } else if (block.timestamp < maturity && supplied + backupAssets == 0) { + vm.expectRevert(bytes("")); + } else if ( + (block.timestamp < maturity && positionAssets > backupAssets + supplied) || + (borrowed + positionAssets).divWadUp(backupAssets + supplied) > 1e18 + ) { + vm.expectRevert(UtilizationExceeded.selector); } else if ( ( block.timestamp < maturity @@ -298,18 +302,18 @@ contract ProtocolTest is Test { if (assets == 0) { vm.expectRevert(ZeroBorrow.selector); - } else if (supplied + backupAssets == 0) { - vm.expectRevert(bytes("")); - } else if (assets > backupAssets + supplied || (borrowed + assets).divWadUp(backupAssets + supplied) > 1e18) { - vm.expectRevert(UtilizationExceeded.selector); } else if ( backupDebtAddition > 0 && market.floatingBackupBorrowed() + backupDebtAddition + market.totalFloatingBorrowAssets() > (market.floatingAssets() + previewNewFloatingDebt(market)).mulWadDown(1e18 - RESERVE_FACTOR) ) { vm.expectRevert(InsufficientProtocolLiquidity.selector); + } else if (supplied + backupAssets == 0) { + vm.expectRevert(bytes("")); + } else if (assets > backupAssets + supplied || (borrowed + assets).divWadUp(backupAssets + supplied) > 1e18) { + vm.expectRevert(UtilizationExceeded.selector); } else { - uint256 fees = assets.mulWadDown( + uint256 fees = assets.mulWadUp( market.interestRateModel().fixedBorrowRate(maturity, assets, borrowed, supplied, backupAssets) ); (uint256 collateral, uint256 debt) = accountLiquidity(account, market, assets + fees, 0); @@ -372,12 +376,24 @@ contract ProtocolTest is Test { assertEq(rewardAsset.balanceOf(account), balanceBefore + accumulatedRewards); } + function setNonCollateral(uint256 i) internal { + Market market = markets[(i / 2) % markets.length]; + (, , , , , bool nonCollateral) = auditor.markets(market); + bool disabled = !nonCollateral; + + vm.expectEmit(true, true, true, true, address(auditor)); + emit NonCollateralSet(market, disabled); + auditor.setNonCollateral(market, disabled); + } + function enterMarket(uint256 i) internal { Market market = markets[(i / 2) % markets.length]; address account = accounts[i % accounts.length]; - (, , uint256 index, , ) = auditor.markets(market); + (, , uint256 index, , , bool disabled) = auditor.markets(market); - if ((auditor.accountMarkets(account) & (1 << index)) == 0) { + if (disabled) { + vm.expectRevert(MarketNotCollateral.selector); + } else if ((auditor.accountMarkets(account) & (1 << index)) == 0) { vm.expectEmit(true, true, true, true, address(auditor)); emit MarketEntered(market, account); } @@ -388,7 +404,7 @@ contract ProtocolTest is Test { function exitMarket(uint256 i) internal { Market market = markets[(i / 2) % markets.length]; address account = accounts[i % accounts.length]; - (, , uint256 index, , ) = auditor.markets(market); + (, , uint256 index, , , bool disabled) = auditor.markets(market); (uint256 balance, uint256 debt) = market.accountSnapshot(account); (uint256 adjustedCollateral, uint256 adjustedDebt) = accountLiquidity(account, market, 0, balance); uint256 marketMap = auditor.accountMarkets(account); @@ -396,7 +412,7 @@ contract ProtocolTest is Test { if ((marketMap & (1 << index)) != 0) { if (debt > 0) { vm.expectRevert(RemainingDebt.selector); - } else if (adjustedCollateral < adjustedDebt) { + } else if (adjustedCollateral < adjustedDebt && !disabled) { vm.expectRevert(InsufficientAccountLiquidity.selector); } else { vm.expectEmit(true, true, true, true, address(auditor)); @@ -477,12 +493,12 @@ contract ProtocolTest is Test { Market market = markets[(i / 2) % markets.length]; uint256 shareValue = market.totalSupply() > 0 ? market.previewMint(1e18) : 0; address account = accounts[i % accounts.length]; - (, , uint256 index, , ) = auditor.markets(market); + (, , uint256 index, , , bool disabled) = auditor.markets(market); uint256 expectedShares = market.totalAssets() != 0 ? market.previewWithdraw(assets) : 0; (uint256 collateral, uint256 debt) = accountLiquidity(account, market, 0, assets); uint256 earnings = previewAccumulatedEarnings(market); - if ((auditor.accountMarkets(account) & (1 << index)) != 0 && debt > collateral) { + if (!disabled && (auditor.accountMarkets(account) & (1 << index)) != 0 && debt > collateral) { vm.expectRevert(InsufficientAccountLiquidity.selector); } else if (market.totalSupply() > 0 && market.totalAssets() == 0) { vm.expectRevert(bytes("")); @@ -510,14 +526,15 @@ contract ProtocolTest is Test { Market market = markets[(i / 2) % markets.length]; uint256 shareValue = market.totalSupply() > 0 ? market.previewMint(1e18) : 0; address account = accounts[i % accounts.length]; - (, , uint256 index, , ) = auditor.markets(market); + (, , uint256 index, , , bool disabled) = auditor.markets(market); uint256 expectedAssets = market.previewRedeem(shares); (uint256 collateral, uint256 debt) = accountLiquidity(account, market, 0, expectedAssets); uint256 earnings = previewAccumulatedEarnings(market); - if (expectedAssets == 0 && ((auditor.accountMarkets(account) & (1 << index)) == 0 || collateral >= debt)) { + bool inMarket = !disabled && (auditor.accountMarkets(account) & (1 << index)) != 0; + if (expectedAssets == 0 && (!inMarket || collateral >= debt)) { vm.expectRevert(bytes("")); - } else if ((auditor.accountMarkets(account) & (1 << index)) != 0 && debt > collateral) { + } else if (inMarket && debt > collateral) { vm.expectRevert(InsufficientAccountLiquidity.selector); } else if (market.totalSupply() > 0 && market.totalAssets() == 0) { vm.expectRevert(bytes("")); @@ -546,11 +563,11 @@ contract ProtocolTest is Test { uint256 shareValue = market.totalSupply() > 0 ? market.previewMint(1e18) : 0; address account = accounts[i % accounts.length]; address otherAccount = accounts[(i + 1) % accounts.length]; - (, , uint256 index, , ) = auditor.markets(market); + (, , uint256 index, , , bool disabled) = auditor.markets(market); uint256 withdrawAssets = market.previewRedeem(shares); (uint256 collateral, uint256 debt) = accountLiquidity(account, market, 0, withdrawAssets); - if ((auditor.accountMarkets(account) & (1 << index)) != 0 && debt > collateral) { + if (!disabled && (auditor.accountMarkets(account) & (1 << index)) != 0 && debt > collateral) { vm.expectRevert(InsufficientAccountLiquidity.selector); } else if (shares > market.balanceOf(account)) { vm.expectRevert(stdError.arithmeticError); @@ -567,8 +584,8 @@ contract ProtocolTest is Test { Market market = markets[i]; Market collateralMarket = markets[(i + 1) % MARKET_COUNT]; uint256 shareValue = collateralMarket.totalSupply() > 0 ? collateralMarket.previewMint(1e18) : 0; - (, , uint256 index, , ) = auditor.markets(market); - (, , uint256 collateralIndex, , ) = auditor.markets(collateralMarket); + (, , uint256 index, , , ) = auditor.markets(market); + (, , uint256 collateralIndex, , , ) = auditor.markets(collateralMarket); (uint256 collateral, uint256 debt) = accountLiquidity(BOB, Market(address(0)), 0, 0); if (collateral >= debt) { @@ -610,13 +627,13 @@ contract ProtocolTest is Test { if (collateralMarket.totalSupply() > 0) assertGe(collateralMarket.previewMint(1e18), shareValue); if (repaidAssets > 0) { BadDebtVars memory b; - (b.adjustFactor, b.decimals, , , ) = auditor.markets(market); + (b.adjustFactor, b.decimals, , , , ) = auditor.markets(market); (b.balance, b.repayMarketDebt) = market.accountSnapshot(BOB); b.adjustedCollateral = b .balance .mulDivDown(uint256(priceFeeds[market].latestAnswer()), 10 ** b.decimals) .mulWadDown(b.adjustFactor); - (b.adjustFactor, b.decimals, , , ) = auditor.markets(market); + (b.adjustFactor, b.decimals, , , , ) = auditor.markets(market); (b.balance, b.collateralMarketDebt) = collateralMarket.accountSnapshot(BOB); b.adjustedCollateral += b .balance @@ -825,20 +842,27 @@ contract ProtocolTest is Test { for (uint256 i = 0; i < auditor.allMarkets().length; ++i) { Market market = auditor.marketList(i); if ((marketMap & (1 << i)) != 0) { - (, uint8 decimals, , , ) = auditor.markets(market); - (uint256 balance, ) = market.accountSnapshot(account); - sumCollateral += balance.mulDivDown(uint256(priceFeeds[market].latestAnswer()), 10 ** decimals); + (, uint8 decimals, , , , bool disabled) = auditor.markets(market); + if (!disabled) { + (uint256 balance, ) = market.accountSnapshot(account); + sumCollateral += balance.mulDivDown(uint256(priceFeeds[market].latestAnswer()), 10 ** decimals); + } } if ((1 << i) > marketMap) break; } } function seizeAvailable(address account, Market market) internal view returns (uint256) { + (, uint8 decimals, , , , bool disabled) = auditor.markets(market); + if (disabled) return 0; uint256 collateral = market.convertToAssets(market.balanceOf(account)); - (, uint8 decimals, , , ) = auditor.markets(market); return collateral.mulDivDown(uint256(priceFeeds[market].latestAnswer()), 10 ** decimals); } + function isDisabled(Market market) internal view returns (bool disabled) { + (, , , , , disabled) = auditor.markets(market); + } + function accountLiquidity( address account, Market marketToSimulate, @@ -850,20 +874,22 @@ contract ProtocolTest is Test { uint256 marketMap = auditor.accountMarkets(account); // if simulating a borrow, add the market to the account's map if (borrowAssets > 0) { - (, , uint256 index, , ) = auditor.markets(marketToSimulate); + (, , uint256 index, , , ) = auditor.markets(marketToSimulate); if ((marketMap & (1 << index)) == 0) marketMap = marketMap | (1 << index); } for (uint256 i = 0; i < auditor.allMarkets().length; ++i) { Market market = auditor.marketList(i); if ((marketMap & (1 << i)) != 0) { - (uint128 adjustFactor, uint8 decimals, , , ) = auditor.markets(market); + (uint128 adjustFactor, uint8 decimals, , , , ) = auditor.markets(market); (vars.balance, vars.borrowBalance) = market.accountSnapshot(account); vars.price = uint256(priceFeeds[market].latestAnswer()); - sumCollateral += vars.balance.mulDivDown(vars.price, 10 ** decimals).mulWadDown(adjustFactor); + if (!isDisabled(market)) { + sumCollateral += vars.balance.mulDivDown(vars.price, 10 ** decimals).mulWadDown(adjustFactor); + } sumDebtPlusEffects += (vars.borrowBalance + (market == marketToSimulate ? borrowAssets : 0)) .mulDivUp(vars.price, 10 ** decimals) .divWadUp(adjustFactor); - if (market == marketToSimulate && withdrawAssets != 0) { + if (market == marketToSimulate && withdrawAssets != 0 && !isDisabled(market)) { sumDebtPlusEffects += withdrawAssets.mulDivDown(vars.price, 10 ** decimals).mulWadDown(adjustFactor); } } @@ -881,17 +907,19 @@ contract ProtocolTest is Test { uint256 marketMap = auditor.accountMarkets(account); // if simulating a borrow, add the market to the account's map - (, , uint256 index, , ) = auditor.markets(marketToSimulate); + (, , uint256 index, , , ) = auditor.markets(marketToSimulate); if ((marketMap & (1 << index)) == 0) marketMap = marketMap | (1 << index); for (uint256 i = 0; i < auditor.allMarkets().length; ++i) { Market market = auditor.marketList(i); if ((marketMap & (1 << i)) != 0) { - (uint128 adjustFactor, uint8 decimals, , , ) = auditor.markets(market); + (uint128 adjustFactor, uint8 decimals, , , , bool disabled) = auditor.markets(market); if (market == marketToSimulate) { (vars.balance, vars.borrowBalance) = previewAccountSnapshot(market, account, borrowAssets, borrowShares); } else (vars.balance, vars.borrowBalance) = market.accountSnapshot(account); vars.price = uint256(priceFeeds[market].latestAnswer()); - sumCollateral += vars.balance.mulDivDown(vars.price, 10 ** decimals).mulWadDown(adjustFactor); + if (!disabled) { + sumCollateral += vars.balance.mulDivDown(vars.price, 10 ** decimals).mulWadDown(adjustFactor); + } sumDebtPlusEffects += vars.borrowBalance.mulDivUp(vars.price, 10 ** decimals).divWadUp(adjustFactor); } if ((1 << i) > marketMap) break; @@ -1050,6 +1078,7 @@ contract ProtocolTest is Test { event Transfer(address indexed from, address indexed to, uint256 amount); event MarketExited(Market indexed market, address indexed account); event MarketEntered(Market indexed market, address indexed account); + event NonCollateralSet(Market indexed market, bool disabled); event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); event Borrow( address indexed caller, diff --git a/test/RewardsController.t.sol b/test/RewardsController.t.sol index 6a6345294..63df5f53c 100644 --- a/test/RewardsController.t.sol +++ b/test/RewardsController.t.sol @@ -63,7 +63,7 @@ contract RewardsControllerTest is Test { 0.42e18 ); vm.label(address(marketUSDC), "MarketUSDC"); - auditor.enableMarket(marketUSDC, new MockPriceFeed(18, 1e18), 0.8e18); + auditor.enableMarket(marketUSDC, new MockPriceFeed(18, 1e18), 0.8e18, false); marketWETH = Market(address(new ERC1967Proxy(address(new Market(weth, auditor)), ""))); marketWETH.initialize( @@ -79,7 +79,7 @@ contract RewardsControllerTest is Test { 0.42e18 ); vm.label(address(marketWETH), "MarketWETH"); - auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.9e18); + auditor.enableMarket(marketWETH, IPriceFeed(auditor.BASE_FEED()), 0.9e18, false); marketWBTC = Market(address(new ERC1967Proxy(address(new Market(wbtc, auditor)), ""))); marketWBTC.initialize( @@ -95,7 +95,7 @@ contract RewardsControllerTest is Test { 0.42e18 ); vm.label(address(marketWBTC), "MarketWBTC"); - auditor.enableMarket(marketWBTC, new MockPriceFeed(18, 20_000e18), 0.9e18); + auditor.enableMarket(marketWBTC, new MockPriceFeed(18, 20_000e18), 0.9e18, false); rewardsController = RewardsController(address(new ERC1967Proxy(address(new RewardsController()), ""))); rewardsController.initialize(); @@ -1798,7 +1798,7 @@ contract RewardsControllerTest is Test { 0.0046e18, 0.42e18 ); - auditor.enableMarket(market, new MockPriceFeed(18, 1e18), 0.8e18); + auditor.enableMarket(market, new MockPriceFeed(18, 1e18), 0.8e18, false); RewardsController.Config[] memory configs = new RewardsController.Config[](1); configs[0] = RewardsController.Config({ @@ -1904,7 +1904,7 @@ contract RewardsControllerTest is Test { marketWBTC.deposit(100e8, BOB); auditor.enterMarket(marketUSDC); marketWBTC.borrow(20e8, address(this), address(this)); - (, , , , IPriceFeed wbtcPriceFeed) = auditor.markets(marketWBTC); + (, , , , IPriceFeed wbtcPriceFeed, ) = auditor.markets(marketWBTC); MockPriceFeed(address(wbtcPriceFeed)).setPrice(50_000e18); vm.warp(4 weeks); @@ -1933,7 +1933,7 @@ contract RewardsControllerTest is Test { marketUSDC.depositAtMaturity(FixedLib.INTERVAL, 30_000e6, 30_000e6, BOB); vm.warp(4 weeks); - (, , , , IPriceFeed wbtcPriceFeed) = auditor.markets(marketWBTC); + (, , , , IPriceFeed wbtcPriceFeed, ) = auditor.markets(marketWBTC); MockPriceFeed(address(wbtcPriceFeed)).setPrice(10); vm.prank(ALICE); marketWETH.liquidate(address(this), type(uint256).max, marketWBTC); @@ -1998,7 +1998,7 @@ contract RewardsControllerTest is Test { marketUSDC.borrow(500_000e6, address(this), address(this)); vm.warp(4 weeks); - (, , , , IPriceFeed wbtcPriceFeed) = auditor.markets(marketWBTC); + (, , , , IPriceFeed wbtcPriceFeed, ) = auditor.markets(marketWBTC); MockPriceFeed(address(wbtcPriceFeed)).setPrice(100e18); vm.prank(ALICE); marketUSDC.liquidate(address(this), type(uint256).max, marketWBTC); @@ -2015,7 +2015,7 @@ contract RewardsControllerTest is Test { marketUSDC.borrowAtMaturity(FixedLib.INTERVAL, 100_000e6, 200_000e6, address(this), address(this)); vm.warp(4 weeks); - (, , , , IPriceFeed wbtcPriceFeed) = auditor.markets(marketWBTC); + (, , , , IPriceFeed wbtcPriceFeed, ) = auditor.markets(marketWBTC); MockPriceFeed(address(wbtcPriceFeed)).setPrice(100e18); vm.prank(ALICE); marketUSDC.liquidate(address(this), type(uint256).max, marketWBTC); diff --git a/test/VerifiedMarket.t.sol b/test/VerifiedMarket.t.sol index e0c0213e8..955b7d596 100644 --- a/test/VerifiedMarket.t.sol +++ b/test/VerifiedMarket.t.sol @@ -8,7 +8,8 @@ import { MockERC20 } from "solmate/src/test/utils/mocks/MockERC20.sol"; import { MarketTest } from "./Market.t.sol"; import { Auditor } from "../contracts/Auditor.sol"; import { InterestRateModel } from "../contracts/InterestRateModel.sol"; -import { Market } from "../contracts/Market.sol"; +import { Market, ZeroRepay } from "../contracts/Market.sol"; +import { MockBorrowRate } from "../contracts/mocks/MockBorrowRate.sol"; import { Firewall } from "../contracts/verified/Firewall.sol"; import { NotAllowed, RemainingDebt, VerifiedAuditor } from "../contracts/verified/VerifiedAuditor.sol"; import { Locked, NotAuditor, Unlocked, VerifiedMarket } from "../contracts/verified/VerifiedMarket.sol"; @@ -82,8 +83,8 @@ contract VerifiedMarketTest is MarketTest { marketWETHPriceFeed = MockPriceFeed(address(auditor.BASE_FEED())); daiPriceFeed = new MockPriceFeed(18, 1e18); - auditor.enableMarket(market, daiPriceFeed, 0.8e18); - auditor.enableMarket(marketWETH, marketWETHPriceFeed, 0.9e18); + auditor.enableMarket(market, daiPriceFeed, 0.8e18, false); + auditor.enableMarket(marketWETH, marketWETHPriceFeed, 0.9e18, false); auditor.enterMarket(marketWETH); weth.mint(address(this), 1_000_000 ether); @@ -725,6 +726,206 @@ contract VerifiedMarketTest is MarketTest { VerifiedAuditor(address(auditor)).unlock(BOB); } + function seedAccumulator(Market market_, uint256 assets) internal { + market_.setBackupFeeRate(1e18); + market_.setInterestRateModel(InterestRateModel(address(new MockBorrowRate(1e18)))); + market_.borrowAtMaturity(FixedLib.INTERVAL, assets, type(uint256).max, address(this), address(this)); + market_.depositAtMaturity(FixedLib.INTERVAL, assets, 0, address(this)); + market_.repayAtMaturity(FixedLib.INTERVAL, type(uint256).max, type(uint256).max, address(this)); + } + + function test_unwind_liquidatesAndLocks_whenDisallowedIsHealthyWithCollateralOnly() external { + marketWETHPriceFeed = new MockPriceFeed(18, 1_000e18); + auditor.setPriceFeed(marketWETH, marketWETHPriceFeed); + marketWETH.deposit(10 ether, address(this)); + + market.deposit(5_000e18, BOB); + vm.startPrank(BOB); + auditor.enterMarket(market); + marketWETH.borrow(1 ether, BOB, BOB); + vm.stopPrank(); + + firewall.allow(BOB, false); + + // position is healthy, yet the disallowed branch lets it be fully repaid by liquidation + (uint256 collateral, uint256 debt) = auditor.accountLiquidity(BOB, Market(address(0)), 0); + assertGt(collateral, debt, "position should be healthy"); + + uint256 repaid = marketWETH.liquidate(BOB, type(uint256).max, market); + assertEq(repaid, 1 ether, "debt not fully repaid"); + assertEq(marketWETH.previewDebt(BOB), 0, "debt left"); + assertEq(marketWETH.earningsAccumulator(), 0, "incentives were charged"); + + VerifiedAuditor(address(auditor)).lock(BOB); + assertEq(market.balanceOf(BOB), 0, "shares left"); + assertEq(VerifiedMarket(address(market)).lockedAssets(BOB), 4_000e18, "wrong locked assets"); + } + + function test_unwind_liquidatesClearsBadDebtAndLocks_whenDisallowedIsUnderwaterWithCollateralOnly() external { + marketWETHPriceFeed = new MockPriceFeed(18, 1_000e18); + auditor.setPriceFeed(marketWETH, marketWETHPriceFeed); + marketWETH.deposit(10 ether, address(this)); + + market.deposit(5_000e18, BOB); + vm.startPrank(BOB); + auditor.enterMarket(market); + marketWETH.borrow(1 ether, BOB, BOB); + vm.stopPrank(); + + // bad debt can only be cleared against the accumulator, so it must have funds + seedAccumulator(marketWETH, 1 ether); + assertGe(marketWETH.earningsAccumulator(), 0.2 ether, "accumulator not seeded"); + + marketWETHPriceFeed = new MockPriceFeed(18, 6_000e18); + auditor.setPriceFeed(marketWETH, marketWETHPriceFeed); + (uint256 collateral, uint256 debt) = auditor.accountLiquidity(BOB, Market(address(0)), 0); + assertLt(collateral, debt, "position should be underwater"); + + firewall.allow(BOB, false); + + // seizes all collateral and clears the remainder as bad debt within the same call + marketWETH.liquidate(BOB, type(uint256).max, market); + assertEq(market.balanceOf(BOB), 0, "collateral left"); + assertEq(marketWETH.previewDebt(BOB), 0, "debt left"); + + VerifiedAuditor(address(auditor)).lock(BOB); + assertEq(VerifiedMarket(address(market)).lockedAssets(BOB), 0, "nothing should be left to lock"); + assertEq(VerifiedMarket(address(marketWETH)).lockedAssets(BOB), 0, "nothing should be left to lock"); + } + + function test_unwind_liquidatesAndLocks_whenDisallowedIsHealthyWithMixedSupply() external { + auditor.setNonCollateral(market, true); + + vm.prank(BOB); + auditor.enterMarket(marketWETH); + marketWETH.deposit(10 ether, BOB); + market.deposit(1_000e18, BOB); + vm.prank(BOB); + marketWETH.borrow(1 ether, BOB, BOB); + + firewall.allow(BOB, false); + + // seizing from the enabled collateral market still works and repays the whole debt + uint256 repaid = marketWETH.liquidate(BOB, type(uint256).max, marketWETH); + assertEq(repaid, 1 ether, "debt not fully repaid"); + assertEq(marketWETH.previewDebt(BOB), 0, "debt left"); + + // lock seizes the remaining collateral and the non-collateral supply + VerifiedAuditor(address(auditor)).lock(BOB); + assertEq(marketWETH.balanceOf(BOB), 0, "collateral shares left"); + assertEq(market.balanceOf(BOB), 0, "non-collateral shares left"); + assertEq(VerifiedMarket(address(marketWETH)).lockedAssets(BOB), 9 ether, "wrong locked collateral"); + assertEq(VerifiedMarket(address(market)).lockedAssets(BOB), 1_000e18, "wrong locked non-collateral supply"); + } + + function test_unwind_liquidatesClearsBadDebtAndLocks_whenDisallowedIsUnderwaterWithMixedSupply() external { + marketWETHPriceFeed = new MockPriceFeed(18, 1_000e18); + auditor.setPriceFeed(marketWETH, marketWETHPriceFeed); + auditor.setNonCollateral(market, true); + + marketWETH.deposit(1 ether, address(this)); + marketWETH.deposit(1 ether, BOB); + market.deposit(1_000e18, BOB); + vm.startPrank(BOB); + auditor.enterMarket(marketWETH); + market.borrow(500e18, BOB, BOB); + vm.stopPrank(); + + seedAccumulator(market, 200e18); + assertGe(market.earningsAccumulator(), 150e18, "accumulator not seeded"); + + marketWETHPriceFeed = new MockPriceFeed(18, 400e18); + auditor.setPriceFeed(marketWETH, marketWETHPriceFeed); + (uint256 collateral, uint256 debt) = auditor.accountLiquidity(BOB, Market(address(0)), 0); + assertLt(collateral, debt, "position should be underwater"); + + firewall.allow(BOB, false); + + // seizes all weth collateral and clears the remainder as bad debt, non-collateral supply stays untouched + uint256 repaid = market.liquidate(BOB, type(uint256).max, marketWETH); + assertEq(repaid, 400e18, "wrong repaid amount"); + assertEq(marketWETH.balanceOf(BOB), 0, "collateral left"); + assertEq(market.previewDebt(BOB), 0, "debt left"); + assertEq(market.balanceOf(BOB), 1_000e18, "non-collateral supply was consumed"); + + VerifiedAuditor(address(auditor)).lock(BOB); + assertEq(market.balanceOf(BOB), 0, "non-collateral shares left"); + assertEq(VerifiedMarket(address(market)).lockedAssets(BOB), 1_000e18, "wrong locked non-collateral supply"); + } + + function test_unwind_locks_whenDisallowedHasOnlyNonCollateralSupplyAndNoDebt() external { + auditor.setNonCollateral(market, true); + market.deposit(500e18, BOB); + market.depositAtMaturity(FixedLib.INTERVAL, 100e18, 0, BOB); + + firewall.allow(BOB, false); + VerifiedAuditor(address(auditor)).lock(BOB); + + assertEq(market.balanceOf(BOB), 0, "shares left"); + (uint256 principal, ) = market.fixedDepositPositions(FixedLib.INTERVAL, BOB); + assertEq(principal, 0, "fixed deposit left"); + assertGt(VerifiedMarket(address(market)).lockedAssets(BOB), 500e18, "wrong locked assets"); + } + + function test_unwind_isStranded_whenDisallowedHasDebtAndOnlyNonCollateralSupply() external { + marketWETH.deposit(10 ether, address(this)); + + market.deposit(1_000e18, BOB); + vm.startPrank(BOB); + auditor.enterMarket(market); + marketWETH.borrow(1 ether, BOB, BOB); + vm.stopPrank(); + + auditor.setNonCollateral(market, true); + firewall.allow(BOB, false); + + // liquidation cannot seize the non-collateral supply: seizeAvailable == 0 -> maxRepayAssets == 0 + vm.expectRevert(ZeroRepay.selector); + marketWETH.liquidate(BOB, type(uint256).max, market); + + // nor seize from a market where the account holds nothing + vm.expectRevert(ZeroRepay.selector); + marketWETH.liquidate(BOB, type(uint256).max, marketWETH); + + // debt cannot be repaid on behalf of a disallowed borrower + vm.expectRevert(abi.encodeWithSelector(NotAllowed.selector, BOB)); + marketWETH.repay(1 ether, BOB); + + // bad debt cannot be cleared while the accumulator is empty + auditor.handleBadDebt(BOB); + assertEq(marketWETH.previewDebt(BOB), 1 ether, "debt should remain"); + + // so the account cannot be locked either + vm.expectRevert(RemainingDebt.selector); + VerifiedAuditor(address(auditor)).lock(BOB); + } + + function test_unwind_socializesDebt_whenDisallowedHasDebtAndOnlyNonCollateralSupply() external { + marketWETH.deposit(10 ether, address(this)); + + market.deposit(1_000e18, BOB); + vm.startPrank(BOB); + auditor.enterMarket(market); + marketWETH.borrow(1 ether, BOB, BOB); + vm.stopPrank(); + + auditor.setNonCollateral(market, true); + firewall.allow(BOB, false); + + seedAccumulator(marketWETH, 2 ether); + uint256 accumulatorBefore = marketWETH.earningsAccumulator(); + assertGe(accumulatorBefore, 1 ether, "accumulator can't cover the debt"); + + // the only way out: socialize the full debt to weth lenders even though BOB holds 1_000 dai + auditor.handleBadDebt(BOB); + assertEq(marketWETH.previewDebt(BOB), 0, "debt left"); + assertEq(accumulatorBefore - marketWETH.earningsAccumulator(), 1 ether, "lenders didn't absorb the debt"); + assertEq(market.maxWithdraw(BOB), 1_000e18, "non-collateral supply was consumed"); + + VerifiedAuditor(address(auditor)).lock(BOB); + assertEq(VerifiedMarket(address(market)).lockedAssets(BOB), 1_000e18, "supply not locked"); + } + function test_unlock_afterOperating_unlocks() external { marketWETH.deposit(10 ether, address(this)); marketWETH.borrow(1 ether, address(this), address(this)); diff --git a/test/hardhat/0_admin.ts b/test/hardhat/0_admin.ts index c5911b572..72a657356 100644 --- a/test/hardhat/0_admin.ts +++ b/test/hardhat/0_admin.ts @@ -44,7 +44,7 @@ describe("Auditor Admin", function () { describe("GIVEN a regular account", () => { it("WHEN trying to enable a market, THEN the transaction should revert with Access Control", async () => { - await expect(auditor.enableMarket(marketDAI.target, priceFeedDAI.target, 0)).to.be.revertedWithoutReason(); + await expect(auditor.enableMarket(marketDAI.target, priceFeedDAI.target, 0, false)).to.be.revertedWithoutReason(); }); it("WHEN trying to set liquidation incentive, THEN the transaction should revert with Access Control", async () => { @@ -72,7 +72,7 @@ describe("Auditor Admin", function () { }); it("WHEN trying to enable a market for the second time, THEN the transaction should revert with MarketAlreadyListed", async () => { - await expect(auditor.enableMarket(marketDAI.target, priceFeedDAI.target, 0)).to.be.revertedWithCustomError( + await expect(auditor.enableMarket(marketDAI.target, priceFeedDAI.target, 0, false)).to.be.revertedWithCustomError( auditor, "MarketAlreadyListed", ); @@ -85,7 +85,7 @@ describe("Auditor Admin", function () { newAuditor.target, ); await expect( - auditor.enableMarket(market.target, priceFeedDAI.target, parseUnits("0.5")), + auditor.enableMarket(market.target, priceFeedDAI.target, parseUnits("0.5"), false), ).to.be.revertedWithCustomError(auditor, "AuditorMismatch"); }); @@ -99,7 +99,7 @@ describe("Auditor Admin", function () { it("WHEN trying to set a new market, THEN the auditor should emit MarketListed event", async () => { const market = await ((await getContractFactory("Market")) as Market__factory).deploy(dai.target, auditor.target); - await expect(auditor.enableMarket(market.target, priceFeedDAI.target, parseUnits("0.5"))) + await expect(auditor.enableMarket(market.target, priceFeedDAI.target, parseUnits("0.5"), false)) .to.emit(auditor, "MarketListed") .withArgs(market.target, 18); }); diff --git a/test/hardhat/1_auditor.ts b/test/hardhat/1_auditor.ts index 27e381c8a..004bafc4d 100644 --- a/test/hardhat/1_auditor.ts +++ b/test/hardhat/1_auditor.ts @@ -151,4 +151,49 @@ describe("Auditor from Account Space", function () { expect(isListed).to.equal(true); expect(decimals).to.equal(18); }); + + it("Get market data through the legacy markets getter ABI", async () => { + // integrators compiled before `nonCollateral` was appended keep decoding the getter + const legacyAuditor = new ethers.Contract( + auditor.target, + [ + "function markets(address market) view returns (uint128 adjustFactor, uint8 decimals, uint8 index, bool isListed, address priceFeed)", + ], + account, + ); + const { index: marketIndex } = await auditor.markets(marketDAI.target); + // the updated ABI reads the same values plus the new `nonCollateral` field + const expectMarketData = async (nonCollateral: boolean) => { + const market = await auditor.markets(marketDAI.target); + expect(market.adjustFactor).to.equal(parseUnits("0.95")); + expect(market.decimals).to.equal(18); + expect(market.index).to.equal(marketIndex); + expect(market.isListed).to.equal(true); + expect(market.priceFeed).to.equal(priceFeedDAI.target); + expect(market.nonCollateral).to.equal(nonCollateral); + }; + + const [adjustFactor, decimals, index, isListed, priceFeed] = await legacyAuditor.markets(marketDAI.target); + expect(adjustFactor).to.equal(parseUnits("0.95")); + expect(decimals).to.equal(18); + expect(index).to.equal(marketIndex); + expect(isListed).to.equal(true); + expect(priceFeed).to.equal(priceFeedDAI.target); + await expectMarketData(false); + + // decoding is unaffected by the appended `nonCollateral` flag being set + await timelockExecute(owner, auditor, "setNonCollateral", [marketDAI.target, true]); + const [adjustFactorDisabled, decimalsDisabled, indexDisabled, isListedDisabled, priceFeedDisabled] = + await legacyAuditor.markets(marketDAI.target); + expect(adjustFactorDisabled).to.equal(parseUnits("0.95")); + expect(decimalsDisabled).to.equal(18); + expect(indexDisabled).to.equal(marketIndex); + expect(isListedDisabled).to.equal(true); + expect(priceFeedDisabled).to.equal(priceFeedDAI.target); + await expectMarketData(true); + + // toggling the flag back off restores the unset reading with every other field untouched + await timelockExecute(owner, auditor, "setNonCollateral", [marketDAI.target, false]); + await expectMarketData(false); + }); }); diff --git a/test/hardhat/defaultEnv.ts b/test/hardhat/defaultEnv.ts index ba18f768d..525663bbf 100644 --- a/test/hardhat/defaultEnv.ts +++ b/test/hardhat/defaultEnv.ts @@ -167,7 +167,7 @@ export class DefaultEnv { const mockPriceFeed = await MockPriceFeed.deploy(8, usdPrice); await mockPriceFeed.waitForDeployment(); // Enable Market for MarketASSET by setting the collateral rates - await auditor.enableMarket(market.target, mockPriceFeed.target, adjustFactor); + await auditor.enableMarket(market.target, mockPriceFeed.target, adjustFactor, false); // Handy maps with all the markets and underlying assets priceFeeds[market.target as string] = mockPriceFeed; diff --git a/test/hardhat/marketEnv.ts b/test/hardhat/marketEnv.ts index bf73b32d4..6bc7cd7ab 100644 --- a/test/hardhat/marketEnv.ts +++ b/test/hardhat/marketEnv.ts @@ -92,7 +92,7 @@ export class MarketEnv { const MockPriceFeed = (await getContractFactory("MockPriceFeed")) as MockPriceFeed__factory; const mockPriceFeed = await MockPriceFeed.deploy(8, parseUnits("1", 8)); await mockPriceFeed.waitForDeployment(); - await auditor.enableMarket(marketHarness.target, mockPriceFeed.target, parseUnits("0.9")); + await auditor.enableMarket(marketHarness.target, mockPriceFeed.target, parseUnits("0.9"), false); return new MarketEnv(mockInterestRateModel, marketHarness, asset, owner); }