diff --git a/src/spells/EmergencySpell_SparkLend_FreezeAllAssets.sol b/src/spells/EmergencySpell_SparkLend_FreezeAllAssets.sol index 8c27490..c2a1ade 100644 --- a/src/spells/EmergencySpell_SparkLend_FreezeAllAssets.sol +++ b/src/spells/EmergencySpell_SparkLend_FreezeAllAssets.sol @@ -7,12 +7,16 @@ contract EmergencySpell_SparkLend_FreezeAllAssets { address public immutable sparkLendFreezerMom; + bool public executed; + constructor(address sparklendFreezerMom_) { sparkLendFreezerMom = sparklendFreezerMom_; } function freeze() external { + require(!executed, "FreezeAllAssetsSpell/already-executed"); ISparkLendFreezerMom(sparkLendFreezerMom).freezeAllMarkets(); + executed = true; } } diff --git a/src/spells/EmergencySpell_SparkLend_FreezeSingleAsset.sol b/src/spells/EmergencySpell_SparkLend_FreezeSingleAsset.sol index 59d2375..3615928 100644 --- a/src/spells/EmergencySpell_SparkLend_FreezeSingleAsset.sol +++ b/src/spells/EmergencySpell_SparkLend_FreezeSingleAsset.sol @@ -8,13 +8,17 @@ contract EmergencySpell_SparkLend_FreezeSingleAsset { address public immutable sparkLendFreezerMom; address public immutable reserve; + bool public executed; + constructor(address sparklendFreezerMom_, address reserve_) { sparkLendFreezerMom = sparklendFreezerMom_; reserve = reserve_; } function freeze() external { + require(!executed, "FreezeSingleAssetSpell/already-executed"); ISparkLendFreezerMom(sparkLendFreezerMom).freezeMarket(reserve); + executed = true; } } diff --git a/test/IntegrationTests.t.sol b/test/IntegrationTests.t.sol index b95c9d8..249aea8 100644 --- a/test/IntegrationTests.t.sol +++ b/test/IntegrationTests.t.sol @@ -235,6 +235,28 @@ contract FreezeSingleAssetSpellFailures is IntegrationTestsBase { freezeAssetSpell.freeze(); } + function test_cannotCallTwice() external { + vm.prank(SPARK_PROXY); + aclManager.addRiskAdmin(address(freezer)); + + _vote(address(freezeAssetSpell)); + + assertTrue(authority.hat() == address(freezeAssetSpell)); + assertTrue( + authority.canCall( + address(freezeAssetSpell), + address(freezer), + freezer.freezeMarket.selector + ) + ); + + vm.startPrank(randomUser); // Demonstrate no ACL in spell + freezeAssetSpell.freeze(); + + vm.expectRevert("FreezeSingleAssetSpell/already-executed"); + freezeAssetSpell.freeze(); + } + } contract FreezeAllAssetsSpellFailures is IntegrationTestsBase { @@ -276,6 +298,28 @@ contract FreezeAllAssetsSpellFailures is IntegrationTestsBase { freezeAllAssetsSpell.freeze(); } + function test_cannotCallTwice() external { + vm.prank(SPARK_PROXY); + aclManager.addRiskAdmin(address(freezer)); + + _vote(address(freezeAllAssetsSpell)); + + assertTrue(authority.hat() == address(freezeAllAssetsSpell)); + assertTrue( + authority.canCall( + address(freezeAllAssetsSpell), + address(freezer), + freezer.freezeMarket.selector + ) + ); + + vm.startPrank(randomUser); // Demonstrate no ACL in spell + freezeAllAssetsSpell.freeze(); + + vm.expectRevert("FreezeAllAssetsSpell/already-executed"); + freezeAllAssetsSpell.freeze(); + } + } contract FreezeSingleAssetSpellTest is IntegrationTestsBase { @@ -334,9 +378,13 @@ contract FreezeSingleAssetSpellTest is IntegrationTestsBase { repayAmount ); + assertEq(FreezeSingleAssetSpell(freezeAssetSpell).executed(), false); + vm.prank(randomUser); // Demonstrate no ACL in spell FreezeSingleAssetSpell(freezeAssetSpell).freeze(); + assertEq(FreezeSingleAssetSpell(freezeAssetSpell).executed(), true); + _checkUserActionsFrozen( asset, supplyAmount, @@ -417,9 +465,13 @@ contract FreezeAllAssetsSpellTest is IntegrationTestsBase { assertEq(untestedReserves.length, 0); + assertEq(FreezeAllAssetsSpell(freezeAllAssetsSpell).executed(), false); + // Freeze all assets in the protocol vm.prank(randomUser); // Demonstrate no ACL in spell - FreezeSingleAssetSpell(freezeAllAssetsSpell).freeze(); + FreezeAllAssetsSpell(freezeAllAssetsSpell).freeze(); + + assertEq(FreezeAllAssetsSpell(freezeAllAssetsSpell).executed(), true); // Check that protocol is working as expected after the freeze spell for all assets for (uint256 i = 0; i < reserves.length; i++) {