From 4aa6d4361b02499e20090aca3a4654d874f31002 Mon Sep 17 00:00:00 2001 From: christn Date: Mon, 11 Nov 2024 16:24:01 +0100 Subject: [PATCH] fix: Adjust applicationo of "unitilizedLeveragePercentage" in calculation of maxBorrow (#192) * fix: Apply unitilizedLeveragePercentage to maxBorrowCollateral only when levering up * fix: Add separate unitilizedLeveragePercentage parameter for delevering --- .../MorphoLeverageStrategyExtension.sol | 13 +++++++++-- .../morphoLeverageStrategyExtension.spec.ts | 23 ++++++++++++++++++- utils/types.ts | 1 + 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/contracts/adapters/MorphoLeverageStrategyExtension.sol b/contracts/adapters/MorphoLeverageStrategyExtension.sol index 32bbf35c..1c72edd9 100644 --- a/contracts/adapters/MorphoLeverageStrategyExtension.sol +++ b/contracts/adapters/MorphoLeverageStrategyExtension.sol @@ -101,6 +101,7 @@ contract MorphoLeverageStrategyExtension is BaseExtension { struct ExecutionSettings { uint256 unutilizedLeveragePercentage; // Percent of max borrow left unutilized in precise units (1% = 10e16) + uint256 unutilizedLeveragePercentageDelever; // Percent of max borrow left unutilized when delevering uint256 slippageTolerance; // % in precise units to price min token receive amount from trade quantities uint256 twapCooldownPeriod; // Cooldown period required since last trade timestamp in seconds } @@ -161,6 +162,7 @@ contract MorphoLeverageStrategyExtension is BaseExtension { ); event ExecutionSettingsUpdated( uint256 _unutilizedLeveragePercentage, + uint256 _unutilizedLeveragePercentageDelever, uint256 _twapCooldownPeriod, uint256 _slippageTolerance ); @@ -490,6 +492,7 @@ contract MorphoLeverageStrategyExtension is BaseExtension { emit ExecutionSettingsUpdated( execution.unutilizedLeveragePercentage, + execution.unutilizedLeveragePercentageDelever, execution.twapCooldownPeriod, execution.slippageTolerance ); @@ -960,6 +963,10 @@ contract MorphoLeverageStrategyExtension is BaseExtension { _execution.unutilizedLeveragePercentage <= PreciseUnitMath.preciseUnit(), "Unutilized leverage must be <100%" ); + require ( + _execution.unutilizedLeveragePercentageDelever <= PreciseUnitMath.preciseUnit(), + "Unutilized leverage on delever must be <100%" + ); require ( _execution.slippageTolerance <= PreciseUnitMath.preciseUnit(), "Slippage tolerance must be <100%" @@ -1123,14 +1130,16 @@ contract MorphoLeverageStrategyExtension is BaseExtension { // Note NetBorrow Limit is already denominated in borrow asset uint256 netBorrowLimit = _actionInfo.collateralBalance .mul(_actionInfo.collateralPrice).div(MORPHO_ORACLE_PRICE_SCALE) - .preciseMul(_actionInfo.lltv) - .preciseMul(PreciseUnitMath.preciseUnit().sub(execution.unutilizedLeveragePercentage)); + .preciseMul(_actionInfo.lltv); + if (_isLever) { return netBorrowLimit + .preciseMul(PreciseUnitMath.preciseUnit().sub(execution.unutilizedLeveragePercentage)) .sub(_actionInfo.borrowBalance) .mul(MORPHO_ORACLE_PRICE_SCALE).div(_actionInfo.collateralPrice); } else { return _actionInfo.collateralBalance + .preciseMul(PreciseUnitMath.preciseUnit().sub(execution.unutilizedLeveragePercentageDelever)) .preciseMul(netBorrowLimit.sub(_actionInfo.borrowBalance)) .preciseDiv(netBorrowLimit); } diff --git a/test/integration/ethereum/morphoLeverageStrategyExtension.spec.ts b/test/integration/ethereum/morphoLeverageStrategyExtension.spec.ts index 94b7767e..6292a0e1 100644 --- a/test/integration/ethereum/morphoLeverageStrategyExtension.spec.ts +++ b/test/integration/ethereum/morphoLeverageStrategyExtension.spec.ts @@ -385,6 +385,7 @@ if (process.env.INTEGRATIONTEST) { const rebalanceInterval = BigNumber.from(86400); const unutilizedLeveragePercentage = ether(0.01); + const unutilizedLeveragePercentageDelever = ZERO; const twapMaxTradeSize = ether(0.5); const twapCooldownPeriod = BigNumber.from(3000); const slippageTolerance = ether(0.01); @@ -412,6 +413,7 @@ if (process.env.INTEGRATIONTEST) { }; execution = { unutilizedLeveragePercentage: unutilizedLeveragePercentage, + unutilizedLeveragePercentageDelever: unutilizedLeveragePercentageDelever, twapCooldownPeriod: twapCooldownPeriod, slippageTolerance: slippageTolerance, }; @@ -473,6 +475,7 @@ if (process.env.INTEGRATIONTEST) { }; subjectExecutionSettings = { unutilizedLeveragePercentage: ether(0.01), + unutilizedLeveragePercentageDelever: ZERO, twapCooldownPeriod: BigNumber.from(120), slippageTolerance: ether(0.01), }; @@ -553,6 +556,9 @@ if (process.env.INTEGRATIONTEST) { expect(execution.unutilizedLeveragePercentage).to.eq( subjectExecutionSettings.unutilizedLeveragePercentage, ); + expect(execution.unutilizedLeveragePercentageDelever).to.eq( + subjectExecutionSettings.unutilizedLeveragePercentageDelever + ); expect(execution.twapCooldownPeriod).to.eq(subjectExecutionSettings.twapCooldownPeriod); expect(execution.slippageTolerance).to.eq(subjectExecutionSettings.slippageTolerance); }); @@ -650,6 +656,16 @@ if (process.env.INTEGRATIONTEST) { }); }); + describe("when unutilizedLeveragePercentageDelever is >100%", async () => { + beforeEach(async () => { + subjectExecutionSettings.unutilizedLeveragePercentageDelever = ether(1.1); + }); + + it("should revert", async () => { + await expect(subject()).to.be.revertedWith("Unutilized leverage on delever must be <100%"); + }); + }); + describe("when slippage tolerance is >100%", async () => { beforeEach(async () => { subjectExecutionSettings.slippageTolerance = ether(1.1); @@ -2967,7 +2983,6 @@ if (process.env.INTEGRATIONTEST) { ); const expectedFirstPositionUnit = initialPositions[0].unit.sub(maxRedeemCollateral); - console.log("expectedFirstPositionUnit", expectedFirstPositionUnit.toString()); expect(initialPositions.length).to.eq(2); expect(currentPositions.length).to.eq(2); @@ -3430,6 +3445,7 @@ if (process.env.INTEGRATIONTEST) { const oldExecution = await leverageStrategyExtension.getExecution(); const newExecution: ExecutionSettings = { unutilizedLeveragePercentage: oldExecution.unutilizedLeveragePercentage, + unutilizedLeveragePercentageDelever: ZERO, twapCooldownPeriod: oldExecution.twapCooldownPeriod, slippageTolerance: ether(0.05), }; @@ -3851,6 +3867,7 @@ if (process.env.INTEGRATIONTEST) { const initializeSubjectVariables = () => { subjectExecutionSettings = { unutilizedLeveragePercentage: ether(0.05), + unutilizedLeveragePercentageDelever: ZERO, twapCooldownPeriod: BigNumber.from(360), slippageTolerance: ether(0.02), }; @@ -3872,6 +3889,9 @@ if (process.env.INTEGRATIONTEST) { expect(execution.unutilizedLeveragePercentage).to.eq( subjectExecutionSettings.unutilizedLeveragePercentage, ); + expect(execution.unutilizedLeveragePercentageDelever).to.eq( + subjectExecutionSettings.unutilizedLeveragePercentageDelever, + ); expect(execution.twapCooldownPeriod).to.eq(subjectExecutionSettings.twapCooldownPeriod); expect(execution.slippageTolerance).to.eq(subjectExecutionSettings.slippageTolerance); }); @@ -3881,6 +3901,7 @@ if (process.env.INTEGRATIONTEST) { .to.emit(leverageStrategyExtension, "ExecutionSettingsUpdated") .withArgs( subjectExecutionSettings.unutilizedLeveragePercentage, + subjectExecutionSettings.unutilizedLeveragePercentageDelever, subjectExecutionSettings.twapCooldownPeriod, subjectExecutionSettings.slippageTolerance, ); diff --git a/utils/types.ts b/utils/types.ts index 39bae07d..e97995e6 100644 --- a/utils/types.ts +++ b/utils/types.ts @@ -73,6 +73,7 @@ export interface MethodologySettings { export interface ExecutionSettings { unutilizedLeveragePercentage: BigNumber; + unutilizedLeveragePercentageDelever: BigNumber; twapCooldownPeriod: BigNumber; slippageTolerance: BigNumber; }