@bananapus/core-v6 0.0.31 → 0.0.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -208,8 +208,8 @@ contract JBRulesets is JBControlled, IJBRulesets {
208
208
  caller: msg.sender
209
209
  });
210
210
 
211
- // Return the struct for the new ruleset's ID.
212
- return _getStructFor({projectId: projectId, rulesetId: rulesetId});
211
+ // Return the struct for the new ruleset's ID, with metadata.
212
+ return _getStructFor({projectId: projectId, rulesetId: rulesetId, withMetadata: true});
213
213
  }
214
214
 
215
215
  /// @notice Cache the value of the ruleset weight for a specific ruleset.
@@ -221,7 +221,8 @@ contract JBRulesets is JBControlled, IJBRulesets {
221
221
  /// @param rulesetId The ID of the ruleset to update the cache for.
222
222
  function updateRulesetWeightCache(uint256 projectId, uint256 rulesetId) external override {
223
223
  // Get the target ruleset.
224
- JBRuleset memory targetRuleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
224
+ JBRuleset memory targetRuleset =
225
+ _getStructFor({projectId: projectId, rulesetId: rulesetId, withMetadata: false});
225
226
 
226
227
  // Nothing to cache if the target ruleset doesn't have a duration or a weight cut percent.
227
228
  // slither-disable-next-line incorrect-equality
@@ -296,8 +297,8 @@ contract JBRulesets is JBControlled, IJBRulesets {
296
297
  // Keep a reference to the number of rulesets being returned.
297
298
  uint256 count = 0;
298
299
 
299
- // Keep a reference to the starting ruleset.
300
- JBRuleset memory ruleset = _getStructFor({projectId: projectId, rulesetId: startingId});
300
+ // Keep a reference to the starting ruleset (metadata not needed — only counting).
301
+ JBRuleset memory ruleset = _getStructFor({projectId: projectId, rulesetId: startingId, withMetadata: false});
301
302
 
302
303
  // First, count the number of rulesets to include in the result by iterating backwards from the starting
303
304
  // ruleset.
@@ -306,7 +307,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
306
307
  count++;
307
308
 
308
309
  // Iterate to the ruleset it was based on.
309
- ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
310
+ ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId, withMetadata: false});
310
311
  }
311
312
 
312
313
  // Keep a reference to the array of rulesets that'll be populated.
@@ -317,8 +318,8 @@ contract JBRulesets is JBControlled, IJBRulesets {
317
318
  return rulesets;
318
319
  }
319
320
 
320
- // Reset the ruleset being iterated on to the starting ruleset.
321
- ruleset = _getStructFor({projectId: projectId, rulesetId: startingId});
321
+ // Reset the ruleset being iterated on to the starting ruleset, now with metadata for the return array.
322
+ ruleset = _getStructFor({projectId: projectId, rulesetId: startingId, withMetadata: true});
322
323
 
323
324
  // Set the counter.
324
325
  uint256 i;
@@ -328,8 +329,10 @@ contract JBRulesets is JBControlled, IJBRulesets {
328
329
  // Add the ruleset to the array.
329
330
  rulesets[i++] = ruleset;
330
331
 
331
- // Get the ruleset it was based on if needed.
332
- if (i != count) ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
332
+ // Get the ruleset it was based on if needed, with metadata for the return array.
333
+ if (i != count) {
334
+ ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId, withMetadata: true});
335
+ }
333
336
  }
334
337
  }
335
338
 
@@ -345,8 +348,8 @@ contract JBRulesets is JBControlled, IJBRulesets {
345
348
  // Get a reference to the latest ruleset ID.
346
349
  uint256 rulesetId = latestRulesetIdOf[projectId];
347
350
 
348
- // Resolve the struct for the latest ruleset.
349
- JBRuleset memory ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
351
+ // Resolve the struct for the latest ruleset, with metadata (forwarded to the external approval hook).
352
+ JBRuleset memory ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId, withMetadata: true});
350
353
 
351
354
  return _approvalStatusOf({projectId: projectId, ruleset: ruleset});
352
355
  }
@@ -362,15 +365,18 @@ contract JBRulesets is JBControlled, IJBRulesets {
362
365
  function currentOf(uint256 projectId) external view override returns (JBRuleset memory ruleset) {
363
366
  // If the project does not have a ruleset, return an empty struct.
364
367
  // slither-disable-next-line incorrect-equality
365
- if (latestRulesetIdOf[projectId] == 0) return _getStructFor({projectId: 0, rulesetId: 0});
368
+ if (latestRulesetIdOf[projectId] == 0) {
369
+ return _getStructFor({projectId: 0, rulesetId: 0, withMetadata: false});
370
+ }
366
371
 
367
372
  // Get a reference to the currently approvable ruleset's ID.
368
373
  uint256 rulesetId = _currentlyApprovableRulesetIdOf(projectId);
369
374
 
370
375
  // If a currently approvable ruleset exists...
371
376
  if (rulesetId != 0) {
372
- // Resolve the struct for the currently approvable ruleset.
373
- ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
377
+ // Resolve the struct for the currently approvable ruleset, with metadata (forwarded to external hooks
378
+ // and potentially returned).
379
+ ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId, withMetadata: true});
374
380
 
375
381
  // Get a reference to the approval status.
376
382
  JBApprovalStatus approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
@@ -387,15 +393,16 @@ contract JBRulesets is JBControlled, IJBRulesets {
387
393
  // which carries the last approved configuration.
388
394
  rulesetId = ruleset.basedOnId;
389
395
 
390
- // Keep a reference to its ruleset.
391
- ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
396
+ // Keep a reference to its ruleset, with metadata (used by `_simulateCycledRulesetBasedOn` and may be
397
+ // returned).
398
+ ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId, withMetadata: true});
392
399
  } else {
393
400
  // No upcoming ruleset found that is currently approvable,
394
401
  // so use the latest ruleset ID.
395
402
  rulesetId = latestRulesetIdOf[projectId];
396
403
 
397
- // Get the struct for the latest ID.
398
- ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
404
+ // Get the struct for the latest ID, with metadata (forwarded to external hooks and may be returned).
405
+ ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId, withMetadata: true});
399
406
 
400
407
  // Get a reference to the approval status.
401
408
  JBApprovalStatus approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
@@ -407,7 +414,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
407
414
  || block.timestamp < ruleset.start
408
415
  ) {
409
416
  rulesetId = ruleset.basedOnId;
410
- ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
417
+ ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId, withMetadata: true});
411
418
  approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
412
419
  }
413
420
  }
@@ -433,7 +440,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
433
440
  override
434
441
  returns (JBRuleset memory ruleset)
435
442
  {
436
- return _getStructFor({projectId: projectId, rulesetId: rulesetId});
443
+ ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId, withMetadata: true});
437
444
  }
438
445
 
439
446
  /// @notice The latest ruleset queued for a project. Returns the ruleset's struct and its current approval status.
@@ -451,8 +458,9 @@ contract JBRulesets is JBControlled, IJBRulesets {
451
458
  // Get a reference to the latest ruleset's ID.
452
459
  uint256 rulesetId = latestRulesetIdOf[projectId];
453
460
 
454
- // Resolve the struct for the latest ruleset.
455
- ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
461
+ // Resolve the struct for the latest ruleset, with metadata (forwarded to the external approval hook and
462
+ // returned).
463
+ ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId, withMetadata: true});
456
464
 
457
465
  // Resolve the approval status.
458
466
  approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
@@ -465,7 +473,9 @@ contract JBRulesets is JBControlled, IJBRulesets {
465
473
  function upcomingOf(uint256 projectId) external view override returns (JBRuleset memory ruleset) {
466
474
  // If the project does not have a latest ruleset, return an empty struct.
467
475
  // slither-disable-next-line incorrect-equality
468
- if (latestRulesetIdOf[projectId] == 0) return _getStructFor({projectId: 0, rulesetId: 0});
476
+ if (latestRulesetIdOf[projectId] == 0) {
477
+ return _getStructFor({projectId: 0, rulesetId: 0, withMetadata: false});
478
+ }
469
479
 
470
480
  // Get a reference to the upcoming approvable ruleset's ID.
471
481
  uint256 upcomingApprovableRulesetId = _upcomingApprovableRulesetIdOf(projectId);
@@ -476,7 +486,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
476
486
  // If an upcoming approvable ruleset has been queued, and it's approval status is Approved or ApprovalExpected,
477
487
  // return its ruleset struct
478
488
  if (upcomingApprovableRulesetId != 0) {
479
- ruleset = _getStructFor({projectId: projectId, rulesetId: upcomingApprovableRulesetId});
489
+ ruleset = _getStructFor({projectId: projectId, rulesetId: upcomingApprovableRulesetId, withMetadata: true});
480
490
 
481
491
  // Get a reference to the approval status.
482
492
  approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
@@ -488,23 +498,25 @@ contract JBRulesets is JBControlled, IJBRulesets {
488
498
  || approvalStatus == JBApprovalStatus.Empty
489
499
  ) return ruleset;
490
500
 
491
- // Resolve the ruleset for the ruleset the upcoming approvable ruleset was based on.
492
- ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
501
+ // Resolve the ruleset for the ruleset the upcoming approvable ruleset was based on, with metadata
502
+ // (used by `_simulateCycledRulesetBasedOn`).
503
+ ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId, withMetadata: true});
493
504
  } else {
494
- // Resolve the ruleset for the latest queued ruleset.
495
- ruleset = _getStructFor({projectId: projectId, rulesetId: latestRulesetIdOf[projectId]});
505
+ // Resolve the ruleset for the latest queued ruleset, with metadata (forwarded to external hooks and used
506
+ // by `_simulateCycledRulesetBasedOn`).
507
+ ruleset = _getStructFor({projectId: projectId, rulesetId: latestRulesetIdOf[projectId], withMetadata: true});
496
508
 
497
509
  // If the latest ruleset starts in the future, it must start in the distant future
498
510
  // Since its not the upcoming approvable ruleset. In this case, base the upcoming ruleset on the base
499
511
  // ruleset.
500
512
  while (ruleset.start > block.timestamp) {
501
- ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
513
+ ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId, withMetadata: true});
502
514
  }
503
515
  }
504
516
 
505
517
  // There's no queued if the current has a duration of 0.
506
518
  // slither-disable-next-line incorrect-equality
507
- if (ruleset.duration == 0) return _getStructFor({projectId: 0, rulesetId: 0});
519
+ if (ruleset.duration == 0) return _getStructFor({projectId: 0, rulesetId: 0, withMetadata: false});
508
520
 
509
521
  // Get a reference to the approval status.
510
522
  approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
@@ -516,12 +528,13 @@ contract JBRulesets is JBControlled, IJBRulesets {
516
528
  return _simulateCycledRulesetBasedOn({projectId: projectId, baseRuleset: ruleset, allowMidRuleset: false});
517
529
  }
518
530
 
519
- // Get the ruleset of its base ruleset, which carries the last approved configuration.
520
- ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
531
+ // Get the ruleset of its base ruleset, which carries the last approved configuration. Metadata is needed by
532
+ // `_simulateCycledRulesetBasedOn`.
533
+ ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId, withMetadata: true});
521
534
 
522
535
  // There's no queued if the base, which must still be the current, has a duration of 0.
523
536
  // slither-disable-next-line incorrect-equality
524
- if (ruleset.duration == 0) return _getStructFor({projectId: 0, rulesetId: 0});
537
+ if (ruleset.duration == 0) return _getStructFor({projectId: 0, rulesetId: 0, withMetadata: false});
525
538
 
526
539
  // Return a simulated cycled ruleset.
527
540
  return _simulateCycledRulesetBasedOn({projectId: projectId, baseRuleset: ruleset, allowMidRuleset: false});
@@ -672,17 +685,19 @@ contract JBRulesets is JBControlled, IJBRulesets {
672
685
  revert JBRulesets_WeightCacheRequired(projectId);
673
686
  }
674
687
 
675
- for (uint256 i; i < weightCutMultiple; i++) {
676
- // The number of times to apply the weight cut percent.
688
+ // Cache the cut factor and max percent to avoid recomputing each iteration.
689
+ uint256 cutFactor = JBConstants.MAX_WEIGHT_CUT_PERCENT - baseRulesetWeightCutPercent;
690
+ uint256 maxPercent = JBConstants.MAX_WEIGHT_CUT_PERCENT;
691
+
692
+ for (uint256 i; i < weightCutMultiple;) {
677
693
  // Base the new weight on the specified ruleset's weight.
678
- weight = mulDiv(
679
- weight,
680
- JBConstants.MAX_WEIGHT_CUT_PERCENT - baseRulesetWeightCutPercent,
681
- JBConstants.MAX_WEIGHT_CUT_PERCENT
682
- );
694
+ weight = mulDiv(weight, cutFactor, maxPercent);
683
695
 
684
696
  // The calculation doesn't need to continue if the weight is 0.
685
697
  if (weight == 0) break;
698
+ unchecked {
699
+ ++i;
700
+ }
686
701
  }
687
702
  }
688
703
 
@@ -713,15 +728,16 @@ contract JBRulesets is JBControlled, IJBRulesets {
713
728
  // Use an empty ruleset as the base.
714
729
  return _initializeRulesetFor({
715
730
  projectId: projectId,
716
- baseRuleset: _getStructFor({projectId: 0, rulesetId: 0}),
731
+ baseRuleset: _getStructFor({projectId: 0, rulesetId: 0, withMetadata: false}),
717
732
  rulesetId: rulesetId,
718
733
  mustStartAtOrAfter: mustStartAtOrAfter,
719
734
  weight: weight
720
735
  });
721
736
  }
722
737
 
723
- // Get a reference to the latest ruleset's struct.
724
- JBRuleset memory baseRuleset = _getStructFor({projectId: projectId, rulesetId: latestId});
738
+ // Get a reference to the latest ruleset's struct, with metadata because `_approvalStatusOf` forwards the
739
+ // struct to external approval hooks.
740
+ JBRuleset memory baseRuleset = _getStructFor({projectId: projectId, rulesetId: latestId, withMetadata: true});
725
741
 
726
742
  // Get a reference to the approval status.
727
743
  JBApprovalStatus approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: baseRuleset});
@@ -743,7 +759,9 @@ contract JBRulesets is JBControlled, IJBRulesets {
743
759
  && approvalStatus != JBApprovalStatus.ApprovalExpected
744
760
  && approvalStatus != JBApprovalStatus.Empty)
745
761
  ) {
746
- baseRuleset = _getStructFor({projectId: projectId, rulesetId: baseRuleset.basedOnId});
762
+ // Metadata not needed — the fallback ruleset is only used for intrinsic fields (start, basedOnId, etc.)
763
+ // and not forwarded to any external approval hook.
764
+ baseRuleset = _getStructFor({projectId: projectId, rulesetId: baseRuleset.basedOnId, withMetadata: false});
747
765
  }
748
766
 
749
767
  // Make sure the ruleset starts after the base ruleset.
@@ -896,11 +914,14 @@ contract JBRulesets is JBControlled, IJBRulesets {
896
914
  // slither-disable-next-line incorrect-equality
897
915
  if (ruleset.basedOnId == 0) return JBApprovalStatus.Empty;
898
916
 
899
- // Get the struct of the ruleset with the approval hook.
900
- JBRuleset memory approvalHookRuleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
917
+ // Read only the packed user properties to extract the approval hook address,
918
+ // avoiding the cost of loading the full parent ruleset struct.
919
+ uint256 packedUserProperties = _packedUserPropertiesOf[projectId][ruleset.basedOnId];
920
+ // forge-lint: disable-next-line(unsafe-typecast)
921
+ IJBRulesetApprovalHook approvalHook = IJBRulesetApprovalHook(address(uint160(packedUserProperties)));
901
922
 
902
923
  // If there is no approval hook, it's considered empty.
903
- if (approvalHookRuleset.approvalHook == IJBRulesetApprovalHook(address(0))) {
924
+ if (approvalHook == IJBRulesetApprovalHook(address(0))) {
904
925
  return JBApprovalStatus.Empty;
905
926
  }
906
927
 
@@ -909,9 +930,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
909
930
  // Note: A malicious hook that consumes all gas (e.g. infinite loop) could still DoS via gas exhaustion.
910
931
  // This is accepted risk since the project owner chose their own approval hook.
911
932
  // slither-disable-next-line calls-loop
912
- try approvalHookRuleset.approvalHook.approvalStatusOf({projectId: projectId, ruleset: ruleset}) returns (
913
- JBApprovalStatus status
914
- ) {
933
+ try approvalHook.approvalStatusOf({projectId: projectId, ruleset: ruleset}) returns (JBApprovalStatus status) {
915
934
  return status;
916
935
  } catch {
917
936
  return JBApprovalStatus.Failed;
@@ -928,8 +947,8 @@ contract JBRulesets is JBControlled, IJBRulesets {
928
947
  // Get a reference to the project's latest ruleset.
929
948
  uint256 rulesetId = latestRulesetIdOf[projectId];
930
949
 
931
- // Get the struct for the latest ruleset.
932
- JBRuleset memory ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
950
+ // Get the struct for the latest ruleset (metadata not needed — only traversal fields are checked).
951
+ JBRuleset memory ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId, withMetadata: false});
933
952
 
934
953
  // Loop through all most recently queued rulesets until an approvable one is found, or we've proven one can't
935
954
  // exist.
@@ -945,17 +964,28 @@ contract JBRulesets is JBControlled, IJBRulesets {
945
964
  return ruleset.id;
946
965
  }
947
966
 
948
- ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
967
+ ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId, withMetadata: false});
949
968
  } while (ruleset.cycleNumber != 0);
950
969
 
951
970
  return 0;
952
971
  }
953
972
 
954
- /// @notice Unpack a ruleset's packed stored values into an easy-to-work-with ruleset struct.
973
+ /// @notice Unpack a ruleset's packed stored values into a struct.
974
+ /// @dev When `withMetadata` is false, one cold SLOAD (~2,100 gas) is skipped — use this for linked-list traversal
975
+ /// where only id, start, duration, basedOnId, etc. are needed.
955
976
  /// @param projectId The ID of the project the ruleset belongs to.
956
- /// @param rulesetId The ID of the ruleset to get the full struct for.
957
- /// @return ruleset A ruleset struct.
958
- function _getStructFor(uint256 projectId, uint256 rulesetId) internal view returns (JBRuleset memory ruleset) {
977
+ /// @param rulesetId The ID of the ruleset to get the struct for.
978
+ /// @param withMetadata Whether to load the packed metadata from storage.
979
+ /// @return ruleset The ruleset struct (`metadata` is 0 when `withMetadata` is false).
980
+ function _getStructFor(
981
+ uint256 projectId,
982
+ uint256 rulesetId,
983
+ bool withMetadata
984
+ )
985
+ internal
986
+ view
987
+ returns (JBRuleset memory ruleset)
988
+ {
959
989
  // Return an empty ruleset if the specified `rulesetId` is 0.
960
990
  // slither-disable-next-line incorrect-equality
961
991
  if (rulesetId == 0) return ruleset;
@@ -990,7 +1020,8 @@ contract JBRulesets is JBControlled, IJBRulesets {
990
1020
  // forge-lint: disable-next-line(unsafe-typecast)
991
1021
  ruleset.weightCutPercent = uint32(packedUserProperties >> 192);
992
1022
 
993
- ruleset.metadata = _metadataOf[projectId][rulesetId];
1023
+ // Load metadata only when needed — saves one cold SLOAD (~2,100 gas) for traversal-only paths.
1024
+ if (withMetadata) ruleset.metadata = _metadataOf[projectId][rulesetId];
994
1025
  }
995
1026
 
996
1027
  /// @notice A simulated view of the ruleset that would be created if the provided one cycled over (if the project
@@ -1067,8 +1098,8 @@ contract JBRulesets is JBControlled, IJBRulesets {
1067
1098
  // Get a reference to the ID of the project's latest ruleset.
1068
1099
  rulesetId = latestRulesetIdOf[projectId];
1069
1100
 
1070
- // Get the struct for the latest ruleset.
1071
- JBRuleset memory ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
1101
+ // Get the struct for the latest ruleset (metadata not needed — only traversal fields are checked).
1102
+ JBRuleset memory ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId, withMetadata: false});
1072
1103
 
1073
1104
  // There is no upcoming ruleset if the latest ruleset has already started.
1074
1105
  // slither-disable-next-line incorrect-equality
@@ -1086,7 +1117,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
1086
1117
 
1087
1118
  // Find the base ruleset that is not still queued.
1088
1119
  while (true) {
1089
- baseRuleset = _getStructFor({projectId: projectId, rulesetId: basedOnId});
1120
+ baseRuleset = _getStructFor({projectId: projectId, rulesetId: basedOnId, withMetadata: false});
1090
1121
 
1091
1122
  // If the base ruleset starts in the future,
1092
1123
  if (block.timestamp < baseRuleset.start) {
@@ -1100,8 +1131,8 @@ contract JBRulesets is JBControlled, IJBRulesets {
1100
1131
  }
1101
1132
  }
1102
1133
 
1103
- // Get the ruleset struct for the ID found.
1104
- ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
1134
+ // Get the ruleset struct for the ID found (metadata not needed — only `start` and `duration` are checked).
1135
+ ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId, withMetadata: false});
1105
1136
 
1106
1137
  // If the latest ruleset doesn't start until after another base ruleset return 0.
1107
1138
  if (baseRuleset.duration != 0 && block.timestamp < ruleset.start - baseRuleset.duration) {
package/src/JBSplits.sol CHANGED
@@ -97,15 +97,13 @@ contract JBSplits is JBControlled, IJBSplits {
97
97
  // Cache whether the controller check has already passed to avoid repeated external calls.
98
98
  bool controllerChecked;
99
99
 
100
- // Set each grouped splits.
101
- for (uint256 i; i < splitGroups.length; i++) {
102
- // Get a reference to the grouped split being iterated on.
103
- JBSplitGroup memory splitGroup = splitGroups[i];
104
-
100
+ // Set each grouped splits. Access calldata directly to avoid copying each split group to memory.
101
+ for (uint256 i; i < splitGroups.length;) {
105
102
  // Self-auth: lower 160 bits must match msg.sender AND upper 96 bits must be non-zero.
106
103
  // GroupIds with zero upper bits are reserved for protocol use (e.g. terminal payout groups)
107
104
  // and always require controller authorization to prevent token contracts from hijacking payouts.
108
- bool isSelfManaged = splitGroup.groupId >> 160 != 0 && address(uint160(splitGroup.groupId)) == msg.sender;
105
+ bool isSelfManaged =
106
+ splitGroups[i].groupId >> 160 != 0 && address(uint160(splitGroups[i].groupId)) == msg.sender;
109
107
 
110
108
  if (!isSelfManaged && !controllerChecked) {
111
109
  _onlyControllerOf(projectId);
@@ -114,8 +112,14 @@ contract JBSplits is JBControlled, IJBSplits {
114
112
 
115
113
  // Set the splits for the group.
116
114
  _setSplitsOf({
117
- projectId: projectId, rulesetId: rulesetId, groupId: splitGroup.groupId, splits: splitGroup.splits
115
+ projectId: projectId,
116
+ rulesetId: rulesetId,
117
+ groupId: splitGroups[i].groupId,
118
+ splits: splitGroups[i].splits
118
119
  });
120
+ unchecked {
121
+ ++i;
122
+ }
119
123
  }
120
124
  }
121
125
 
@@ -168,7 +172,7 @@ contract JBSplits is JBControlled, IJBSplits {
168
172
  uint256 numberOfCurrentSplits = currentSplits.length;
169
173
 
170
174
  // Check to see if all locked splits are included in the array of splits which is being set.
171
- for (uint256 i; i < numberOfCurrentSplits; i++) {
175
+ for (uint256 i; i < numberOfCurrentSplits;) {
172
176
  // If not locked, continue.
173
177
  if (
174
178
  block.timestamp < currentSplits[i].lockedUntil
@@ -176,6 +180,9 @@ contract JBSplits is JBControlled, IJBSplits {
176
180
  ) {
177
181
  revert JBSplits_PreviousLockedSplitsNotIncluded(projectId, rulesetId);
178
182
  }
183
+ unchecked {
184
+ ++i;
185
+ }
179
186
  }
180
187
 
181
188
  // Add up all the `percent`s to make sure their total is under 100%.
@@ -184,7 +191,7 @@ contract JBSplits is JBControlled, IJBSplits {
184
191
  // Keep a reference to the number of splits to set.
185
192
  uint256 numberOfSplits = splits.length;
186
193
 
187
- for (uint256 i; i < numberOfSplits; i++) {
194
+ for (uint256 i; i < numberOfSplits;) {
188
195
  // Set the split being iterated on.
189
196
  JBSplit memory split = splits[i];
190
197
 
@@ -228,6 +235,9 @@ contract JBSplits is JBControlled, IJBSplits {
228
235
  emit SetSplit({
229
236
  projectId: projectId, rulesetId: rulesetId, groupId: groupId, split: split, caller: msg.sender
230
237
  });
238
+ unchecked {
239
+ ++i;
240
+ }
231
241
  }
232
242
 
233
243
  // Store the number of splits for the project, ruleset, and group.
@@ -235,9 +245,12 @@ contract JBSplits is JBControlled, IJBSplits {
235
245
 
236
246
  // Clean up stale storage slots if the new split count is less than the previous count.
237
247
  // This zeroes out leftover packed data to reclaim gas via storage refunds.
238
- for (uint256 i = numberOfSplits; i < numberOfCurrentSplits; i++) {
248
+ for (uint256 i = numberOfSplits; i < numberOfCurrentSplits;) {
239
249
  delete _packedSplitParts1Of[projectId][rulesetId][groupId][i];
240
250
  delete _packedSplitParts2Of[projectId][rulesetId][groupId][i];
251
+ unchecked {
252
+ ++i;
253
+ }
241
254
  }
242
255
  }
243
256
 
@@ -267,7 +280,7 @@ contract JBSplits is JBControlled, IJBSplits {
267
280
  JBSplit[] memory splits = new JBSplit[](splitCount);
268
281
 
269
282
  // Loop through each split and unpack the values into structs.
270
- for (uint256 i; i < splitCount; i++) {
283
+ for (uint256 i; i < splitCount;) {
271
284
  // Get a reference to the first part of the split's packed data.
272
285
  uint256 packedSplitPart1 = _packedSplitParts1Of[projectId][rulesetId][groupId][i];
273
286
 
@@ -301,6 +314,9 @@ contract JBSplits is JBControlled, IJBSplits {
301
314
 
302
315
  // Add the split to the value being returned.
303
316
  splits[i] = split;
317
+ unchecked {
318
+ ++i;
319
+ }
304
320
  }
305
321
 
306
322
  return splits;
@@ -314,7 +330,7 @@ contract JBSplits is JBControlled, IJBSplits {
314
330
  // Keep a reference to the number of splits.
315
331
  uint256 numberOfSplits = splits.length;
316
332
 
317
- for (uint256 i; i < numberOfSplits; i++) {
333
+ for (uint256 i; i < numberOfSplits;) {
318
334
  // Set the split being iterated on.
319
335
  JBSplit memory split = splits[i];
320
336
 
@@ -326,6 +342,9 @@ contract JBSplits is JBControlled, IJBSplits {
326
342
  && split.preferAddToBalance == lockedSplit.preferAddToBalance
327
343
  && split.lockedUntil >= lockedSplit.lockedUntil
328
344
  ) return true;
345
+ unchecked {
346
+ ++i;
347
+ }
329
348
  }
330
349
 
331
350
  return false;