@bananapus/core-v6 0.0.30 → 0.0.32

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.
Files changed (49) hide show
  1. package/ADMINISTRATION.md +43 -13
  2. package/ARCHITECTURE.md +62 -137
  3. package/AUDIT_INSTRUCTIONS.md +149 -428
  4. package/CHANGELOG.md +73 -0
  5. package/README.md +90 -201
  6. package/RISKS.md +27 -12
  7. package/SKILLS.md +31 -441
  8. package/STYLE_GUIDE.md +52 -19
  9. package/USER_JOURNEYS.md +76 -627
  10. package/package.json +1 -2
  11. package/references/entrypoints.md +160 -0
  12. package/references/types-errors-events.md +297 -0
  13. package/script/Deploy.s.sol +7 -2
  14. package/script/DeployPeriphery.s.sol +51 -4
  15. package/src/JBController.sol +45 -17
  16. package/src/JBDirectory.sol +26 -13
  17. package/src/JBFundAccessLimits.sol +28 -7
  18. package/src/JBMultiTerminal.sol +180 -86
  19. package/src/JBPermissions.sol +17 -17
  20. package/src/JBRulesets.sol +82 -23
  21. package/src/JBSplits.sol +31 -12
  22. package/src/JBTerminalStore.sol +137 -53
  23. package/src/JBTokens.sol +5 -2
  24. package/src/abstract/JBControlled.sol +10 -3
  25. package/src/abstract/JBPermissioned.sol +1 -1
  26. package/src/interfaces/IJBRulesetDataHook.sol +5 -4
  27. package/src/libraries/JBCashOuts.sol +1 -1
  28. package/src/libraries/JBConstants.sol +1 -1
  29. package/src/libraries/JBCurrencyIds.sol +1 -1
  30. package/src/libraries/JBFees.sol +1 -1
  31. package/src/libraries/JBFixedPointNumber.sol +1 -1
  32. package/src/libraries/JBMetadataResolver.sol +5 -2
  33. package/src/libraries/JBPayoutSplitGroupLib.sol +7 -2
  34. package/src/libraries/JBRulesetMetadataResolver.sol +1 -1
  35. package/src/libraries/JBSplitGroupIds.sol +1 -1
  36. package/src/libraries/JBSurplus.sol +5 -2
  37. package/src/structs/JBSplit.sol +4 -1
  38. package/test/TestForwardedTokenConsumption.sol +419 -0
  39. package/test/audit/CrossTerminalSurplusSpoof.t.sol +140 -0
  40. package/test/audit/CycledSurplusAllowanceReset.t.sol +184 -0
  41. package/test/units/static/JBController/TestPreviewMintOf.sol +5 -4
  42. package/test/units/static/JBMultiTerminal/TestCashOutTokensOf.sol +15 -12
  43. package/test/units/static/JBMultiTerminal/TestExecutePayout.sol +6 -0
  44. package/test/units/static/JBMultiTerminal/TestExecuteProcessFee.sol +3 -0
  45. package/test/units/static/JBMultiTerminal/TestMigrateBalanceOf.sol +3 -0
  46. package/test/units/static/JBMultiTerminal/TestPay.sol +7 -15
  47. package/test/units/static/JBMultiTerminal/TestSendPayoutsOf.sol +1 -1
  48. package/test/units/static/JBMultiTerminal/TestUseAllowanceOf.sol +1 -1
  49. package/CHANGE_LOG.md +0 -479
@@ -158,6 +158,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
158
158
  RULESETS = rulesets;
159
159
  SPLITS = splits;
160
160
  TOKENS = tokens;
161
+ // slither-disable-next-line missing-zero-check
161
162
  OMNICHAIN_RULESET_OPERATOR = omnichainRulesetOperator;
162
163
  }
163
164
 
@@ -521,23 +522,28 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
521
522
  // Get a reference to the project's ruleset.
522
523
  JBRuleset memory ruleset = _currentRulesetOf(projectId);
523
524
 
525
+ // Cache common values used in both permission checks.
526
+ address sender = _msgSender();
527
+ bool senderIsTerminal = _isTerminalOf(projectId, sender);
528
+
524
529
  // Minting is restricted to: the project's owner, addresses with permission to `MINT_TOKENS`, the project's
525
530
  // terminals, and the project's data hook.
526
531
  _requirePermissionAllowingOverrideFrom({
527
532
  account: PROJECTS.ownerOf(projectId),
528
533
  projectId: projectId,
529
534
  permissionId: JBPermissionIds.MINT_TOKENS,
530
- alsoGrantAccessIf: _isTerminalOf(projectId, _msgSender()) || _msgSender() == ruleset.dataHook()
531
- || _hasDataHookMintPermissionFor(projectId, ruleset, _msgSender())
535
+ alsoGrantAccessIf: senderIsTerminal || sender == ruleset.dataHook()
536
+ || _hasDataHookMintPermissionFor(projectId, ruleset, sender)
532
537
  });
533
538
 
534
539
  // If the message sender is not the project's terminal or data hook, the ruleset must have `allowOwnerMinting`
535
540
  // set to `true`.
536
541
  if (
537
- ruleset.id != 0 && !ruleset.allowOwnerMinting() && !_isTerminalOf(projectId, _msgSender())
538
- && _msgSender() != address(ruleset.dataHook())
539
- && !_hasDataHookMintPermissionFor(projectId, ruleset, _msgSender())
540
- ) revert JBController_MintNotAllowedAndNotTerminalOrHook(_msgSender());
542
+ ruleset.id != 0 && !ruleset.allowOwnerMinting() && !senderIsTerminal && sender != ruleset.dataHook()
543
+ && !_hasDataHookMintPermissionFor(projectId, ruleset, sender)
544
+ ) {
545
+ revert JBController_MintNotAllowedAndNotTerminalOrHook(sender);
546
+ }
541
547
 
542
548
  // Determine the reserved percent to use.
543
549
  reservedPercent = useReservedPercent ? ruleset.reservedPercent() : 0;
@@ -558,7 +564,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
558
564
  beneficiaryTokenCount: beneficiaryTokenCount,
559
565
  memo: memo,
560
566
  reservedPercent: reservedPercent,
561
- caller: _msgSender()
567
+ caller: sender
562
568
  });
563
569
 
564
570
  // Add any reserved tokens to the pending reserved token balance.
@@ -747,12 +753,15 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
747
753
  rulesets = new JBRulesetWithMetadata[](numberOfRulesets);
748
754
 
749
755
  // Populate the array with rulesets AND their metadata.
750
- for (uint256 i; i < numberOfRulesets; i++) {
756
+ for (uint256 i; i < numberOfRulesets;) {
751
757
  // Set the ruleset being iterated on.
752
758
  JBRuleset memory baseRuleset = baseRulesets[i];
753
759
 
754
760
  // Set the returned value.
755
761
  rulesets[i] = JBRulesetWithMetadata({ruleset: baseRuleset, metadata: baseRuleset.expandMetadata()});
762
+ unchecked {
763
+ ++i;
764
+ }
756
765
  }
757
766
  }
758
767
 
@@ -820,8 +829,8 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
820
829
  override
821
830
  returns (uint256 beneficiaryTokenCount, uint256 reservedTokenCount)
822
831
  {
823
- // Revert if there are no tokens to split.
824
- if (tokenCount == 0) revert JBController_ZeroTokensToMint();
832
+ // A zero preview amount means there are no tokens to split.
833
+ if (tokenCount == 0) return (0, 0);
825
834
 
826
835
  // Keep a reference to the current ruleset.
827
836
  JBRuleset memory ruleset = _currentRulesetOf(projectId);
@@ -895,11 +904,12 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
895
904
  // Initialize an array of terminals to populate.
896
905
  IJBTerminal[] memory terminals = new IJBTerminal[](terminalConfigurations.length);
897
906
 
898
- for (uint256 i; i < terminalConfigurations.length; i++) {
907
+ for (uint256 i; i < terminalConfigurations.length;) {
899
908
  // Set the terminal configuration being iterated on.
900
909
  JBTerminalConfig memory terminalConfig = terminalConfigurations[i];
901
910
 
902
911
  // Add the accounting contexts for the specified tokens.
912
+ // slither-disable-next-line calls-loop
903
913
  terminalConfig.terminal
904
914
  .addAccountingContextsFor({
905
915
  projectId: projectId, accountingContexts: terminalConfig.accountingContextsToAccept
@@ -907,6 +917,9 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
907
917
 
908
918
  // Add the terminal.
909
919
  terminals[i] = terminalConfig.terminal;
920
+ unchecked {
921
+ ++i;
922
+ }
910
923
  }
911
924
 
912
925
  // Set the terminals in the directory.
@@ -926,7 +939,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
926
939
  internal
927
940
  returns (uint256 rulesetId)
928
941
  {
929
- for (uint256 i; i < rulesetConfigurations.length; i++) {
942
+ for (uint256 i; i < rulesetConfigurations.length;) {
930
943
  // Get a reference to the ruleset config being iterated on.
931
944
  JBRulesetConfig memory rulesetConfig = rulesetConfigurations[i];
932
945
 
@@ -945,6 +958,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
945
958
  }
946
959
 
947
960
  // Queue its ruleset.
961
+ // slither-disable-next-line calls-loop
948
962
  JBRuleset memory ruleset = RULESETS.queueFor({
949
963
  projectId: projectId,
950
964
  duration: rulesetConfig.duration,
@@ -956,11 +970,13 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
956
970
  });
957
971
 
958
972
  // Set its split groups.
973
+ // slither-disable-next-line calls-loop
959
974
  SPLITS.setSplitGroupsOf({
960
975
  projectId: projectId, rulesetId: ruleset.id, splitGroups: rulesetConfig.splitGroups
961
976
  });
962
977
 
963
978
  // Set its fund access limits.
979
+ // slither-disable-next-line calls-loop
964
980
  FUND_ACCESS_LIMITS.setFundAccessLimitsFor({
965
981
  projectId: projectId, rulesetId: ruleset.id, fundAccessLimitGroups: rulesetConfig.fundAccessLimitGroups
966
982
  });
@@ -969,6 +985,9 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
969
985
  if (i == rulesetConfigurations.length - 1) {
970
986
  rulesetId = ruleset.id;
971
987
  }
988
+ unchecked {
989
+ ++i;
990
+ }
972
991
  }
973
992
  }
974
993
 
@@ -999,8 +1018,11 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
999
1018
  // Keep a reference to the number of splits being iterated on.
1000
1019
  uint256 numberOfSplits = splits.length;
1001
1020
 
1021
+ // Cache _msgSender() before the loop.
1022
+ address messageSender = _msgSender();
1023
+
1002
1024
  // Send the tokens to the splits.
1003
- for (uint256 i; i < numberOfSplits; i++) {
1025
+ for (uint256 i; i < numberOfSplits;) {
1004
1026
  // Get a reference to the split being iterated on.
1005
1027
  JBSplit memory split = splits[i];
1006
1028
 
@@ -1023,7 +1045,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
1023
1045
  projectId: projectId, tokenCount: splitTokenCount, recipient: address(split.hook), token: token
1024
1046
  });
1025
1047
 
1026
- // slither-disable-next-line reentrancy-events
1048
+ // slither-disable-next-line calls-loop,reentrancy-events
1027
1049
  try split.hook
1028
1050
  .processSplitWith(
1029
1051
  JBSplitHookContext({
@@ -1043,10 +1065,11 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
1043
1065
  } else {
1044
1066
  // Pay the project using the split's beneficiary if one was provided. Otherwise, use the message
1045
1067
  // sender.
1046
- address beneficiary = split.beneficiary != address(0) ? split.beneficiary : _msgSender();
1068
+ address beneficiary = split.beneficiary != address(0) ? split.beneficiary : messageSender;
1047
1069
 
1048
1070
  if (split.projectId != 0) {
1049
1071
  // Get a reference to the receiving project's primary payment terminal for the token.
1072
+ // slither-disable-next-line calls-loop
1050
1073
  IJBTerminal terminal = token == IJBToken(address(0))
1051
1074
  ? IJBTerminal(address(0))
1052
1075
  : DIRECTORY.primaryTerminalOf({projectId: split.projectId, token: address(token)});
@@ -1065,6 +1088,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
1065
1088
  bytes memory metadata = bytes(abi.encodePacked(projectId));
1066
1089
 
1067
1090
  // Try to fulfill the payment.
1091
+ // slither-disable-next-line calls-loop
1068
1092
  try this.executePayReservedTokenToTerminal({
1069
1093
  projectId: split.projectId,
1070
1094
  terminal: terminal,
@@ -1079,7 +1103,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
1079
1103
  split: split,
1080
1104
  tokenCount: splitTokenCount,
1081
1105
  reason: reason,
1082
- caller: _msgSender()
1106
+ caller: messageSender
1083
1107
  });
1084
1108
 
1085
1109
  // If it fails, transfer the tokens from this contract to the beneficiary.
@@ -1088,6 +1112,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
1088
1112
  }
1089
1113
  } else if (beneficiary == address(0xdead)) {
1090
1114
  // If the split has no project ID, and the beneficiary is 0xdead, burn.
1115
+ // slither-disable-next-line calls-loop
1091
1116
  TOKENS.burnFrom({holder: address(this), projectId: projectId, count: splitTokenCount});
1092
1117
  } else {
1093
1118
  // If the split has no project Id, send to beneficiary.
@@ -1107,8 +1132,11 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
1107
1132
  groupId: groupId,
1108
1133
  split: split,
1109
1134
  tokenCount: splitTokenCount,
1110
- caller: _msgSender()
1135
+ caller: messageSender
1111
1136
  });
1137
+ unchecked {
1138
+ ++i;
1139
+ }
1112
1140
  }
1113
1141
  }
1114
1142
 
@@ -139,6 +139,7 @@ contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
139
139
  // slither-disable-next-line reentrancy-no-eth
140
140
  controllerOf[projectId] = controller;
141
141
 
142
+ // slither-disable-next-line reentrancy-events
142
143
  emit SetController({projectId: projectId, controller: controller, caller: msg.sender});
143
144
 
144
145
  // Notify the new controller that migration is complete and it is now the active controller.
@@ -209,23 +210,23 @@ contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
209
210
  /// @param projectId The ID of the project whose terminals are being set.
210
211
  /// @param terminals An array of terminal addresses to set for the project.
211
212
  function setTerminalsOf(uint256 projectId, IJBTerminal[] calldata terminals) external override {
213
+ // Cache the controller to avoid redundant storage reads.
214
+ IERC165 controller = controllerOf[projectId];
215
+
212
216
  // Enforce permissions.
213
217
  _requirePermissionAllowingOverrideFrom({
214
218
  account: PROJECTS.ownerOf(projectId),
215
219
  projectId: projectId,
216
220
  permissionId: JBPermissionIds.SET_TERMINALS,
217
- alsoGrantAccessIf: msg.sender == address(controllerOf[projectId])
221
+ alsoGrantAccessIf: msg.sender == address(controller)
218
222
  });
219
223
 
220
- // Keep a reference to the project's controller.
221
- IERC165 controller = controllerOf[projectId];
222
-
223
224
  // Get a reference to the flag indicating whether the project is allowed to set its terminals.
224
225
  bool allowSetTerminals = !controller.supportsInterface(type(IJBDirectoryAccessControl).interfaceId)
225
226
  || IJBDirectoryAccessControl(address(controller)).setTerminalsAllowed(projectId);
226
227
 
227
228
  // If the caller is not the project's controller, the project's ruleset must allow setting terminals.
228
- if (msg.sender != address(controllerOf[projectId]) && !allowSetTerminals) {
229
+ if (msg.sender != address(controller) && !allowSetTerminals) {
229
230
  revert JBDirectory_SetTerminalsNotAllowed(projectId);
230
231
  }
231
232
 
@@ -234,9 +235,15 @@ contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
234
235
 
235
236
  // If there are any duplicates, revert.
236
237
  if (terminals.length > 1) {
237
- for (uint256 i; i < terminals.length; i++) {
238
- for (uint256 j = i + 1; j < terminals.length; j++) {
238
+ for (uint256 i; i < terminals.length;) {
239
+ for (uint256 j = i + 1; j < terminals.length;) {
239
240
  if (terminals[i] == terminals[j]) revert JBDirectory_DuplicateTerminals(terminals[i]);
241
+ unchecked {
242
+ ++j;
243
+ }
244
+ }
245
+ unchecked {
246
+ ++i;
240
247
  }
241
248
  }
242
249
  }
@@ -266,14 +273,14 @@ contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
266
273
  return primaryTerminal;
267
274
  }
268
275
 
269
- // Keep a reference to the project's terminals.
270
- IJBTerminal[] memory terminals = _terminalsOf[projectId];
276
+ // Keep a storage reference to the project's terminals to avoid copying the array to memory.
277
+ IJBTerminal[] storage terminals = _terminalsOf[projectId];
271
278
 
272
279
  // Keep a reference to the number of terminals the project has.
273
280
  uint256 numberOfTerminals = terminals.length;
274
281
 
275
282
  // Return the first terminal which accepts the specified token.
276
- for (uint256 i; i < numberOfTerminals; i++) {
283
+ for (uint256 i; i < numberOfTerminals;) {
277
284
  // Keep a reference to the terminal being iterated on.
278
285
  IJBTerminal terminal = terminals[i];
279
286
 
@@ -282,6 +289,9 @@ contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
282
289
  if (terminal.accountingContextForTokenOf({projectId: projectId, token: token}).token != address(0)) {
283
290
  return terminal;
284
291
  }
292
+ unchecked {
293
+ ++i;
294
+ }
285
295
  }
286
296
 
287
297
  // Not found.
@@ -304,15 +314,18 @@ contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
304
314
  /// @param terminal The terminal to check for.
305
315
  /// @return A flag indicating whether the project uses the terminal.
306
316
  function isTerminalOf(uint256 projectId, IJBTerminal terminal) public view override returns (bool) {
307
- // Keep a reference to the project's terminals.
308
- IJBTerminal[] memory terminals = _terminalsOf[projectId];
317
+ // Keep a storage reference to the project's terminals to avoid copying the array to memory.
318
+ IJBTerminal[] storage terminals = _terminalsOf[projectId];
309
319
 
310
320
  // Keep a reference to the number of terminals the project has.
311
321
  uint256 numberOfTerminals = terminals.length;
312
322
 
313
323
  // Loop through and return true if the terminal is found.
314
- for (uint256 i; i < numberOfTerminals; i++) {
324
+ for (uint256 i; i < numberOfTerminals;) {
315
325
  if (terminals[i] == terminal) return true;
326
+ unchecked {
327
+ ++i;
328
+ }
316
329
  }
317
330
 
318
331
  // Otherwise, return false.
@@ -82,7 +82,7 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
82
82
  uint256 numberOfFundAccessLimitGroups = fundAccessLimitGroups.length;
83
83
 
84
84
  // Set payout limits if there are any.
85
- for (uint256 i; i < numberOfFundAccessLimitGroups; i++) {
85
+ for (uint256 i; i < numberOfFundAccessLimitGroups;) {
86
86
  // Set the limits being iterated on.
87
87
  JBFundAccessLimitGroup calldata fundAccessLimitGroup = fundAccessLimitGroups[i];
88
88
 
@@ -90,7 +90,7 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
90
90
  uint256 numberOfPayoutLimits = fundAccessLimitGroup.payoutLimits.length;
91
91
 
92
92
  // Iterate through each payout limit to validate and store them.
93
- for (uint256 j; j < numberOfPayoutLimits; j++) {
93
+ for (uint256 j; j < numberOfPayoutLimits;) {
94
94
  // Set the payout limit being iterated on.
95
95
  JBCurrencyAmount calldata payoutLimit = fundAccessLimitGroup.payoutLimits[j];
96
96
 
@@ -106,13 +106,16 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
106
106
  uint256(payoutLimit.amount) | (uint256(payoutLimit.currency) << 224)
107
107
  );
108
108
  }
109
+ unchecked {
110
+ ++j;
111
+ }
109
112
  }
110
113
 
111
114
  // Keep a reference to the number of surplus allowances.
112
115
  uint256 numberOfSurplusAllowances = fundAccessLimitGroup.surplusAllowances.length;
113
116
 
114
117
  // Iterate through each surplus allowance to validate and store them.
115
- for (uint256 j; j < numberOfSurplusAllowances; j++) {
118
+ for (uint256 j; j < numberOfSurplusAllowances;) {
116
119
  // Set the surplus allowance being iterated on.
117
120
  JBCurrencyAmount calldata surplusAllowance = fundAccessLimitGroup.surplusAllowances[j];
118
121
 
@@ -128,6 +131,9 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
128
131
  uint256(surplusAllowance.amount) | (uint256(surplusAllowance.currency) << 224)
129
132
  );
130
133
  }
134
+ unchecked {
135
+ ++j;
136
+ }
131
137
  }
132
138
 
133
139
  emit SetFundAccessLimits({
@@ -136,6 +142,9 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
136
142
  fundAccessLimitGroup: fundAccessLimitGroup,
137
143
  caller: msg.sender
138
144
  });
145
+ unchecked {
146
+ ++i;
147
+ }
139
148
  }
140
149
  }
141
150
 
@@ -171,7 +180,7 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
171
180
  uint256 numberOfData = data.length;
172
181
 
173
182
  // Iterate through the stored packed values and return the value of the matching currency.
174
- for (uint256 i; i < numberOfData; i++) {
183
+ for (uint256 i; i < numberOfData;) {
175
184
  // Set the data being iterated on.
176
185
  uint256 packedPayoutLimitData = data[i];
177
186
 
@@ -180,6 +189,9 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
180
189
  // forge-lint: disable-next-line(unsafe-typecast)
181
190
  return uint256(uint224(packedPayoutLimitData));
182
191
  }
192
+ unchecked {
193
+ ++i;
194
+ }
183
195
  }
184
196
  }
185
197
 
@@ -213,7 +225,7 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
213
225
  payoutLimits = new JBCurrencyAmount[](numberOfData);
214
226
 
215
227
  // Iterate through the packed values and format the returned value.
216
- for (uint256 i; i < numberOfData; i++) {
228
+ for (uint256 i; i < numberOfData;) {
217
229
  // Set the data being iterated on.
218
230
  uint256 packedPayoutLimitData = packedPayoutLimitsData[i];
219
231
 
@@ -225,6 +237,9 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
225
237
  // forge-lint: disable-next-line(unsafe-typecast)
226
238
  amount: uint224(packedPayoutLimitData)
227
239
  });
240
+ unchecked {
241
+ ++i;
242
+ }
228
243
  }
229
244
  }
230
245
 
@@ -257,7 +272,7 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
257
272
  uint256 numberOfData = packedSurplusAllowancesData.length;
258
273
 
259
274
  // Iterate through the stored packed values and format the returned value.
260
- for (uint256 i; i < numberOfData; i++) {
275
+ for (uint256 i; i < numberOfData;) {
261
276
  // Set the data being iterated on.
262
277
  uint256 packedSurplusAllowanceData = packedSurplusAllowancesData[i];
263
278
 
@@ -266,6 +281,9 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
266
281
  // forge-lint: disable-next-line(unsafe-typecast)
267
282
  return uint256(uint224(packedSurplusAllowanceData));
268
283
  }
284
+ unchecked {
285
+ ++i;
286
+ }
269
287
  }
270
288
  }
271
289
 
@@ -301,7 +319,7 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
301
319
  surplusAllowances = new JBCurrencyAmount[](numberOfData);
302
320
 
303
321
  // Iterate through the stored packed values and format the returned value.
304
- for (uint256 i; i < numberOfData; i++) {
322
+ for (uint256 i; i < numberOfData;) {
305
323
  // Set the data being iterated on.
306
324
  uint256 packedSurplusAllowanceData = packedSurplusAllowancesData[i];
307
325
 
@@ -313,6 +331,9 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
313
331
  // forge-lint: disable-next-line(unsafe-typecast)
314
332
  amount: uint224(packedSurplusAllowanceData)
315
333
  });
334
+ unchecked {
335
+ ++i;
336
+ }
316
337
  }
317
338
  }
318
339
  }