From c306df9173d963a6f08d98c1c1e48e7c0026caa7 Mon Sep 17 00:00:00 2001 From: 0xLaz3r <219995259+0xLaz3r@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:49:13 +0100 Subject: [PATCH 1/5] feat: :sparkles: add starguard's plot and drop actions --- src/DssAction.t.sol | 56 ++++++++++++++++++++++++++++++++ src/DssExecLib.sol | 19 +++++++++++ src/mocks/MockDssSpellAction.sol | 8 +++++ src/mocks/MockStarGuard.sol | 42 ++++++++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 src/mocks/MockStarGuard.sol diff --git a/src/DssAction.t.sol b/src/DssAction.t.sol index 136028c..0358706 100644 --- a/src/DssAction.t.sol +++ b/src/DssAction.t.sol @@ -28,6 +28,7 @@ import {MockToken} from "./mocks/MockToken.sol"; import {MockValue} from "./mocks/MockValue.sol"; import {MockOracle} from "./mocks/MockOracle.sol"; import {MockOsm} from "./mocks/MockOsm.sol"; +import {MockStarGuard} from "./mocks/MockStarGuard.sol"; import {MockStarProxy} from "./mocks/MockStarProxy.sol"; import {MockStarSpell} from "./mocks/MockStarSpell.sol"; @@ -1328,4 +1329,59 @@ contract DssActionTest is Test { // Verify the spell was not executed assertFalse(proxy.executed(), "Spell should not have been executed when configured to fail"); } + + function test_plotStarSpell_success() public { + MockStarGuard guard = new MockStarGuard(); + address starSpell = address(0xBEEF); + bytes32 starSpellTag = keccak256("some-bytecode"); + + action.plotStarSpell_test(address(guard), starSpell, starSpellTag); + + assertEq(guard.plottedAddr(), starSpell, "StarGuard should have recorded the plotted spell"); + assertEq(guard.plottedTag(), starSpellTag, "StarGuard should have recorded the plotted tag"); + } + + function test_plotStarSpell_overwritesPreviousPlot() public { + MockStarGuard guard = new MockStarGuard(); + address firstSpell = address(0xBEEF); + bytes32 firstTag = keccak256("first-bytecode"); + address secondSpell = address(0xCAFE); + bytes32 secondTag = keccak256("second-bytecode"); + + action.plotStarSpell_test(address(guard), firstSpell, firstTag); + action.plotStarSpell_test(address(guard), secondSpell, secondTag); + + assertEq(guard.plottedAddr(), secondSpell, "StarGuard should overwrite the plotted spell"); + assertEq(guard.plottedTag(), secondTag, "StarGuard should overwrite the plotted tag"); + } + + function test_plotStarSpell_failure() public { + MockStarGuard guard = new MockStarGuard(); + guard.setShouldFail(true); + + vm.expectRevert("MockStarGuard/plot-failed"); + action.plotStarSpell_test(address(guard), address(0xBEEF), bytes32(uint256(1))); + } + + function test_dropStarSpell_success() public { + MockStarGuard guard = new MockStarGuard(); + address starSpell = address(0xBEEF); + bytes32 starSpellTag = keccak256("some-bytecode"); + + action.plotStarSpell_test(address(guard), starSpell, starSpellTag); + assertEq(guard.plottedAddr(), starSpell, "Precondition: spell should be plotted"); + + action.dropStarSpell_test(address(guard)); + + assertEq(guard.plottedAddr(), address(0), "StarGuard should have cleared the plotted spell"); + assertEq(guard.plottedTag(), bytes32(0), "StarGuard should have cleared the plotted tag"); + } + + function test_dropStarSpell_failure() public { + MockStarGuard guard = new MockStarGuard(); + guard.setShouldFail(true); + + vm.expectRevert("MockStarGuard/drop-failed"); + action.dropStarSpell_test(address(guard)); + } } diff --git a/src/DssExecLib.sol b/src/DssExecLib.sol index 12cb5b6..6624adc 100644 --- a/src/DssExecLib.sol +++ b/src/DssExecLib.sol @@ -154,6 +154,11 @@ interface ProxyLike { function exec(address target, bytes calldata args) external payable returns (bytes memory out); } +interface StarGuardLike { + function plot(address addr_, bytes32 tag_) external; + function drop() external; +} + /// @title DssExecLib - Sky Protocol's Executive Spellcrafting Library /// @notice This library provides a suite of functions for managing the Sky Protocol. /// @dev Includes functions for collateral management, system configuration, governance, and more. @@ -1197,4 +1202,18 @@ library DssExecLib { return (success, result); } + + /// @dev Whitelist a star spell on its star guard so it can be executed permissionlessly. + /// @param _starGuard The star guard contract that gates the star spell. + /// @param _starSpell The address of the star spell to whitelist. + /// @param _starSpellTag The expected codehash of the star spell. + function plotStarSpell(address _starGuard, address _starSpell, bytes32 _starSpellTag) public { + StarGuardLike(_starGuard).plot(_starSpell, _starSpellTag); + } + + /// @dev Remove the currently whitelisted star spell from a star guard. + /// @param _starGuard The star guard contract that gates the star spell. + function dropStarSpell(address _starGuard) public { + StarGuardLike(_starGuard).drop(); + } } diff --git a/src/mocks/MockDssSpellAction.sol b/src/mocks/MockDssSpellAction.sol index ee5d201..4d8e356 100644 --- a/src/mocks/MockDssSpellAction.sol +++ b/src/mocks/MockDssSpellAction.sol @@ -346,4 +346,12 @@ contract MockDssSpellAction is DssAction { function tryExecuteStarSpell_test(address starProxy, address starSpell) public returns (bool, bytes memory) { return DssExecLib.tryExecuteStarSpell(starProxy, starSpell); } + + function plotStarSpell_test(address starGuard, address starSpell, bytes32 starSpellTag) public { + DssExecLib.plotStarSpell(starGuard, starSpell, starSpellTag); + } + + function dropStarSpell_test(address starGuard) public { + DssExecLib.dropStarSpell(starGuard); + } } diff --git a/src/mocks/MockStarGuard.sol b/src/mocks/MockStarGuard.sol new file mode 100644 index 0000000..c4dd728 --- /dev/null +++ b/src/mocks/MockStarGuard.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// MockStarGuard.sol -- Mock Star Guard for testing +// +// Copyright (C) 2022-2025 Dai Foundation +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pragma solidity ^0.8.16; + +contract MockStarGuard { + address public plottedAddr; + bytes32 public plottedTag; + bool public shouldFail; + + function setShouldFail(bool _shouldFail) external { + shouldFail = _shouldFail; + } + + function plot(address addr_, bytes32 tag_) external { + require(!shouldFail, "MockStarGuard/plot-failed"); + plottedAddr = addr_; + plottedTag = tag_; + } + + function drop() external { + require(!shouldFail, "MockStarGuard/drop-failed"); + plottedAddr = address(0); + plottedTag = bytes32(0); + } +} From f688469869bafa18d9bba5660b5a9eacf90762d3 Mon Sep 17 00:00:00 2001 From: 0xLaz3r <219995259+0xLaz3r@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:55:57 +0100 Subject: [PATCH 2/5] docs: :memo: update README with starguard actions --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 52ccbc5..bd22e55 100644 --- a/README.md +++ b/README.md @@ -295,6 +295,8 @@ DssExecLib.setChangelogAddress("MCD_CLIP_CALC_XMPL-A", xmpl_calc); - `executeStarSpell(address _starProxy, address _starSpell)`: Execute a star spell through its star proxy. Returns the return data from the spell execution. - `tryExecuteStarSpell(address _starProxy, address _starSpell)`: Tries to execute a star spell through its star proxy using low-level call to avoid reverts in case of error. Returns a boolean indicating success and the return data or error message. +- `plotStarSpell(address _starGuard, address _starSpell, bytes32 _starSpellTag)`: Whitelist a star spell on its star guard so it can be executed permissionlessly. `_starSpellTag` is the expected codehash of the star spell. +- `dropStarSpell(address _starGuard)`: Remove the currently whitelisted star spell from a star guard. ### Misc From c1b436c08b96421ff936b4747708bc97da5100bf Mon Sep 17 00:00:00 2001 From: 0xLaz3r <219995259+0xLaz3r@users.noreply.github.com> Date: Thu, 30 Apr 2026 11:01:46 +0100 Subject: [PATCH 3/5] docs: :memo: update copyright years --- src/mocks/MockStarGuard.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mocks/MockStarGuard.sol b/src/mocks/MockStarGuard.sol index c4dd728..6d22062 100644 --- a/src/mocks/MockStarGuard.sol +++ b/src/mocks/MockStarGuard.sol @@ -2,7 +2,7 @@ // // MockStarGuard.sol -- Mock Star Guard for testing // -// Copyright (C) 2022-2025 Dai Foundation +// Copyright (C) 2022-2026 Dai Foundation // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by From a6ea702978e6c704e701222412f97afd2d89e410 Mon Sep 17 00:00:00 2001 From: 0xLaz3r <219995259+0xLaz3r@users.noreply.github.com> Date: Mon, 4 May 2026 11:58:11 +0100 Subject: [PATCH 4/5] docs: :memo: add missing comments to tests --- src/DssAction.t.sol | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/DssAction.t.sol b/src/DssAction.t.sol index 0358706..6507a79 100644 --- a/src/DssAction.t.sol +++ b/src/DssAction.t.sol @@ -1331,56 +1331,74 @@ contract DssActionTest is Test { } function test_plotStarSpell_success() public { + // Setup mock contract and spell parameters MockStarGuard guard = new MockStarGuard(); address starSpell = address(0xBEEF); bytes32 starSpellTag = keccak256("some-bytecode"); + // Plot the spell on the guard action.plotStarSpell_test(address(guard), starSpell, starSpellTag); + // Verify the guard recorded the correct spell address and tag assertEq(guard.plottedAddr(), starSpell, "StarGuard should have recorded the plotted spell"); assertEq(guard.plottedTag(), starSpellTag, "StarGuard should have recorded the plotted tag"); } function test_plotStarSpell_overwritesPreviousPlot() public { + // Setup mock contract and two distinct spell parameters MockStarGuard guard = new MockStarGuard(); address firstSpell = address(0xBEEF); bytes32 firstTag = keccak256("first-bytecode"); address secondSpell = address(0xCAFE); bytes32 secondTag = keccak256("second-bytecode"); + // Plot the first spell, then plot the second spell on top of it action.plotStarSpell_test(address(guard), firstSpell, firstTag); action.plotStarSpell_test(address(guard), secondSpell, secondTag); + // Verify the guard now reflects the second spell, overwriting the first assertEq(guard.plottedAddr(), secondSpell, "StarGuard should overwrite the plotted spell"); assertEq(guard.plottedTag(), secondTag, "StarGuard should overwrite the plotted tag"); } function test_plotStarSpell_failure() public { + // Setup mock contract MockStarGuard guard = new MockStarGuard(); + + // Configure the guard to fail guard.setShouldFail(true); + // This should revert vm.expectRevert("MockStarGuard/plot-failed"); action.plotStarSpell_test(address(guard), address(0xBEEF), bytes32(uint256(1))); } function test_dropStarSpell_success() public { + // Setup mock contract and spell parameters MockStarGuard guard = new MockStarGuard(); address starSpell = address(0xBEEF); bytes32 starSpellTag = keccak256("some-bytecode"); + // Plot a spell so we have something to drop action.plotStarSpell_test(address(guard), starSpell, starSpellTag); assertEq(guard.plottedAddr(), starSpell, "Precondition: spell should be plotted"); + // Drop the plotted spell action.dropStarSpell_test(address(guard)); + // Verify the guard cleared its recorded spell address and tag assertEq(guard.plottedAddr(), address(0), "StarGuard should have cleared the plotted spell"); assertEq(guard.plottedTag(), bytes32(0), "StarGuard should have cleared the plotted tag"); } function test_dropStarSpell_failure() public { + // Setup mock contract MockStarGuard guard = new MockStarGuard(); + + // Configure the guard to fail guard.setShouldFail(true); + // This should revert vm.expectRevert("MockStarGuard/drop-failed"); action.dropStarSpell_test(address(guard)); } From 1cefa2ed3202e70ea06e721702f91d65bcc8bbe4 Mon Sep 17 00:00:00 2001 From: 0xLaz3r <219995259+0xLaz3r@users.noreply.github.com> Date: Mon, 18 May 2026 18:24:17 -0300 Subject: [PATCH 5/5] docs: :memo: update minor comments --- README.md | 4 ++-- src/DssExecLib.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bd22e55..bcf328b 100644 --- a/README.md +++ b/README.md @@ -295,8 +295,8 @@ DssExecLib.setChangelogAddress("MCD_CLIP_CALC_XMPL-A", xmpl_calc); - `executeStarSpell(address _starProxy, address _starSpell)`: Execute a star spell through its star proxy. Returns the return data from the spell execution. - `tryExecuteStarSpell(address _starProxy, address _starSpell)`: Tries to execute a star spell through its star proxy using low-level call to avoid reverts in case of error. Returns a boolean indicating success and the return data or error message. -- `plotStarSpell(address _starGuard, address _starSpell, bytes32 _starSpellTag)`: Whitelist a star spell on its star guard so it can be executed permissionlessly. `_starSpellTag` is the expected codehash of the star spell. -- `dropStarSpell(address _starGuard)`: Remove the currently whitelisted star spell from a star guard. +- `plotStarSpell(address _starGuard, address _starSpell, bytes32 _starSpellTag)`: Whitelist a star spell in its star guard so it can be executed permissionlessly. `_starSpellTag` is the expected codehash of the star spell. +- `dropStarSpell(address _starGuard)`: Remove the currently whitelisted star spell from the star guard. ### Misc diff --git a/src/DssExecLib.sol b/src/DssExecLib.sol index 6624adc..57eb9c2 100644 --- a/src/DssExecLib.sol +++ b/src/DssExecLib.sol @@ -1203,7 +1203,7 @@ library DssExecLib { return (success, result); } - /// @dev Whitelist a star spell on its star guard so it can be executed permissionlessly. + /// @dev Whitelist a star spell in the corresponding star guard to enable permissionless execution. /// @param _starGuard The star guard contract that gates the star spell. /// @param _starSpell The address of the star spell to whitelist. /// @param _starSpellTag The expected codehash of the star spell.