diff --git a/contracts/MSVBase.sol b/contracts/MSVBase.sol index 24a93c7..5833a39 100644 --- a/contracts/MSVBase.sol +++ b/contracts/MSVBase.sol @@ -279,7 +279,7 @@ abstract contract MSVBase is IExposeStorage { if (strategyIndex >= MAX_STRATEGIES) revert InvalidStrategy(); IInvestStrategy strategy = _strategies[strategyIndex]; if (address(strategy) == address(0)) revert InvalidStrategy(); - if (strategy.totalAssets() != 0) revert CannotRemoveStrategyWithAssets(); + if (!force && strategy.totalAssets() != 0) revert CannotRemoveStrategyWithAssets(); // Check isn't removing the last one if (strategyIndex == 0 && address(_strategies[1]) == address(0)) revert InvalidStrategiesLength(); // Shift the following strategies in the array diff --git a/test/test-multi-strategy-erc4626.js b/test/test-multi-strategy-erc4626.js index 14c1da0..552d8a2 100644 --- a/test/test-multi-strategy-erc4626.js +++ b/test/test-multi-strategy-erc4626.js @@ -821,6 +821,44 @@ variants.forEach((variant) => { await invariantChecks(vault); }); + variant.tagit("It can removeStrategy only if doesn't have funds unless forced", async () => { + const { deployVault, lp, lp2, currency, grantRole, grantForwardToStrategy, strategies } = + await helpers.loadFixture(variant.fixture); + const vault = await deployVault(3, undefined, [1, 0, 2], [2, 0, 1]); + await currency.connect(lp).approve(vault, MaxUint256); + await grantRole(vault, "LP_ROLE", lp); + await expect(vault.connect(lp).mint(_A(100), lp)).not.to.be.reverted; + await invariantChecks(vault); + + expect(await vault.totalAssets()).to.be.equal(_A(100)); + // Check money went to strategy[3] + expect(await currency.balanceOf(await strategies[1].other())).to.be.equal(_A(100)); + + await expect(vault.connect(lp2).removeStrategy(0, false)).to.be[variant.accessError]( + vault, + lp2, + "STRATEGY_ADMIN_ROLE" + ); + + await grantRole(vault, "STRATEGY_ADMIN_ROLE", lp2); + + await expect(vault.connect(lp2).removeStrategy(33, false)).to.be.revertedWithCustomError( + vault, + "InvalidStrategy" + ); + await expect(vault.connect(lp2).removeStrategy(5, false)).to.be.revertedWithCustomError(vault, "InvalidStrategy"); + await expect(vault.connect(lp2).removeStrategy(1, false)).to.be.revertedWithCustomError( + vault, + "CannotRemoveStrategyWithAssets" + ); + + // When forced, it can remove the strategy + await expect(vault.connect(lp2).removeStrategy(1, true)) + .to.emit(vault, "StrategyRemoved") + .withArgs(strategies[1], 1); + await invariantChecks(vault); + }); + variant.tagit("It can removeStrategy only if doesn't have funds", async () => { const { deployVault, lp, lp2, currency, grantRole, grantForwardToStrategy, strategies } = await helpers.loadFixture(variant.fixture);