@@ -20,7 +20,7 @@ import "dss-test/DssTest.sol";
2020import { MCD, DssInstance } from "dss-test/MCD.sol " ;
2121import { PASInstance } from "deploy/PASInstance.sol " ;
2222import { PASDeploy } from "deploy/PASDeploy.sol " ;
23- import { PASInit } from "deploy/PASInit.sol " ;
23+ import { PASInit, InitRateLimitConfig, InitControllerActionConfig } from "deploy/PASInit.sol " ;
2424import { BeamState } from "src/BeamState.sol " ;
2525import { Configurator, RateLimitsLike } from "src/Configurator.sol " ;
2626import { Timelock } from "src/timelock/Timelock.sol " ;
@@ -265,6 +265,51 @@ contract IntegrationTest is DssTest {
265265 this .initExtras ();
266266 }
267267
268+ function testInitLimitsAndControllerData () public {
269+ PASInstance memory freshPas = PASDeploy.deploy (address (this ), pauseProxy, MIN_DELAY);
270+
271+ // Setup rate limit configs
272+ InitRateLimitConfig[] memory rlConfigs = new InitRateLimitConfig [](2 );
273+ rlConfigs[0 ] = InitRateLimitConfig ({
274+ key: bytes32 (0 ),
275+ rateLimits: SPARK_RATE_LIMITS,
276+ maxAmount: 1_000_000 ether,
277+ slope: 100 ether
278+ });
279+ rlConfigs[1 ] = InitRateLimitConfig ({
280+ key: bytes32 (uint256 (1 )),
281+ rateLimits: address (0x42 ),
282+ maxAmount: 500_000 ether,
283+ slope: 50 ether
284+ });
285+
286+ // Setup controller action configs
287+ bytes memory actionData1 = abi.encodeWithSelector (bytes4 (0xdeadbeef ), uint256 (123 ));
288+ bytes memory actionData2 = abi.encodeWithSelector (bytes4 (0xcafebabe ), address (0x99 ));
289+ InitControllerActionConfig[] memory caConfigs = new InitControllerActionConfig [](2 );
290+ caConfigs[0 ] = InitControllerActionConfig ({data: actionData1, controller: SPARK_CONTROLLER});
291+ caConfigs[1 ] = InitControllerActionConfig ({data: actionData2, controller: address (0 )});
292+
293+ BeamState freshBeamState = BeamState (freshPas.beamState);
294+
295+ vm.startPrank (pauseProxy);
296+ PASInit.initLimitsAndControllerData (freshPas, rlConfigs, caConfigs);
297+ vm.stopPrank ();
298+
299+ // Verify init rate limits
300+ (uint256 maxAmount0 , uint256 slope0 ) = freshBeamState.initRateLimits (bytes32 (0 ), SPARK_RATE_LIMITS);
301+ assertEq (maxAmount0, 1_000_000 ether, "first rate limit maxAmount " );
302+ assertEq (slope0, 100 ether, "first rate limit slope " );
303+
304+ (uint256 maxAmount1 , uint256 slope1 ) = freshBeamState.initRateLimits (bytes32 (uint256 (1 )), address (0x42 ));
305+ assertEq (maxAmount1, 500_000 ether, "second rate limit maxAmount " );
306+ assertEq (slope1, 50 ether, "second rate limit slope " );
307+
308+ // Verify init controller actions
309+ assertEq (freshBeamState.initControllerActions (keccak256 (actionData1), SPARK_CONTROLLER), 1 , "first controller action " );
310+ assertEq (freshBeamState.initControllerActions (keccak256 (actionData2), address (0 )), 1 , "second controller action " );
311+ }
312+
268313 // ============================================================================
269314 // CoreCouncil Direct Actions (IMMEDIATE Role) Tests
270315 // ============================================================================
@@ -872,6 +917,32 @@ contract IntegrationTest is DssTest {
872917 bytes32 (uint256 (uint160 (testRecipient)))
873918 );
874919
920+ // ========================================
921+ // Phase 0: Init some defaults via spell (PASInit.initLimitsAndControllerData)
922+ // ========================================
923+ bytes32 spellRateLimitKey = keccak256 ("spell-init-key " );
924+ bytes memory spellControllerAction = abi.encodeWithSelector (
925+ ControllerLike.setMintRecipient.selector ,
926+ uint32 (7 ), // different domain than the timelock-onboarded action
927+ bytes32 (uint256 (uint160 (testRecipient)))
928+ );
929+ {
930+ InitRateLimitConfig[] memory rlConfigs = new InitRateLimitConfig [](1 );
931+ rlConfigs[0 ] = InitRateLimitConfig ({
932+ key: spellRateLimitKey,
933+ rateLimits: SPARK_RATE_LIMITS,
934+ maxAmount: 2_000_000e18 ,
935+ slope: 200_000e18
936+ });
937+
938+ InitControllerActionConfig[] memory caConfigs = new InitControllerActionConfig [](1 );
939+ caConfigs[0 ] = InitControllerActionConfig ({data: spellControllerAction, controller: SPARK_CONTROLLER});
940+
941+ vm.startPrank (pauseProxy);
942+ PASInit.initLimitsAndControllerData (pas, rlConfigs, caConfigs);
943+ vm.stopPrank ();
944+ }
945+
875946 // ========================================
876947 // Phase 1: Onboard via Timelock (Role 1)
877948 // ========================================
@@ -932,6 +1003,13 @@ contract IntegrationTest is DssTest {
9321003 assertEq (limits.slope, 100_000e18 , "init rate limit slope should be set " );
9331004 }
9341005 assertTrue (beamState.isControllerActionEnabled (keccak256 (setMintRecipientAction), SPARK_CONTROLLER), "controller action should be enabled " );
1006+ // Verify spell-configured defaults
1007+ {
1008+ BeamState.DefaultRateLimits memory spellLimits = beamState.getInitRateLimits (spellRateLimitKey, SPARK_RATE_LIMITS);
1009+ assertEq (spellLimits.maxAmount, 2_000_000e18 , "spell init rate limit maxAmount should be set " );
1010+ assertEq (spellLimits.slope, 200_000e18 , "spell init rate limit slope should be set " );
1011+ }
1012+ assertTrue (beamState.isControllerActionEnabled (keccak256 (spellControllerAction), SPARK_CONTROLLER), "spell controller action should be enabled " );
9351013
9361014 // ========================================
9371015 // Phase 2: Grant admin role to configurator and associate cBeam (Role 2 - direct)
@@ -973,6 +1051,20 @@ contract IntegrationTest is DssTest {
9731051 bytes32 expectedRecipient = bytes32 (uint256 (uint160 (testRecipient)));
9741052 assertEq (ControllerLike (SPARK_CONTROLLER).mintRecipients (6 ), expectedRecipient, "mintRecipient should be set on real controller " );
9751053
1054+ // 3c. cBeam sets rate limit using spell-configured defaults
1055+ vm.prank (cBeam);
1056+ configurator.setRateLimit (SPARK_RATE_LIMITS, spellRateLimitKey, 1_500_000e18 , 150_000e18 );
1057+ {
1058+ RateLimitsLike.RateLimitData memory data = RateLimitsLike (SPARK_RATE_LIMITS).getRateLimitData (spellRateLimitKey);
1059+ assertEq (data.maxAmount, 1_500_000e18 , "spell rate limit maxAmount should be set by cBeam on real contract " );
1060+ assertEq (data.slope, 150_000e18 , "spell rate limit slope should be set by cBeam on real contract " );
1061+ }
1062+
1063+ // 3d. cBeam calls spell-configured controller action
1064+ vm.prank (cBeam);
1065+ configurator.callControllerAction (SPARK_CONTROLLER, spellControllerAction);
1066+ assertEq (ControllerLike (SPARK_CONTROLLER).mintRecipients (7 ), expectedRecipient, "spell mintRecipient should be set on real controller " );
1067+
9761068 // ========================================
9771069 // Phase 4: Verify restrictions
9781070 // ========================================
@@ -991,7 +1083,7 @@ contract IntegrationTest is DssTest {
9911083 // 4c. cBeam cannot call non-whitelisted action
9921084 bytes memory nonWhitelistedAction = abi.encodeWithSelector (
9931085 ControllerLike.setMintRecipient.selector ,
994- uint32 (7 ), // different domain
1086+ uint32 (8 ), // domain not whitelisted by either timelock or spell
9951087 bytes32 (uint256 (uint160 (testRecipient)))
9961088 );
9971089 vm.prank (cBeam);
0 commit comments