From 78268142bb6f3054e821233a9c460b797253f6e0 Mon Sep 17 00:00:00 2001 From: amusingaxl <112016538+amusingaxl@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:59:20 -0300 Subject: [PATCH] refactor: rename `Batched` spells as `Grouped`; switch to dynamic storage ilk list --- README.md | 18 +++- ...Spell.sol => DssGroupedEmergencySpell.sol} | 86 +++++++++---------- ...ssGroupedEmergencySpell.t.integration.sol} | 77 ++++++++++++++--- ...rSpell.sol => GroupedClipBreakerSpell.sol} | 30 +++---- ...GroupedClipBreakerSpell.t.integration.sol} | 20 ++--- ...WipeSpell.sol => GroupedLineWipeSpell.sol} | 28 +++--- ...=> GroupedLineWipeSpell.t.integration.sol} | 20 ++--- 7 files changed, 168 insertions(+), 111 deletions(-) rename src/{DssBatchedEmergencySpell.sol => DssGroupedEmergencySpell.sol} (61%) rename src/{DssBatchedEmergencySpell.t.integration.sol => DssGroupedEmergencySpell.t.integration.sol} (61%) rename src/clip-breaker/{BatchedClipBreakerSpell.sol => GroupedClipBreakerSpell.sol} (80%) rename src/clip-breaker/{BatchedClipBreakerSpell.t.integration.sol => GroupedClipBreakerSpell.t.integration.sol} (91%) rename src/line-wipe/{BatchedLineWipeSpell.sol => GroupedLineWipeSpell.sol} (79%) rename src/line-wipe/{BatchedLineWipeSpell.t.integration.sol => GroupedLineWipeSpell.t.integration.sol} (92%) diff --git a/README.md b/README.md index 1f07f88..e8048f3 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ TBD. ## Implemented Actions -| Description | Single-ilk | Batched | Multi-ilk / Global | +| Description | Single-ilk | Grouped | Multi-ilk / Global | | :---------- | :--------: | :-----: | :----------------: | | Wipe `line` | :white_check_mark: | :white_check_mark: | :white_check_mark: | | Set `Clip` breaker | :white_check_mark: | :white_check_mark: | :white_check_mark: | @@ -123,12 +123,24 @@ constructor. Some types of emergency spells may come in 3 flavors: 1. Single-ilk: applies the desired spell action to a single pre-defined ilk. -1. Batched: applies the desired spell action to a list of related ilks (i.e.: `ETH-A`, `ETH-B` and `ETH-C`) +1. Grouped: applies the desired spell action to a list of related ilks (i.e.: `ETH-A`, `ETH-B` and `ETH-C`) 1. Multi: applies the desired spell action to all applicable ilks. Furthermore, this repo provides on-chain factories for single ilk emergency spells to make it easier to deploy for new ilks. + +### About storage variables in `DssGroupedEmergencySpell` + +Regular spell actions are executed through a `delegatecall` from `MCD_PAUSE_PROXY`. For that reason, they usually should +not have storage variables, as they would be accessing and interacting with `MCD_PAUSE_PROXY`'s storage, not their own. + +However, Emergency Spells are not required to interact with `MCD_PAUSE` and `MCD_PAUSE_PROXY` at all. They execute +actions through regular `call` on `Mom` contracts, so we do not have this limitation. + +Even if the contract is somehow misused and used as a regular spell, interacting with `MCD_PAUSE`, there would not be a +problem because the storage should not be changed outside the constructor by the concrete implementations. + ### About the `done()` function Conforming spells have a [`done`][spell-done] public storage variable which is `false` when the spell is deployed and @@ -138,7 +150,7 @@ An emergency spell is not meant to be cast, but it can be scheduled multiple tim storage variable, it becomes a getter function that will return: - `false`: if the emergency spell can be scheduled in the current state, given it is lifted to the hat. - `true`: if the desired effects of the spell can be verified or if there is anything that would prevent the spell from - being scheduled (i.e.: bad system config) + being scheduled (i.e.: bad system config). Generally speaking, `done` should almost always return `false` for any emergency spell. If it returns `true` it means it has just been scheduled or there is most likely something wrong with the modules touched by it. The exception is the diff --git a/src/DssBatchedEmergencySpell.sol b/src/DssGroupedEmergencySpell.sol similarity index 61% rename from src/DssBatchedEmergencySpell.sol rename to src/DssGroupedEmergencySpell.sol index ad622c3..c6dda43 100644 --- a/src/DssBatchedEmergencySpell.sol +++ b/src/DssGroupedEmergencySpell.sol @@ -17,34 +17,30 @@ pragma solidity ^0.8.16; import {DssEmergencySpell, DssEmergencySpellLike} from "./DssEmergencySpell.sol"; -interface DssBatchedEmergencySpellLike is DssEmergencySpellLike { +interface DssGroupedEmergencySpellLike is DssEmergencySpellLike { function ilks() external view returns (bytes32[] memory); + function emergencyActionsInBatch(uint256 start, uint256 end) external; } -/// @title Batched Emergency Spell -/// @notice Defines the base implementation for batched emergency spells. +/// @title Grouped Emergency Spell +/// @notice Defines the base implementation for grouped emergency spells. /// @custom:authors [amusingaxl] /// @custom:reviewers [] /// @custom:auditors [] /// @custom:bounties [] -abstract contract DssBatchedEmergencySpell is DssEmergencySpell, DssBatchedEmergencySpellLike { +abstract contract DssGroupedEmergencySpell is DssEmergencySpell, DssGroupedEmergencySpellLike { /// @dev The min size for the list of ilks - uint256 public constant MIN_ILKS = 2; - /// @dev The max size for the list of ilks - uint256 public constant MAX_ILKS = 3; - - /// @dev The total number of ilks in the spell. - uint256 internal immutable _totalIlks; - /// @dev The 0th ilk to which the spell should be applicable. - bytes32 internal immutable _ilk0; - /// @dev The 1st ilk to which the spell should be applicable. - bytes32 internal immutable _ilk1; - /// @dev The 2nd ilk to which the spell should be applicable. - bytes32 internal immutable _ilk2; + uint256 private constant MIN_ILKS = 1; + + /// @notice The list of ilks to which the spell is applicable. + /// @dev While spells should not have storage variables, we can make an exception here because this spell should not + /// change its own storage, an therefore, could not overwrite the PauseProxy state through delegate call even + /// if used incorrectly. + bytes32[] private ilkList; /// @param _ilks The list of ilks for which the spell should be applicable /// @dev The list size is be at least 2 and less than or equal to 3. - /// The batched spell is meant to be used for ilks that are a variation of tha same collateral gem + /// The grouped spell is meant to be used for ilks that are a variation of tha same collateral gem /// (i.e.: ETH-A, ETH-B, ETH-C) /// There has never been a case where MCD onboarded 4 or more ilks for the same collateral gem. /// For cases where there is only one ilk for the same collateral gem, use the single-ilk version. @@ -52,33 +48,23 @@ abstract contract DssBatchedEmergencySpell is DssEmergencySpell, DssBatchedEmerg // This is a workaround to Solidity's lack of support for immutable arrays, as described in // https://github.com/ethereum/solidity/issues/12587 uint256 len = _ilks.length; - require(len >= MIN_ILKS, "DssBatchedEmergencySpell/too-few-ilks"); - require(len <= MAX_ILKS, "DssBatchedEmergencySpell/too-many-ilks"); - _totalIlks = len; - - _ilk0 = _ilks[0]; - _ilk1 = _ilks[1]; - // Only ilk2 is not guaranteed to exist. - _ilk2 = len > 2 ? _ilks[2] : bytes32(0); + require(len >= MIN_ILKS, "DssGroupedEmergencySpell/too-few-ilks"); + + ilkList = _ilks; } /// @notice Returns the list of ilks to which the spell is applicable. - /// @return _ilks The list of ilks - function ilks() public view returns (bytes32[] memory _ilks) { - _ilks = new bytes32[](_totalIlks); - _ilks[0] = _ilk0; - _ilks[1] = _ilk1; - if (_totalIlks > 2) { - _ilks[2] = _ilk2; - } + function ilks() external view returns (bytes32[] memory) { + return ilkList; } /// @notice Returns the spell description. function description() external view returns (string memory) { // Join the list of ilks into a comma-separated string - string memory buf = string.concat(_bytes32ToString(_ilk0), ", ", _bytes32ToString(_ilk1)); - if (_totalIlks > 2) { - buf = string.concat(buf, ", ", _bytes32ToString(_ilk2)); + string memory buf = _bytes32ToString(ilkList[0]); + // Start from one because the first item was already added. + for (uint256 i = 1; i < ilkList.length; i++) { + buf = string.concat(buf, ", ", _bytes32ToString(ilkList[i])); } return string.concat(_descriptionPrefix(), " ", buf); @@ -106,10 +92,24 @@ abstract contract DssBatchedEmergencySpell is DssEmergencySpell, DssBatchedEmerg /// @inheritdoc DssEmergencySpell function _emergencyActions() internal override { - _emergencyActions(_ilk0); - _emergencyActions(_ilk1); - if (_totalIlks > 2) { - _emergencyActions(_ilk2); + for (uint256 i = 0; i < ilkList.length; i++) { + _emergencyActions(ilkList[i]); + } + } + + /** + * @notice Executes the emergency actions for all ilks in the batch. + * @dev This is an escape hatch to prevent the spell from being blocked in case it would hit the block gas limit. + * In case `end` is greater than the ilk registry length, the iteration will be automatically capped. + * @param start The index to start the iteration (inclusive). + * @param end The index to stop the iteration (inclusive). + */ + function emergencyActionsInBatch(uint256 start, uint256 end) external { + end = end > ilkList.length - 1 ? ilkList.length - 1 : end; + require(start <= end, "DssGroupedEmergencySpell/bad-iteration"); + + for (uint256 i = start; i <= end; i++) { + _emergencyActions(ilkList[i]); } } @@ -120,9 +120,9 @@ abstract contract DssBatchedEmergencySpell is DssEmergencySpell, DssBatchedEmerg /// @notice Returns whether the spell is done for all ilks or not. /// @return res Whether the spells is done or not. function done() external view returns (bool res) { - res = _done(_ilk0) && _done(_ilk1); - if (_totalIlks > 2) { - res = res && _done(_ilk2); + res = true; + for (uint256 i = 0; i < ilkList.length; i++) { + res = res && _done(ilkList[i]); } } diff --git a/src/DssBatchedEmergencySpell.t.integration.sol b/src/DssGroupedEmergencySpell.t.integration.sol similarity index 61% rename from src/DssBatchedEmergencySpell.t.integration.sol rename to src/DssGroupedEmergencySpell.t.integration.sol index c71cd53..1add72b 100644 --- a/src/DssBatchedEmergencySpell.t.integration.sol +++ b/src/DssGroupedEmergencySpell.t.integration.sol @@ -17,37 +17,39 @@ pragma solidity ^0.8.16; import {stdStorage, StdStorage} from "forge-std/Test.sol"; import {DssTest, DssInstance, MCD, GodMode} from "dss-test/DssTest.sol"; -import {DssBatchedEmergencySpell} from "./DssBatchedEmergencySpell.sol"; +import {DssGroupedEmergencySpell} from "./DssGroupedEmergencySpell.sol"; -contract DssBatchedEmergencySpellImpl is DssBatchedEmergencySpell { - mapping(bytes32 => bool) internal _isDone; +contract DssGroupedEmergencySpellImpl is DssGroupedEmergencySpell { + mapping(bytes32 => bool) public isDone; function setDone(bytes32 ilk, bool val) external { - _isDone[ilk] = val; + isDone[ilk] = val; } function _descriptionPrefix() internal pure override returns (string memory) { - return "Batched Emergency Spell:"; + return "Grouped Emergency Spell:"; } event EmergencyAction(bytes32 indexed ilk); - constructor(bytes32[] memory _ilks) DssBatchedEmergencySpell(_ilks) {} + constructor(bytes32[] memory _ilks) DssGroupedEmergencySpell(_ilks) {} function _emergencyActions(bytes32 ilk) internal override { emit EmergencyAction(ilk); + isDone[ilk] = true; } function _done(bytes32 ilk) internal view override returns (bool) { - return _isDone[ilk]; + return isDone[ilk]; } } -contract DssBatchedEmergencySpellTest is DssTest { +contract DssGroupedEmergencySpellTest is DssTest { address constant CHAINLOG = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; DssInstance dss; - DssBatchedEmergencySpellImpl spell2; - DssBatchedEmergencySpellImpl spell3; + DssGroupedEmergencySpellImpl spell2; + DssGroupedEmergencySpellImpl spell3; + DssGroupedEmergencySpellImpl spellN; address pause; function setUp() public { @@ -60,17 +62,27 @@ contract DssBatchedEmergencySpellTest is DssTest { bytes32[] memory ilks2 = new bytes32[](2); ilks2[0] = "WSTETH-A"; ilks2[1] = "WSTETH-B"; - spell2 = new DssBatchedEmergencySpellImpl(ilks2); + spell2 = new DssGroupedEmergencySpellImpl(ilks2); bytes32[] memory ilks3 = new bytes32[](3); ilks3[0] = "ETH-A"; ilks3[1] = "ETH-B"; ilks3[2] = "ETH-C"; - spell3 = new DssBatchedEmergencySpellImpl(ilks3); + spell3 = new DssGroupedEmergencySpellImpl(ilks3); + bytes32[] memory ilksN = new bytes32[](8); + ilksN[0] = "ETH-A"; + ilksN[1] = "ETH-B"; + ilksN[2] = "ETH-C"; + ilksN[3] = "WSTETH-A"; + ilksN[4] = "WSTETH-B"; + ilksN[5] = "WBTC-A"; + ilksN[6] = "WBTC-B"; + ilksN[7] = "WBTC-C"; + spellN = new DssGroupedEmergencySpellImpl(ilksN); } function testDescription() public view { - assertEq(spell2.description(), "Batched Emergency Spell: WSTETH-A, WSTETH-B"); - assertEq(spell3.description(), "Batched Emergency Spell: ETH-A, ETH-B, ETH-C"); + assertEq(spell2.description(), "Grouped Emergency Spell: WSTETH-A, WSTETH-B"); + assertEq(spell3.description(), "Grouped Emergency Spell: ETH-A, ETH-B, ETH-C"); } function testEmergencyActions() public { @@ -87,6 +99,43 @@ contract DssBatchedEmergencySpellTest is DssTest { vm.expectEmit(true, true, true, true); emit EmergencyAction("ETH-C"); spell3.schedule(); + + vm.expectEmit(true, true, true, true); + emit EmergencyAction("ETH-A"); + vm.expectEmit(true, true, true, true); + emit EmergencyAction("ETH-B"); + vm.expectEmit(true, true, true, true); + emit EmergencyAction("ETH-C"); + vm.expectEmit(true, true, true, true); + emit EmergencyAction("WSTETH-A"); + vm.expectEmit(true, true, true, true); + emit EmergencyAction("WSTETH-B"); + vm.expectEmit(true, true, true, true); + emit EmergencyAction("WBTC-A"); + vm.expectEmit(true, true, true, true); + emit EmergencyAction("WBTC-B"); + vm.expectEmit(true, true, true, true); + emit EmergencyAction("WBTC-C"); + spellN.schedule(); + } + + function testEmergencyActionsInBatches_Fuzz(uint256 batchSize) public { + uint256 count = spellN.ilks().length; + batchSize = bound(batchSize, 1, count); + uint256 start = 0; + // End is inclusive, so we need to subtract 1 + uint256 end = start + batchSize - 1; + + assertFalse(spellN.done(), "spellN unexpectedly done"); + + while (start < count) { + spellN.emergencyActionsInBatch(start, end); + + start += batchSize; + end += batchSize; + } + + assertTrue(spellN.done(), "spellN not done"); } function testDone() public { diff --git a/src/clip-breaker/BatchedClipBreakerSpell.sol b/src/clip-breaker/GroupedClipBreakerSpell.sol similarity index 80% rename from src/clip-breaker/BatchedClipBreakerSpell.sol rename to src/clip-breaker/GroupedClipBreakerSpell.sol index fce4581..d10f8f2 100644 --- a/src/clip-breaker/BatchedClipBreakerSpell.sol +++ b/src/clip-breaker/GroupedClipBreakerSpell.sol @@ -15,7 +15,7 @@ // along with this program. If not, see . pragma solidity ^0.8.16; -import {DssBatchedEmergencySpell} from "../DssBatchedEmergencySpell.sol"; +import {DssGroupedEmergencySpell} from "../DssGroupedEmergencySpell.sol"; interface ClipperMomLike { function setBreaker(address clip, uint256 level, uint256 delay) external; @@ -30,13 +30,13 @@ interface IlkRegistryLike { function xlip(bytes32 ilk) external view returns (address); } -/// @title Emergency Spell: Batched Clip Breaker +/// @title Emergency Spell: Grouped Clip Breaker /// @notice Prevents further collateral auctions to be held in the respective Clip contracts. /// @custom:authors [amusingaxl] /// @custom:reviewers [] /// @custom:auditors [] /// @custom:bounties [] -contract BatchedClipBreakerSpell is DssBatchedEmergencySpell { +contract GroupedClipBreakerSpell is DssGroupedEmergencySpell { /// @notice The ClipperMom from chainlog. ClipperMomLike public immutable clipperMom = ClipperMomLike(_log.getAddress("CLIPPER_MOM")); /// @notice The IlkRegistry from chainlog. @@ -54,19 +54,17 @@ contract BatchedClipBreakerSpell is DssBatchedEmergencySpell { /// @param _ilks The list of ilks for which the spell should be applicable /// @dev The list size is be at least 2 and less than or equal to 3. - /// The batched spell is meant to be used for ilks that are a variation of tha same collateral gem + /// The grouped spell is meant to be used for ilks that are a variation of tha same collateral gem /// (i.e.: ETH-A, ETH-B, ETH-C) - /// There has never been a case where MCD onboarded 4 or more ilks for the same collateral gem. - /// For cases where there is only one ilk for the same collateral gem, use the single-ilk version. - constructor(bytes32[] memory _ilks) DssBatchedEmergencySpell(_ilks) {} + constructor(bytes32[] memory _ilks) DssGroupedEmergencySpell(_ilks) {} - /// @inheritdoc DssBatchedEmergencySpell + /// @inheritdoc DssGroupedEmergencySpell function _descriptionPrefix() internal pure override returns (string memory) { - return "Emergency Spell | Batched Clip Breaker:"; + return "Emergency Spell | Grouped Clip Breaker:"; } /// @notice Sets the breaker for the related Clip contract. - /// @inheritdoc DssBatchedEmergencySpell + /// @inheritdoc DssGroupedEmergencySpell function _emergencyActions(bytes32 _ilk) internal override { address clip = ilkReg.xlip(_ilk); clipperMom.setBreaker(clip, BREAKER_LEVEL, BREAKER_DELAY); @@ -99,22 +97,22 @@ contract BatchedClipBreakerSpell is DssBatchedEmergencySpell { } } -/// @title Emergency Spell Factory: Batched Clip Breaker -/// @notice On-chain factory to deploy Batched Clip Breaker emergency spells. +/// @title Emergency Spell Factory: Grouped Clip Breaker +/// @notice On-chain factory to deploy Grouped Clip Breaker emergency spells. /// @custom:authors [amusingaxl] /// @custom:reviewers [] /// @custom:auditors [] /// @custom:bounties [] -contract BatchedClipBreakerFactory { - /// @notice A new BatchedClipBreakerSpell has been deployed. +contract GroupedClipBreakerFactory { + /// @notice A new GroupedClipBreakerSpell has been deployed. /// @param ilks The list of ilks for which the spell is applicable. /// @param spell The deployed spell address. event Deploy(bytes32[] indexed ilks, address spell); - /// @notice Deploys a BatchedClipBreakerSpell contract. + /// @notice Deploys a GroupedClipBreakerSpell contract. /// @param ilks The list of ilks for which the spell is applicable. function deploy(bytes32[] memory ilks) external returns (address spell) { - spell = address(new BatchedClipBreakerSpell(ilks)); + spell = address(new GroupedClipBreakerSpell(ilks)); emit Deploy(ilks, spell); } } diff --git a/src/clip-breaker/BatchedClipBreakerSpell.t.integration.sol b/src/clip-breaker/GroupedClipBreakerSpell.t.integration.sol similarity index 91% rename from src/clip-breaker/BatchedClipBreakerSpell.t.integration.sol rename to src/clip-breaker/GroupedClipBreakerSpell.t.integration.sol index e248d01..b0351cc 100644 --- a/src/clip-breaker/BatchedClipBreakerSpell.t.integration.sol +++ b/src/clip-breaker/GroupedClipBreakerSpell.t.integration.sol @@ -18,7 +18,7 @@ pragma solidity ^0.8.16; import {stdStorage, StdStorage} from "forge-std/Test.sol"; import {DssTest, DssInstance, MCD} from "dss-test/DssTest.sol"; import {DssEmergencySpellLike} from "../DssEmergencySpell.sol"; -import {BatchedClipBreakerSpell, BatchedClipBreakerFactory} from "./BatchedClipBreakerSpell.sol"; +import {GroupedClipBreakerSpell, GroupedClipBreakerFactory} from "./GroupedClipBreakerSpell.sol"; interface IlkRegistryLike { function xlip(bytes32 ilk) external view returns (address); @@ -34,7 +34,7 @@ interface ClipLike { function deny(address who) external; } -abstract contract BatchedClipBreakerSpellTest is DssTest { +abstract contract GroupedClipBreakerSpellTest is DssTest { using stdStorage for StdStorage; address constant CHAINLOG = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; @@ -47,7 +47,7 @@ abstract contract BatchedClipBreakerSpellTest is DssTest { ClipLike clipA; ClipLike clipB; ClipLike clipC; - BatchedClipBreakerFactory factory; + GroupedClipBreakerFactory factory; DssEmergencySpellLike spell; function setUp() public { @@ -60,7 +60,7 @@ abstract contract BatchedClipBreakerSpellTest is DssTest { ilkReg = IlkRegistryLike(dss.chainlog.getAddress("ILK_REGISTRY")); clipperMom = ClipperMomLike(dss.chainlog.getAddress("CLIPPER_MOM")); _setUpSub(); - factory = new BatchedClipBreakerFactory(); + factory = new GroupedClipBreakerFactory(); spell = DssEmergencySpellLike(factory.deploy(ilks)); stdstore.target(chief).sig("hat()").checked_write(address(spell)); @@ -152,7 +152,7 @@ abstract contract BatchedClipBreakerSpellTest is DssTest { event SetBreaker(bytes32 indexed ilk, address indexed clip); } -contract EthBatchedClipBreakerSpellTest is BatchedClipBreakerSpellTest { +contract EthGroupedClipBreakerSpellTest is GroupedClipBreakerSpellTest { function _setUpSub() internal override { clipA = ClipLike(ilkReg.xlip("ETH-A")); clipB = ClipLike(ilkReg.xlip("ETH-B")); @@ -164,11 +164,11 @@ contract EthBatchedClipBreakerSpellTest is BatchedClipBreakerSpellTest { } function testDescription() public view { - assertEq(spell.description(), "Emergency Spell | Batched Clip Breaker: ETH-A, ETH-B, ETH-C"); + assertEq(spell.description(), "Emergency Spell | Grouped Clip Breaker: ETH-A, ETH-B, ETH-C"); } } -contract WstethBatchedClipBreakerSpellTest is BatchedClipBreakerSpellTest { +contract WstethGroupedClipBreakerSpellTest is GroupedClipBreakerSpellTest { function _setUpSub() internal override { clipA = ClipLike(ilkReg.xlip("WSTETH-A")); clipB = ClipLike(ilkReg.xlip("WSTETH-B")); @@ -178,11 +178,11 @@ contract WstethBatchedClipBreakerSpellTest is BatchedClipBreakerSpellTest { } function testDescription() public view { - assertEq(spell.description(), "Emergency Spell | Batched Clip Breaker: WSTETH-A, WSTETH-B"); + assertEq(spell.description(), "Emergency Spell | Grouped Clip Breaker: WSTETH-A, WSTETH-B"); } } -contract WbtcBatchedClipBreakerSpellTest is BatchedClipBreakerSpellTest { +contract WbtcGroupedClipBreakerSpellTest is GroupedClipBreakerSpellTest { function _setUpSub() internal override { clipA = ClipLike(ilkReg.xlip("WBTC-A")); clipB = ClipLike(ilkReg.xlip("WBTC-B")); @@ -194,6 +194,6 @@ contract WbtcBatchedClipBreakerSpellTest is BatchedClipBreakerSpellTest { } function testDescription() public view { - assertEq(spell.description(), "Emergency Spell | Batched Clip Breaker: WBTC-A, WBTC-B, WBTC-C"); + assertEq(spell.description(), "Emergency Spell | Grouped Clip Breaker: WBTC-A, WBTC-B, WBTC-C"); } } diff --git a/src/line-wipe/BatchedLineWipeSpell.sol b/src/line-wipe/GroupedLineWipeSpell.sol similarity index 79% rename from src/line-wipe/BatchedLineWipeSpell.sol rename to src/line-wipe/GroupedLineWipeSpell.sol index 2e59ae7..103aa79 100644 --- a/src/line-wipe/BatchedLineWipeSpell.sol +++ b/src/line-wipe/GroupedLineWipeSpell.sol @@ -10,7 +10,7 @@ // along with this program. If not, see . pragma solidity ^0.8.16; -import {DssBatchedEmergencySpell} from "../DssBatchedEmergencySpell.sol"; +import {DssGroupedEmergencySpell} from "../DssGroupedEmergencySpell.sol"; interface LineMomLike { function autoLine() external view returns (address); @@ -34,13 +34,13 @@ interface VatLike { function wards(address who) external view returns (uint256); } -/// @title Emergency Spell: Batched Line Wipe +/// @title Emergency Spell: Grouped Line Wipe /// @notice Prevents further debt from being generated for the specified ilks. /// @custom:authors [amusingaxl] /// @custom:reviewers [] /// @custom:auditors [] /// @custom:bounties [] -contract BatchedLineWipeSpell is DssBatchedEmergencySpell { +contract GroupedLineWipeSpell is DssGroupedEmergencySpell { /// @notice The LineMom from chainlog. LineMomLike public immutable lineMom = LineMomLike(_log.getAddress("LINE_MOM")); /// @notice The AutoLine IAM. @@ -54,15 +54,13 @@ contract BatchedLineWipeSpell is DssBatchedEmergencySpell { /// @param _ilks The list of ilks for which the spell should be applicable /// @dev The list size is be at least 2 and less than or equal to 3. - /// The batched spell is meant to be used for ilks that are a variation of tha same collateral gem + /// The grouped spell is meant to be used for ilks that are a variation of tha same collateral gem /// (i.e.: ETH-A, ETH-B, ETH-C) - /// There has never been a case where MCD onboarded 4 or more ilks for the same collateral gem. - /// For cases where there is only one ilk for the same collateral gem, use the single-ilk version. - constructor(bytes32[] memory _ilks) DssBatchedEmergencySpell(_ilks) {} + constructor(bytes32[] memory _ilks) DssGroupedEmergencySpell(_ilks) {} - /// @inheritdoc DssBatchedEmergencySpell + /// @inheritdoc DssGroupedEmergencySpell function _descriptionPrefix() internal pure override returns (string memory) { - return "Emergency Spell | Batched Line Wipe:"; + return "Emergency Spell | Grouped Line Wipe:"; } /// @notice Wipes the line for the specified ilk.. @@ -85,22 +83,22 @@ contract BatchedLineWipeSpell is DssBatchedEmergencySpell { } } -/// @title Emergency Spell Factory: Batched Line Wipe -/// @notice On-chain factory to deploy Batched Line Wipe emergency spells. +/// @title Emergency Spell Factory: Grouped Line Wipe +/// @notice On-chain factory to deploy Grouped Line Wipe emergency spells. /// @custom:authors [amusingaxl] /// @custom:reviewers [] /// @custom:auditors [] /// @custom:bounties [] -contract BatchedLineWipeFactory { - /// @notice A new BatchedLineWipeSpell has been deployed. +contract GroupedLineWipeFactory { + /// @notice A new GroupedLineWipeSpell has been deployed. /// @param ilks The list of ilks for which the spell is applicable. /// @param spell The deployed spell address. event Deploy(bytes32[] indexed ilks, address spell); - /// @notice Deploys a BatchedLineWipeSpell contract. + /// @notice Deploys a GroupedLineWipeSpell contract. /// @param ilks The list of ilks for which the spell is applicable. function deploy(bytes32[] memory ilks) external returns (address spell) { - spell = address(new BatchedLineWipeSpell(ilks)); + spell = address(new GroupedLineWipeSpell(ilks)); emit Deploy(ilks, spell); } } diff --git a/src/line-wipe/BatchedLineWipeSpell.t.integration.sol b/src/line-wipe/GroupedLineWipeSpell.t.integration.sol similarity index 92% rename from src/line-wipe/BatchedLineWipeSpell.t.integration.sol rename to src/line-wipe/GroupedLineWipeSpell.t.integration.sol index 978e450..fe991e6 100644 --- a/src/line-wipe/BatchedLineWipeSpell.t.integration.sol +++ b/src/line-wipe/GroupedLineWipeSpell.t.integration.sol @@ -18,7 +18,7 @@ pragma solidity ^0.8.16; import {stdStorage, StdStorage} from "forge-std/Test.sol"; import {DssTest, DssInstance, MCD} from "dss-test/DssTest.sol"; import {DssEmergencySpellLike} from "../DssEmergencySpell.sol"; -import {BatchedLineWipeSpell, BatchedLineWipeFactory} from "./BatchedLineWipeSpell.sol"; +import {GroupedLineWipeSpell, GroupedLineWipeFactory} from "./GroupedLineWipeSpell.sol"; interface AutoLineLike { function ilks(bytes32 ilk) @@ -36,7 +36,7 @@ interface VatLike { function file(bytes32 ilk, bytes32 what, uint256 data) external; } -abstract contract BatchedLineWipeSpellTest is DssTest { +abstract contract GroupedLineWipeSpellTest is DssTest { using stdStorage for StdStorage; address constant CHAINLOG = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; @@ -47,7 +47,7 @@ abstract contract BatchedLineWipeSpellTest is DssTest { LineMomLike lineMom; AutoLineLike autoLine; bytes32[] ilks; - BatchedLineWipeFactory factory; + GroupedLineWipeFactory factory; DssEmergencySpellLike spell; function setUp() public { @@ -61,7 +61,7 @@ abstract contract BatchedLineWipeSpellTest is DssTest { lineMom = LineMomLike(dss.chainlog.getAddress("LINE_MOM")); autoLine = AutoLineLike(dss.chainlog.getAddress("MCD_IAM_AUTO_LINE")); _setUpSub(); - factory = new BatchedLineWipeFactory(); + factory = new GroupedLineWipeFactory(); spell = DssEmergencySpellLike(factory.deploy(ilks)); stdstore.target(chief).sig("hat()").checked_write(address(spell)); @@ -186,7 +186,7 @@ abstract contract BatchedLineWipeSpellTest is DssTest { event Wipe(bytes32 indexed ilk); } -contract EthBatchedLineWipeSpellTest is BatchedLineWipeSpellTest { +contract EthGroupedLineWipeSpellTest is GroupedLineWipeSpellTest { function _setUpSub() internal override { ilks = new bytes32[](3); ilks[0] = "ETH-A"; @@ -195,11 +195,11 @@ contract EthBatchedLineWipeSpellTest is BatchedLineWipeSpellTest { } function testDescription() public view { - assertEq(spell.description(), "Emergency Spell | Batched Line Wipe: ETH-A, ETH-B, ETH-C"); + assertEq(spell.description(), "Emergency Spell | Grouped Line Wipe: ETH-A, ETH-B, ETH-C"); } } -contract WstethBatchedLineWipeSpellTest is BatchedLineWipeSpellTest { +contract WstethGroupedLineWipeSpellTest is GroupedLineWipeSpellTest { function _setUpSub() internal override { ilks = new bytes32[](2); ilks[0] = "WSTETH-A"; @@ -207,11 +207,11 @@ contract WstethBatchedLineWipeSpellTest is BatchedLineWipeSpellTest { } function testDescription() public view { - assertEq(spell.description(), "Emergency Spell | Batched Line Wipe: WSTETH-A, WSTETH-B"); + assertEq(spell.description(), "Emergency Spell | Grouped Line Wipe: WSTETH-A, WSTETH-B"); } } -contract WbtcBatchedLineWipeSpellTest is BatchedLineWipeSpellTest { +contract WbtcGroupedLineWipeSpellTest is GroupedLineWipeSpellTest { function _setUpSub() internal override { ilks = new bytes32[](3); ilks[0] = "WBTC-A"; @@ -227,6 +227,6 @@ contract WbtcBatchedLineWipeSpellTest is BatchedLineWipeSpellTest { } function testDescription() public view { - assertEq(spell.description(), "Emergency Spell | Batched Line Wipe: WBTC-A, WBTC-B, WBTC-C"); + assertEq(spell.description(), "Emergency Spell | Grouped Line Wipe: WBTC-A, WBTC-B, WBTC-C"); } }