@@ -324,7 +324,7 @@ contract Integration_EmissionsController_E2E is Integration_EmissionsController_
324324 /// Invariants for pressButton
325325 /// -----------------------------------------------------------------------
326326
327- /// @dev Assert that all distributions should succeed given valid inputs.
327+ /// @dev Assert that all distributions succeed given valid inputs.
328328 function testFuzz_addDists_pressButton_allDistributionTypes (uint24 r ) public rand (r) {
329329 (uint64 startEpoch , uint16 totalWeight ,) = _genRandParams ();
330330
@@ -353,25 +353,212 @@ contract Integration_EmissionsController_E2E is Integration_EmissionsController_
353353 ic.pressButton (types.length );
354354 }
355355
356- // /// @dev Assert that the function skips disabled distributions.
357- // function testFuzz_addDists_pressButton_skipsDisabledDistributions(uint24 r) public rand(r) {}
356+ /// @dev Assert that the function skips disabled distributions.
357+ function testFuzz_addDists_pressButton_skipsDisabledDistributions (uint24 r ) public rand (r) {
358+ (uint64 startEpoch , uint16 totalWeight , uint numDistributions ) = _genRandParams ();
359+ numDistributions = _randUint ({min: 2 , max: 32 }); // Need at least 2 distributions
360+
361+ // Create distribution types (all enabled initially)
362+ DistributionType[] memory types = new DistributionType [](numDistributions);
363+ uint numToDisable = _randUint ({min: 1 , max: numDistributions - 1 }); // At least 1 disabled, at least 1 enabled
364+
365+ for (uint i = 0 ; i < numDistributions; ++ i) {
366+ types[i] = DistributionType (uint8 (_randUint ({min: 1 , max: 5 }))); // Non-disabled types
367+ }
368+
369+ // 1. Add distributions (all enabled)
370+ (uint [] memory distributionIds , Distribution[] memory distributions ) =
371+ ic.addDistributionsWithTypes (operatorSets, strategies, types, startEpoch, totalWeight);
372+ check_addDists_State (distributionIds, distributions, totalWeight);
373+
374+ // 2. Update some distributions to disabled
375+ for (uint i = 0 ; i < numToDisable; ++ i) {
376+ distributions[i].distributionType = DistributionType.Disabled;
377+ cheats.prank (incentiveCouncil);
378+ emissionsController.updateDistribution (distributionIds[i], distributions[i]);
379+ }
380+
381+ // Note: Disabling distributions doesn't change totalWeight - it just affects processing
382+ check_addDists_State (distributionIds, distributions, totalWeight);
383+
384+ // 3. Warp to start epoch
385+ ic.warpToEpoch (startEpoch);
386+ check_warpToEpoch_State (startEpoch);
387+
388+ // 4. Press button - should only process non-disabled distributions
389+ uint processed = ic.pressButton (numDistributions);
390+ uint expectedProcessed = numDistributions - numToDisable;
391+ assertEq (processed, expectedProcessed, "Should skip disabled distributions " );
392+ }
393+
394+ /// @dev Assert that the function skips distributions that have not started yet.
395+ function testFuzz_addDists_pressButton_skipsNotYetStartedDistributions (uint24 r ) public rand (r) {
396+ (uint64 startEpoch , uint16 totalWeight , uint numDistributions ) = _genRandParams ();
397+ numDistributions = _randUint ({min: 2 , max: 32 }); // Need at least 2 distributions
398+ startEpoch = uint64 (_randUint ({min: 2 , max: 2 ** 12 - 1 })); // Ensure startEpoch > 0
399+
400+ // Generate random distribution types (non-disabled)
401+ DistributionType[] memory types = new DistributionType [](numDistributions);
402+ for (uint i = 0 ; i < numDistributions; ++ i) {
403+ types[i] = DistributionType (uint8 (_randUint ({min: 1 , max: 5 })));
404+ }
405+
406+ // 1. Add distributions with staggered start epochs
407+ (uint [] memory distributionIds , Distribution[] memory distributions ) =
408+ ic.addDistributionsWithTypes (operatorSets, strategies, types, startEpoch, totalWeight);
409+
410+ // Manually update some distributions to have later start epochs
411+ uint numLaterStart = _randUint ({min: 1 , max: numDistributions - 1 });
412+ for (uint i = 0 ; i < numLaterStart; ++ i) {
413+ distributions[i].startEpoch = startEpoch + 1 ; // Start one epoch later
414+ cheats.prank (incentiveCouncil);
415+ emissionsController.updateDistribution (distributionIds[i], distributions[i]);
416+ }
417+ check_addDists_State (distributionIds, distributions, totalWeight);
418+
419+ // 2. Warp to start epoch (some distributions haven't started yet)
420+ ic.warpToEpoch (startEpoch);
421+ check_warpToEpoch_State (startEpoch);
422+
423+ // 3. Press button - should only process distributions that have started
424+ uint processed = ic.pressButton (numDistributions);
425+ uint expectedProcessed = numDistributions - numLaterStart;
426+ assertEq (processed, expectedProcessed, "Should skip distributions that haven't started " );
427+ }
428+
429+ /// @dev Assert that the function skips distributions that have ended.
430+ function testFuzz_addDists_pressButton_skipsEndedDistributions (uint24 r ) public rand (r) {
431+ (uint64 startEpoch , uint16 totalWeight , uint numDistributions ) = _genRandParams ();
432+ numDistributions = _randUint ({min: 2 , max: 32 }); // Need at least 2 distributions
433+ startEpoch = uint64 (_randUint ({min: 1 , max: 100 })); // Keep epochs reasonable
434+
435+ // Generate random distribution types (non-disabled)
436+ DistributionType[] memory types = new DistributionType [](numDistributions);
437+ for (uint i = 0 ; i < numDistributions; ++ i) {
438+ types[i] = DistributionType (uint8 (_randUint ({min: 1 , max: 5 })));
439+ }
440+
441+ // 1. Add distributions (all have totalEpochs = 1 by default, meaning they end after first epoch)
442+ (uint [] memory distributionIds , Distribution[] memory distributions ) =
443+ ic.addDistributionsWithTypes (operatorSets, strategies, types, startEpoch, totalWeight);
444+
445+ // Update some distributions to have totalEpochs = 0 (infinite, so they DON'T end)
446+ // The rest keep totalEpochs = 1 (end after startEpoch)
447+ uint numInfinite = _randUint ({min: 1 , max: numDistributions - 1 });
448+ for (uint i = 0 ; i < numInfinite; ++ i) {
449+ distributions[i].totalEpochs = 0 ; // Infinite - will be active in all epochs
450+ cheats.prank (incentiveCouncil);
451+ emissionsController.updateDistribution (distributionIds[i], distributions[i]);
452+ }
453+ uint numTimeLimited = numDistributions - numInfinite;
454+ check_addDists_State (distributionIds, distributions, totalWeight);
455+
456+ // 2. Warp to the first epoch where time-limited distributions are active
457+ ic.warpToEpoch (startEpoch);
458+ check_warpToEpoch_State (startEpoch);
459+
460+ // 3. Press button - all distributions should be processed
461+ uint processed = ic.pressButton (numDistributions);
462+ assertEq (processed, numDistributions, "All distributions should be processed in their start epoch " );
463+
464+ // 4. Verify button is not pressable (all distributions processed for this epoch)
465+ assertFalse (emissionsController.isButtonPressable (), "Button should not be pressable after all distributions processed " );
466+
467+ // 5. Warp to the next epoch where time-limited distributions have ended
468+ // Distributions with totalEpochs = 1 end when currentEpoch >= startEpoch + totalEpochs
469+ // i.e., currentEpoch >= startEpoch + 1
470+ ic.warpToEpoch (startEpoch + 1 );
471+ check_warpToEpoch_State (startEpoch + 1 );
472+
473+ // 6. Press button - should skip ended distributions
474+ // In the new epoch, only infinite distributions (totalEpochs = 0) should be processed
475+ // Time-limited distributions (totalEpochs = 1) have ended
476+ processed = ic.pressButton (numDistributions);
477+ assertEq (processed, numInfinite, "Should skip ended distributions and only process infinite ones " );
478+ }
479+
480+ /// @dev Assert that the function skips distributions with zero weight.
481+ function testFuzz_addDists_pressButton_skipsZeroWeightDistributions (uint24 r ) public rand (r) {
482+ (uint64 startEpoch , uint16 totalWeight , uint numDistributions ) = _genRandParams ();
483+ numDistributions = _randUint ({min: 2 , max: 32 }); // Need at least 2 distributions
484+
485+ // Generate random distribution types (non-disabled)
486+ DistributionType[] memory types = new DistributionType [](numDistributions);
487+ for (uint i = 0 ; i < numDistributions; ++ i) {
488+ types[i] = DistributionType (uint8 (_randUint ({min: 1 , max: 5 })));
489+ }
490+
491+ // 1. Add distributions
492+ (uint [] memory distributionIds , Distribution[] memory distributions ) =
493+ ic.addDistributionsWithTypes (operatorSets, strategies, types, startEpoch, totalWeight);
494+
495+ // Update some distributions to have zero weight
496+ uint numZeroWeight = _randUint ({min: 1 , max: numDistributions - 1 });
497+ for (uint i = 0 ; i < numZeroWeight; ++ i) {
498+ distributions[i].weight = 0 ;
499+ cheats.prank (incentiveCouncil);
500+ emissionsController.updateDistribution (distributionIds[i], distributions[i]);
501+ }
502+
503+ // Recalculate expected totalWeight after zero-weight updates
504+ uint16 expectedTotalWeight = 0 ;
505+ for (uint i = 0 ; i < distributions.length ; ++ i) {
506+ expectedTotalWeight += uint16 (distributions[i].weight);
507+ }
508+ check_addDists_State (distributionIds, distributions, expectedTotalWeight);
509+
510+ // 2. Warp to start epoch
511+ ic.warpToEpoch (startEpoch);
512+ check_warpToEpoch_State (startEpoch);
513+
514+ // 3. Press button - should skip zero-weight distributions
515+ // Note: Zero-weight distributions don't emit DistributionProcessed events
516+ uint processed = ic.pressButton (numDistributions);
517+ uint expectedProcessed = numDistributions - numZeroWeight;
518+ assertEq (processed, expectedProcessed, "Should skip zero-weight distributions " );
519+ }
358520
359- // /// @dev Assert that the function skips distributions that have not started yet.
360- // function testFuzz_addDists_pressButton_skipsNotYetStartedDistributions(uint24 r) public rand(r) {}
521+ /// @dev Assert that the function processes zero distributions when length is zero.
522+ function testFuzz_addDists_pressButtonLengthZero_NoneProcessed (uint24 r ) public rand (r) {
523+ (uint64 startEpoch , uint16 totalWeight , uint numDistributions ) = _genRandParams ();
361524
362- // /// @dev Assert that the function skips distributions that have ended.
363- // function testFuzz_addDists_pressButton_skipsEndedDistributions(uint24 r) public rand(r) {}
525+ // 1. Add distributions
526+ (uint [] memory distributionIds , Distribution[] memory distributions ) =
527+ ic.addDistributions (operatorSets, strategies, numDistributions, startEpoch, totalWeight);
528+ check_addDists_State (distributionIds, distributions, totalWeight);
529+
530+ // 2. Warp to start epoch
531+ ic.warpToEpoch (startEpoch);
532+ check_warpToEpoch_State (startEpoch);
364533
365- // /// @dev Assert that the function skips distributions with zero weight.
366- // function testFuzz_addDists_pressButton_skipsZeroWeightDistributions(uint24 r) public rand(r) {}
534+ // 3. Press button with length 0 - should process zero distributions
535+ uint processed = ic.pressButton (0 );
536+ assertEq (processed, 0 , "Should process zero distributions when length is zero " );
367537
368- // /// @dev Assert that the function processes zero distributions when length is zero.
369- // function testFuzz_addDists_pressButtonLengthZero_NoneProcessed(uint24 r) public rand(r) {}
538+ // 4. Button should still be pressable since distributions remain
539+ assertTrue (emissionsController.isButtonPressable (), "Button should still be pressable " );
540+ }
370541
371542 /// -----------------------------------------------------------------------
372543 /// Edge cases
373544 /// -----------------------------------------------------------------------
374545
375- // /// @dev Assert that the function processes zero distributions when there are no distributions.
376- // function testFuzz_noDistributions_pressButton_NoneProcessed_sweep_AllEmissionsShouldBeSwept(uint24 r) public rand(r) {}
546+ /// @dev Assert that sweep returns false when there are no distributions and no emissions minted.
547+ function testFuzz_noDistributions_pressButton_NoneProcessed_sweep_AllEmissionsShouldBeSwept (uint24 r ) public rand (r) {
548+ uint64 startEpoch = uint64 (_randUint ({min: 0 , max: 100 }));
549+
550+ // 1. Warp to start epoch (no distributions added)
551+ ic.warpToEpoch (startEpoch);
552+ check_warpToEpoch_State (startEpoch);
553+
554+ // 2. When there are no distributions, pressButton should revert with AllDistributionsProcessed
555+ // because totalProcessable = 0 and totalProcessed = 0 (i.e., all 0 distributions are "processed")
556+ vm.expectRevert (IEmissionsControllerErrors.AllDistributionsProcessed.selector );
557+ ic.pressButton (1 );
558+
559+ // 3. Sweep - since pressButton was never successfully called, no emissions were minted,
560+ // so sweep should return false (nothing to sweep)
561+ bool swept = ic.sweep ();
562+ assertFalse (swept, "Should not sweep when no emissions were minted " );
563+ }
377564}
0 commit comments