@bananapus/721-hook-v6 0.0.43 → 0.0.46

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.
@@ -72,7 +72,7 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
72
72
  */
73
73
  function balanceOf(address owner) public view virtual returns (uint256) {
74
74
  if (owner == address(0)) {
75
- revert ERC721InvalidOwner(address(0));
75
+ revert ERC721InvalidOwner({owner: address(0)});
76
76
  }
77
77
  return _balances[owner];
78
78
  }
@@ -153,13 +153,13 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
153
153
  */
154
154
  function transferFrom(address from, address to, uint256 tokenId) public virtual {
155
155
  if (to == address(0)) {
156
- revert ERC721InvalidReceiver(address(0));
156
+ revert ERC721InvalidReceiver({receiver: address(0)});
157
157
  }
158
158
  // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
159
159
  // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
160
160
  address previousOwner = _update({to: to, tokenId: tokenId, auth: _msgSender()});
161
161
  if (previousOwner != from) {
162
- revert ERC721IncorrectOwner(from, tokenId, previousOwner);
162
+ revert ERC721IncorrectOwner({sender: from, tokenId: tokenId, owner: previousOwner});
163
163
  }
164
164
  }
165
165
 
@@ -206,7 +206,9 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
206
206
  */
207
207
  function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
208
208
  return spender != address(0)
209
- && (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
209
+ && (owner == spender
210
+ || isApprovedForAll({owner: owner, operator: spender})
211
+ || _getApproved(tokenId) == spender);
210
212
  }
211
213
 
212
214
  /**
@@ -218,11 +220,11 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
218
220
  * assumption.
219
221
  */
220
222
  function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
221
- if (!_isAuthorized(owner, spender, tokenId)) {
223
+ if (!_isAuthorized({owner: owner, spender: spender, tokenId: tokenId})) {
222
224
  if (owner == address(0)) {
223
- revert ERC721NonexistentToken(tokenId);
225
+ revert ERC721NonexistentToken({tokenId: tokenId});
224
226
  } else {
225
- revert ERC721InsufficientApproval(spender, tokenId);
227
+ revert ERC721InsufficientApproval({operator: spender, tokenId: tokenId});
226
228
  }
227
229
  }
228
230
  }
@@ -280,7 +282,7 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
280
282
 
281
283
  _owners[tokenId] = to;
282
284
 
283
- emit Transfer(from, to, tokenId);
285
+ emit Transfer({from: from, to: to, tokenId: tokenId});
284
286
 
285
287
  return from;
286
288
  }
@@ -299,11 +301,11 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
299
301
  */
300
302
  function _mint(address to, uint256 tokenId) internal {
301
303
  if (to == address(0)) {
302
- revert ERC721InvalidReceiver(address(0));
304
+ revert ERC721InvalidReceiver({receiver: address(0)});
303
305
  }
304
306
  address previousOwner = _update({to: to, tokenId: tokenId, auth: address(0)});
305
307
  if (previousOwner != address(0)) {
306
- revert ERC721InvalidSender(address(0));
308
+ revert ERC721InvalidSender({sender: address(0)});
307
309
  }
308
310
  }
309
311
 
@@ -345,7 +347,7 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
345
347
  function _burn(uint256 tokenId) internal {
346
348
  address previousOwner = _update({to: address(0), tokenId: tokenId, auth: address(0)});
347
349
  if (previousOwner == address(0)) {
348
- revert ERC721NonexistentToken(tokenId);
350
+ revert ERC721NonexistentToken({tokenId: tokenId});
349
351
  }
350
352
  }
351
353
 
@@ -362,13 +364,13 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
362
364
  */
363
365
  function _transfer(address from, address to, uint256 tokenId) internal {
364
366
  if (to == address(0)) {
365
- revert ERC721InvalidReceiver(address(0));
367
+ revert ERC721InvalidReceiver({receiver: address(0)});
366
368
  }
367
369
  address previousOwner = _update({to: to, tokenId: tokenId, auth: address(0)});
368
370
  if (previousOwner == address(0)) {
369
- revert ERC721NonexistentToken(tokenId);
371
+ revert ERC721NonexistentToken({tokenId: tokenId});
370
372
  } else if (previousOwner != from) {
371
- revert ERC721IncorrectOwner(from, tokenId, previousOwner);
373
+ revert ERC721IncorrectOwner({sender: from, tokenId: tokenId, owner: previousOwner});
372
374
  }
373
375
  }
374
376
 
@@ -430,12 +432,12 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
430
432
  address owner = _requireOwned(tokenId);
431
433
 
432
434
  // We do not use _isAuthorized because single-token approvals should not be able to call approve
433
- if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
434
- revert ERC721InvalidApprover(auth);
435
+ if (auth != address(0) && owner != auth && !isApprovedForAll({owner: owner, operator: auth})) {
436
+ revert ERC721InvalidApprover({approver: auth});
435
437
  }
436
438
 
437
439
  if (emitEvent) {
438
- emit Approval(owner, to, tokenId);
440
+ emit Approval({owner: owner, approved: to, tokenId: tokenId});
439
441
  }
440
442
  }
441
443
 
@@ -452,10 +454,10 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
452
454
  */
453
455
  function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
454
456
  if (operator == address(0)) {
455
- revert ERC721InvalidOperator(operator);
457
+ revert ERC721InvalidOperator({operator: operator});
456
458
  }
457
459
  _operatorApprovals[owner][operator] = approved;
458
- emit ApprovalForAll(owner, operator, approved);
460
+ emit ApprovalForAll({owner: owner, operator: operator, approved: approved});
459
461
  }
460
462
 
461
463
  /**
@@ -467,7 +469,7 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
467
469
  function _requireOwned(uint256 tokenId) internal view returns (address) {
468
470
  address owner = _ownerOf(tokenId);
469
471
  if (owner == address(0)) {
470
- revert ERC721NonexistentToken(tokenId);
472
+ revert ERC721NonexistentToken({tokenId: tokenId});
471
473
  }
472
474
  return owner;
473
475
  }
@@ -489,11 +491,11 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
489
491
  bytes4 retval
490
492
  ) {
491
493
  if (retval != IERC721Receiver.onERC721Received.selector) {
492
- revert ERC721InvalidReceiver(to);
494
+ revert ERC721InvalidReceiver({receiver: to});
493
495
  }
494
496
  } catch (bytes memory reason) {
495
497
  if (reason.length == 0) {
496
- revert ERC721InvalidReceiver(to);
498
+ revert ERC721InvalidReceiver({receiver: to});
497
499
  } else {
498
500
  /// @solidity memory-safe-assembly
499
501
  assembly {
@@ -20,19 +20,20 @@ import {IJB721Hook} from "../interfaces/IJB721Hook.sol";
20
20
  import {ERC721} from "./ERC721.sol";
21
21
 
22
22
  /// @title JB721Hook
23
- /// @notice When a project which uses this hook is paid, this hook may mint NFTs to the payer, depending on this hook's
24
- /// setup, the amount paid, and information specified by the payer. The project's owner can enable NFT cash outs
25
- /// through this hook, allowing the NFT holders to burn their NFTs to reclaim funds from the project (in proportion to
26
- /// the NFT's price).
23
+ /// @notice Abstract base for Juicebox 721 hooks. Implements the pay hook and cash-out hook interfaces: when a project
24
+ /// is paid through its terminal, this hook mints NFTs to the payer; when NFT holders cash out, this hook burns their
25
+ /// NFTs and lets the terminal send them their share of the project's surplus (proportional to the NFT's price).
26
+ /// @dev Subclasses (like `JB721TiersHook`) implement the actual minting logic via `_processPayment` and burn
27
+ /// tracking via `_didBurn`.
27
28
  abstract contract JB721Hook is ERC721, IJB721Hook {
28
29
  //*********************************************************************//
29
30
  // --------------------------- custom errors ------------------------- //
30
31
  //*********************************************************************//
31
32
 
32
- error JB721Hook_InvalidCashOut();
33
- error JB721Hook_InvalidPay();
33
+ error JB721Hook_InvalidCashOut(address caller, uint256 contextProjectId, uint256 projectId, uint256 msgValue);
34
+ error JB721Hook_InvalidPay(address caller, uint256 contextProjectId, uint256 projectId);
34
35
  error JB721Hook_UnauthorizedToken(uint256 tokenId, address holder);
35
- error JB721Hook_UnexpectedTokenCashedOut();
36
+ error JB721Hook_UnexpectedTokenCashedOut(uint256 cashOutCount);
36
37
 
37
38
  //*********************************************************************//
38
39
  // --------------- public immutable stored properties ---------------- //
@@ -93,7 +94,9 @@ abstract contract JB721Hook is ERC721, IJB721Hook {
93
94
  )
94
95
  {
95
96
  // Make sure (fungible) project tokens aren't also being cashed out.
96
- if (context.cashOutCount > 0) revert JB721Hook_UnexpectedTokenCashedOut();
97
+ if (context.cashOutCount > 0) {
98
+ revert JB721Hook_UnexpectedTokenCashedOut({cashOutCount: context.cashOutCount});
99
+ }
97
100
 
98
101
  // Fetch the cash out hook metadata using the corresponding metadata ID.
99
102
  (bool metadataExists, bytes memory metadata) = JBMetadataResolver.getDataFor({
@@ -184,7 +187,6 @@ abstract contract JB721Hook is ERC721, IJB721Hook {
184
187
  /// `context.beneficiary`. Part of `IJBCashOutHook`.
185
188
  /// @dev Reverts if the calling contract is not one of the project's terminals.
186
189
  /// @param context The cash out context passed in by the terminal.
187
- // slither-disable-next-line locked-ether
188
190
  function afterCashOutRecordedWith(JBAfterCashOutRecordedContext calldata context)
189
191
  external
190
192
  payable
@@ -199,7 +201,11 @@ abstract contract JB721Hook is ERC721, IJB721Hook {
199
201
  if (
200
202
  msg.value != 0 || !DIRECTORY.isTerminalOf({projectId: projectId, terminal: IJBTerminal(msg.sender)})
201
203
  || context.projectId != projectId
202
- ) revert JB721Hook_InvalidCashOut();
204
+ ) {
205
+ revert JB721Hook_InvalidCashOut({
206
+ caller: msg.sender, contextProjectId: context.projectId, projectId: projectId, msgValue: msg.value
207
+ });
208
+ }
203
209
 
204
210
  // Fetch the cash out hook metadata using the corresponding metadata ID.
205
211
  (bool metadataExists, bytes memory metadata) = JBMetadataResolver.getDataFor({
@@ -236,7 +242,6 @@ abstract contract JB721Hook is ERC721, IJB721Hook {
236
242
  /// `IJBPayHook`.
237
243
  /// @dev Reverts if the calling contract is not one of the project's terminals.
238
244
  /// @param context The payment context passed in by the terminal.
239
- // slither-disable-next-line locked-ether
240
245
  function afterPayRecordedWith(JBAfterPayRecordedContext calldata context) external payable virtual override {
241
246
  uint256 projectId = PROJECT_ID;
242
247
 
@@ -246,7 +251,7 @@ abstract contract JB721Hook is ERC721, IJB721Hook {
246
251
  !DIRECTORY.isTerminalOf({projectId: projectId, terminal: IJBTerminal(msg.sender)})
247
252
  || context.projectId != projectId
248
253
  ) {
249
- revert JB721Hook_InvalidPay();
254
+ revert JB721Hook_InvalidPay({caller: msg.sender, contextProjectId: context.projectId, projectId: projectId});
250
255
  }
251
256
 
252
257
  // Process the payment.
@@ -270,7 +275,8 @@ abstract contract JB721Hook is ERC721, IJB721Hook {
270
275
  PROJECT_ID = projectId;
271
276
  }
272
277
 
273
- /// @notice Process a received payment.
274
- /// @param context The payment context passed in by the terminal.
278
+ /// @notice Process a received payment by minting NFTs and/or updating credits. Subclasses implement the
279
+ /// specific minting logic (e.g., tier selection, credit tracking, split distribution).
280
+ /// @param context The payment context passed in by the terminal (includes amount, payer, beneficiary, metadata).
275
281
  function _processPayment(JBAfterPayRecordedContext calldata context) internal virtual;
276
282
  }
@@ -36,6 +36,6 @@ interface IJB721Checkpoints is IERC5805 {
36
36
  /// Auto-self-delegates on first receive so checkpoints work without manual delegation.
37
37
  /// @param from The previous owner (address(0) on mint).
38
38
  /// @param to The new owner (address(0) on burn).
39
- /// @param tokenId The token ID being transferred (used to look up tier voting units).
39
+ /// @param tokenId The token ID to transfer (used to look up tier voting units).
40
40
  function onTransfer(address from, address to, uint256 tokenId) external;
41
41
  }
@@ -6,9 +6,6 @@ import {IJB721TiersHookStore} from "./IJB721TiersHookStore.sol";
6
6
 
7
7
  /// @notice Deploys JB721Checkpoints clones for JB721TiersHook instances.
8
8
  interface IJB721CheckpointsDeployer {
9
- /// @notice Thrown when the caller is not the hook that the checkpoint module is being deployed for.
10
- error JB721CheckpointsDeployer_Unauthorized();
11
-
12
9
  /// @notice The implementation contract that clones are based on.
13
10
  /// @return The implementation address.
14
11
  // forge-lint: disable-next-line(mixed-case-function)
@@ -18,14 +18,6 @@ import {JB721TiersSetDiscountPercentConfig} from "../structs/JB721TiersSetDiscou
18
18
 
19
19
  /// @notice A 721 tiers hook that mints tiered NFTs for payments and tracks their cash out weight.
20
20
  interface IJB721TiersHook is IJB721Hook {
21
- /// @notice Emitted when an `addToBalanceOf` call reverts during leftover distribution. The funds remain
22
- /// stranded in the hook contract.
23
- /// @param projectId The project ID whose terminal reverted.
24
- /// @param token The token being sent.
25
- /// @param amount The amount that failed to send.
26
- /// @param reason The revert reason bytes.
27
- event AddToBalanceReverted(uint256 indexed projectId, address token, uint256 amount, bytes reason);
28
-
29
21
  /// @notice Emitted when pay credits are added for an account.
30
22
  /// @param amount The amount of credits added.
31
23
  /// @param newTotalCredits The new total credits balance for the account.
@@ -108,7 +100,7 @@ interface IJB721TiersHook is IJB721Hook {
108
100
  /// project's balance.
109
101
  /// @param projectId The project ID the split belongs to.
110
102
  /// @param split The split that reverted.
111
- /// @param amount The amount that was being paid out.
103
+ /// @param amount The amount that was paid out.
112
104
  /// @param reason The revert reason bytes.
113
105
  /// @param caller The address that called the function.
114
106
  event SplitPayoutReverted(uint256 indexed projectId, JBSplit split, uint256 amount, bytes reason, address caller);
@@ -142,7 +134,7 @@ interface IJB721TiersHook is IJB721Hook {
142
134
 
143
135
  /// @notice Context for the pricing of this hook's tiers.
144
136
  /// @return currency The currency used for tier prices.
145
- /// @return decimals The amount of decimals being used in tier prices.
137
+ /// @return decimals The number of decimals used in tier prices.
146
138
  function pricingContext() external view returns (uint256 currency, uint256 decimals);
147
139
 
148
140
  /// @notice The checkpoint module that manages IVotes-compatible checkpointed voting power for this hook's NFTs.
@@ -40,7 +40,7 @@ interface IJB721TiersHookProjectDeployer {
40
40
  returns (uint256 projectId, IJB721TiersHook hook);
41
41
 
42
42
  /// @notice Launches rulesets for a project with an attached 721 tiers hook.
43
- /// @param projectId The ID of the project that rulesets are being launched for.
43
+ /// @param projectId The ID of the project to launch rulesets for.
44
44
  /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook.
45
45
  /// @param launchRulesetsConfig Configuration which dictates the project's new rulesets.
46
46
  /// @param projectUri Metadata URI to associate with the project. Pass an empty string to leave it unchanged.
@@ -60,7 +60,7 @@ interface IJB721TiersHookProjectDeployer {
60
60
  returns (uint256 rulesetId, IJB721TiersHook hook);
61
61
 
62
62
  /// @notice Queues rulesets for a project with an attached 721 tiers hook.
63
- /// @param projectId The ID of the project that rulesets are being queued for.
63
+ /// @param projectId The ID of the project to queue rulesets for.
64
64
  /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook.
65
65
  /// @param queueRulesetsConfig Configuration which dictates the project's newly queued rulesets.
66
66
  /// @param controller The controller that the project's rulesets will be queued with.
@@ -203,19 +203,19 @@ interface IJB721TiersHookStore {
203
203
 
204
204
  /// @notice Record newly added tiers.
205
205
  /// @param tiersToAdd The tiers to add.
206
- /// @return tierIds The IDs of the tiers being added.
206
+ /// @return tierIds The IDs of the tiers added.
207
207
  function recordAddTiers(JB721TierConfig[] calldata tiersToAdd) external returns (uint256[] memory tierIds);
208
208
 
209
- /// @notice Record 721 burns.
209
+ /// @notice Records the burning of tiered 721 tokens, updating supply tracking for the affected tiers.
210
210
  /// @param tokenIds The token IDs of the NFTs to burn.
211
211
  function recordBurn(uint256[] calldata tokenIds) external;
212
212
 
213
- /// @notice Record newly set flags.
213
+ /// @notice Stores updated ruleset metadata flags that control minting, transferring, and tier behavior.
214
214
  /// @param flags The flags to set.
215
215
  function recordFlags(JB721TiersHookFlags calldata flags) external;
216
216
 
217
217
  /// @notice Record 721 mints from the provided tiers.
218
- /// @param amount The amount being spent on NFTs.
218
+ /// @param amount The amount to spend on NFTs.
219
219
  /// @param tierIds The IDs of the tiers to mint from.
220
220
  /// @param isOwnerMint Whether this is a direct owner mint.
221
221
  /// @return tokenIds The token IDs of the NFTs which were minted.
@@ -235,13 +235,13 @@ interface IJB721TiersHookStore {
235
235
  /// @return tokenIds The token IDs of the reserve NFTs which were minted.
236
236
  function recordMintReservesFor(uint256 tierId, uint256 count) external returns (uint256[] memory tokenIds);
237
237
 
238
- /// @notice Record tiers being removed.
239
- /// @param tierIds The IDs of the tiers being removed.
238
+ /// @notice Record tiers to remove.
239
+ /// @param tierIds The IDs of the tiers to remove.
240
240
  function recordRemoveTierIds(uint256[] calldata tierIds) external;
241
241
 
242
242
  /// @notice Record the setting of a discount for a tier.
243
243
  /// @param tierId The ID of the tier to set the discount of.
244
- /// @param discountPercent The new discount percent being applied.
244
+ /// @param discountPercent The new discount percent to apply.
245
245
  function recordSetDiscountPercentOf(uint256 tierId, uint256 discountPercent) external;
246
246
 
247
247
  /// @notice Record a new encoded IPFS URI for a tier.
@@ -254,9 +254,9 @@ interface IJB721TiersHookStore {
254
254
  /// @param resolver The resolver to set.
255
255
  function recordSetTokenUriResolver(IJB721TokenUriResolver resolver) external;
256
256
 
257
- /// @notice Record an 721 transfer.
258
- /// @param tierId The ID of the tier that the 721 being transferred belongs to.
259
- /// @param from The address that the 721 is being transferred from.
260
- /// @param to The address that the 721 is being transferred to.
257
+ /// @notice Records a 721 token transfer, updating the first-owner tracking for the receiving address.
258
+ /// @param tierId The ID of the tier that the 721 to transfer belongs to.
259
+ /// @param from The address to transfer the 721 from.
260
+ /// @param to The address to transfer the 721 to.
261
261
  function recordTransferForTier(uint256 tierId, address from, address to) external;
262
262
  }
@@ -3,6 +3,7 @@ pragma solidity 0.8.28;
3
3
 
4
4
  /// @notice Global constants used across 721 hook contracts.
5
5
  library JB721Constants {
6
+ /// @notice The denominator used when applying tier discount percentages.
6
7
  uint16 public constant DISCOUNT_DENOMINATOR = 200;
7
8
 
8
9
  /// @notice The metadata ID used to identify the 721 beneficiary entry in payment metadata.
@@ -31,7 +31,7 @@ library JB721TiersHookLib {
31
31
  // --------------------------- custom errors ------------------------- //
32
32
  //*********************************************************************//
33
33
 
34
- error JB721TiersHook_CantBuyWithCredits();
34
+ error JB721TiersHook_CantBuyWithCredits(uint256 restrictedCost, uint256 freshValue);
35
35
  error JB721TiersHook_Overspending(uint256 leftoverAmount);
36
36
  error JB721TiersHookLib_NoTerminalForLeftover(uint256 projectId, address token, uint256 leftoverAmount);
37
37
  error JB721TiersHookLib_SplitFallbackFailed(uint256 projectId, address token, uint256 amount, bytes reason);
@@ -79,7 +79,6 @@ library JB721TiersHookLib {
79
79
  ++i;
80
80
  }
81
81
  }
82
- // slither-disable-next-line reentrancy-events
83
82
  store.recordRemoveTierIds(tierIdsToRemove);
84
83
  }
85
84
 
@@ -87,7 +86,6 @@ library JB721TiersHookLib {
87
86
  if (tiersToAdd.length != 0) {
88
87
  uint256[] memory tierIdsAdded = store.recordAddTiers(tiersToAdd);
89
88
 
90
- // slither-disable-next-line reentrancy-events
91
89
  for (uint256 i; i < tiersToAdd.length;) {
92
90
  emit AddTier({tierId: tierIdsAdded[i], tier: tiersToAdd[i], caller: caller});
93
91
 
@@ -113,7 +111,7 @@ library JB721TiersHookLib {
113
111
  /// @param splits The splits contract to read tier split groups from.
114
112
  /// @param projectId The project ID of the hook.
115
113
  /// @param hookAddress The hook address (for computing split group IDs).
116
- /// @param token The token being distributed.
114
+ /// @param token The token to distribute.
117
115
  /// @param amount The total amount to distribute.
118
116
  /// @param decimals The token decimals.
119
117
  /// @param encodedSplitData The encoded per-tier breakdown from hookMetadata.
@@ -135,7 +133,9 @@ library JB721TiersHookLib {
135
133
  SafeERC20.safeTransferFrom({token: IERC20(token), from: msg.sender, to: address(this), value: amount});
136
134
  uint256 receivedAmount = IERC20(token).balanceOf(address(this)) - balanceBefore;
137
135
  if (receivedAmount != amount) {
138
- revert JB721TiersHookLib_TokenTransferAmountMismatch(amount, receivedAmount);
136
+ revert JB721TiersHookLib_TokenTransferAmountMismatch({
137
+ expectedAmount: amount, receivedAmount: receivedAmount
138
+ });
139
139
  }
140
140
  }
141
141
 
@@ -223,16 +223,19 @@ library JB721TiersHookLib {
223
223
  if (tierIdsToMint.length != 0) {
224
224
  uint256 restrictedCost;
225
225
 
226
- // slither-disable-next-line reentrancy-events,reentrancy-no-eth
227
226
  (tokenIds, leftoverAmount, restrictedCost) =
228
227
  store.recordMint({amount: leftoverAmount, tierIds: tierIdsToMint, isOwnerMint: false});
229
228
 
230
229
  // Credit-restricted tiers must be fully covered by fresh payment (not stored credits).
231
- if (restrictedCost > value) revert JB721TiersHook_CantBuyWithCredits();
230
+ if (restrictedCost > value) {
231
+ revert JB721TiersHook_CantBuyWithCredits({restrictedCost: restrictedCost, freshValue: value});
232
+ }
232
233
  }
233
234
 
234
235
  // If overspending isn't allowed, revert.
235
- if (leftoverAmount != 0 && !allowOverspending) revert JB721TiersHook_Overspending(leftoverAmount);
236
+ if (leftoverAmount != 0 && !allowOverspending) {
237
+ revert JB721TiersHook_Overspending({leftoverAmount: leftoverAmount});
238
+ }
236
239
 
237
240
  // Compute the new pay credits balance: leftover + unused credits (held in newPayCredits).
238
241
  newPayCredits = leftoverAmount + newPayCredits;
@@ -259,7 +262,6 @@ library JB721TiersHookLib {
259
262
  uint256[] memory tierIdsAdded = store.recordAddTiers(tiersToAdd);
260
263
 
261
264
  for (uint256 i; i < tiersToAdd.length;) {
262
- // slither-disable-next-line reentrancy-events
263
265
  emit AddTier({tierId: tierIdsAdded[i], tier: tiersToAdd[i], caller: caller});
264
266
 
265
267
  unchecked {
@@ -489,7 +491,6 @@ library JB721TiersHookLib {
489
491
 
490
492
  for (uint256 i; i < tierIdsToMint.length;) {
491
493
  // Get only the pricing fields (lightweight — avoids full struct construction).
492
- // slither-disable-next-line calls-loop
493
494
  (uint104 tierPrice, uint32 tierSplitPercent, uint8 tierDiscountPercent) =
494
495
  store.tierPricingOf({hook: hook, id: tierIdsToMint[i]});
495
496
  if (tierSplitPercent != 0) {
@@ -707,7 +708,6 @@ library JB721TiersHookLib {
707
708
  )
708
709
  private
709
710
  {
710
- // slither-disable-next-line calls-loop
711
711
  JBSplit[] memory tierSplits = splitsContract.splitsOf({projectId: projectId, rulesetId: 0, groupId: groupId});
712
712
 
713
713
  bool isNativeToken = token == JBConstants.NATIVE_TOKEN;
@@ -725,7 +725,6 @@ library JB721TiersHookLib {
725
725
  // On failure, don't re-add to leftoverAmount — this prevents inflating later recipients.
726
726
  // Failed amounts accumulate as the gap between `amount` and `leftoverAmount + total sent`.
727
727
  // After the loop, we re-add leftoverPercentage-based residual naturally.
728
- // slither-disable-next-line calls-loop,reentrancy-no-eth,reentrancy-benign,reentrancy-events
729
728
  if (!_sendPayoutToSplit({
730
729
  directory: directory,
731
730
  split: tierSplits[j],
@@ -752,14 +751,14 @@ library JB721TiersHookLib {
752
751
  leftoverAmount += amount;
753
752
 
754
753
  if (leftoverAmount != 0) {
755
- // slither-disable-next-line calls-loop
756
754
  IJBTerminal terminal = directory.primaryTerminalOf({projectId: projectId, token: token});
757
755
  // Revert if there are leftover funds but no terminal to route them to.
758
756
  if (address(terminal) == address(0)) {
759
- revert JB721TiersHookLib_NoTerminalForLeftover(projectId, token, leftoverAmount);
757
+ revert JB721TiersHookLib_NoTerminalForLeftover({
758
+ projectId: projectId, token: token, leftoverAmount: leftoverAmount
759
+ });
760
760
  }
761
761
  if (isNativeToken) {
762
- // slither-disable-next-line arbitrary-send-eth,calls-loop
763
762
  try terminal.addToBalanceOf{value: leftoverAmount}({
764
763
  projectId: projectId,
765
764
  token: token,
@@ -769,11 +768,12 @@ library JB721TiersHookLib {
769
768
  metadata: bytes("")
770
769
  }) {}
771
770
  catch (bytes memory reason) {
772
- revert JB721TiersHookLib_SplitFallbackFailed(projectId, token, leftoverAmount, reason);
771
+ revert JB721TiersHookLib_SplitFallbackFailed({
772
+ projectId: projectId, token: token, amount: leftoverAmount, reason: reason
773
+ });
773
774
  }
774
775
  } else {
775
776
  SafeERC20.forceApprove({token: IERC20(token), spender: address(terminal), value: leftoverAmount});
776
- // slither-disable-next-line calls-loop
777
777
  try terminal.addToBalanceOf({
778
778
  projectId: projectId,
779
779
  token: token,
@@ -785,7 +785,9 @@ library JB721TiersHookLib {
785
785
  catch (bytes memory reason) {
786
786
  // Reset approval on failure.
787
787
  SafeERC20.forceApprove({token: IERC20(token), spender: address(terminal), value: 0});
788
- revert JB721TiersHookLib_SplitFallbackFailed(projectId, token, leftoverAmount, reason);
788
+ revert JB721TiersHookLib_SplitFallbackFailed({
789
+ projectId: projectId, token: token, amount: leftoverAmount, reason: reason
790
+ });
789
791
  }
790
792
  }
791
793
  }
@@ -817,11 +819,9 @@ library JB721TiersHookLib {
817
819
  if (isNativeToken) {
818
820
  // Wrap in try-catch so a reverting hook doesn't brick all project payments.
819
821
  // On revert, ETH stays with the caller and we return false.
820
- // slither-disable-next-line calls-loop,reentrancy-no-eth,reentrancy-events
821
822
  try split.hook.processSplitWith{value: amount}(context) {
822
823
  return true;
823
824
  } catch (bytes memory reason) {
824
- // slither-disable-next-line reentrancy-events
825
825
  emit SplitPayoutReverted({
826
826
  projectId: projectId, split: split, amount: amount, reason: reason, caller: msg.sender
827
827
  });
@@ -834,10 +834,8 @@ library JB721TiersHookLib {
834
834
  // cause the caller to skip subtracting this amount from leftoverAmount, leading
835
835
  // to a double-spend when the leftover is later sent to the project's balance.
836
836
  SafeERC20.safeTransfer({token: IERC20(token), to: address(split.hook), value: amount});
837
- // slither-disable-next-line calls-loop,reentrancy-no-eth,reentrancy-events
838
837
  try split.hook.processSplitWith(context) {}
839
838
  catch (bytes memory reason) {
840
- // slither-disable-next-line reentrancy-events
841
839
  emit SplitPayoutReverted({
842
840
  projectId: projectId, split: split, amount: amount, reason: reason, caller: msg.sender
843
841
  });
@@ -845,14 +843,12 @@ library JB721TiersHookLib {
845
843
  return true;
846
844
  }
847
845
  } else if (split.projectId != 0) {
848
- // slither-disable-next-line calls-loop
849
846
  IJBTerminal terminal = directory.primaryTerminalOf({projectId: split.projectId, token: token});
850
847
  if (address(terminal) == address(0)) return false;
851
848
 
852
849
  // Wrap terminal calls in try-catch to prevent a failing terminal from bricking payments.
853
850
  if (split.preferAddToBalance) {
854
851
  if (isNativeToken) {
855
- // slither-disable-next-line arbitrary-send-eth,calls-loop,reentrancy-no-eth,reentrancy-events
856
852
  try terminal.addToBalanceOf{value: amount}({
857
853
  projectId: split.projectId,
858
854
  token: token,
@@ -863,7 +859,6 @@ library JB721TiersHookLib {
863
859
  }) {
864
860
  return true;
865
861
  } catch (bytes memory reason) {
866
- // slither-disable-next-line reentrancy-events
867
862
  emit SplitPayoutReverted({
868
863
  projectId: projectId, split: split, amount: amount, reason: reason, caller: msg.sender
869
864
  });
@@ -871,7 +866,6 @@ library JB721TiersHookLib {
871
866
  }
872
867
  } else {
873
868
  SafeERC20.forceApprove({token: IERC20(token), spender: address(terminal), value: amount});
874
- // slither-disable-next-line calls-loop,reentrancy-no-eth,reentrancy-events
875
869
  try terminal.addToBalanceOf({
876
870
  projectId: split.projectId,
877
871
  token: token,
@@ -884,7 +878,6 @@ library JB721TiersHookLib {
884
878
  } catch (bytes memory reason) {
885
879
  // Reset approval on failure so tokens aren't left approved to the terminal.
886
880
  SafeERC20.forceApprove({token: IERC20(token), spender: address(terminal), value: 0});
887
- // slither-disable-next-line reentrancy-events
888
881
  emit SplitPayoutReverted({
889
882
  projectId: projectId, split: split, amount: amount, reason: reason, caller: msg.sender
890
883
  });
@@ -893,7 +886,6 @@ library JB721TiersHookLib {
893
886
  }
894
887
  } else {
895
888
  if (isNativeToken) {
896
- // slither-disable-next-line arbitrary-send-eth,unused-return,calls-loop,reentrancy-events
897
889
  try terminal.pay{value: amount}({
898
890
  projectId: split.projectId,
899
891
  token: token,
@@ -905,7 +897,6 @@ library JB721TiersHookLib {
905
897
  }) {
906
898
  return true;
907
899
  } catch (bytes memory reason) {
908
- // slither-disable-next-line reentrancy-events
909
900
  emit SplitPayoutReverted({
910
901
  projectId: projectId, split: split, amount: amount, reason: reason, caller: msg.sender
911
902
  });
@@ -913,7 +904,6 @@ library JB721TiersHookLib {
913
904
  }
914
905
  } else {
915
906
  SafeERC20.forceApprove({token: IERC20(token), spender: address(terminal), value: amount});
916
- // slither-disable-next-line unused-return,calls-loop,reentrancy-no-eth,reentrancy-events
917
907
  try terminal.pay({
918
908
  projectId: split.projectId,
919
909
  token: token,
@@ -927,7 +917,6 @@ library JB721TiersHookLib {
927
917
  } catch (bytes memory reason) {
928
918
  // Reset approval on failure so tokens aren't left approved to the terminal.
929
919
  SafeERC20.forceApprove({token: IERC20(token), spender: address(terminal), value: 0});
930
- // slither-disable-next-line reentrancy-events
931
920
  emit SplitPayoutReverted({
932
921
  projectId: projectId, split: split, amount: amount, reason: reason, caller: msg.sender
933
922
  });
@@ -937,7 +926,6 @@ library JB721TiersHookLib {
937
926
  }
938
927
  } else if (split.beneficiary != address(0)) {
939
928
  if (isNativeToken) {
940
- // slither-disable-next-line arbitrary-send-eth,calls-loop
941
929
  (bool success,) = split.beneficiary.call{value: amount}("");
942
930
  if (!success) return false;
943
931
  } else {
@@ -945,7 +933,6 @@ library JB721TiersHookLib {
945
933
  // false on failure instead of reverting. This handles non-standard tokens (e.g. USDT)
946
934
  // that return void, while routing failed transfers to the project's balance instead
947
935
  // of bricking all payments.
948
- // slither-disable-next-line calls-loop
949
936
  (bool callSuccess, bytes memory returndata) =
950
937
  address(token).call(abi.encodeCall(IERC20.transfer, (split.beneficiary, amount)));
951
938
  if (!callSuccess || (returndata.length != 0 && !abi.decode(returndata, (bool)))) return false;
@@ -32,7 +32,7 @@ library JBBitmap {
32
32
  /// @dev The `index` is the index that the bit would have if the bitmap were reshaped to a 1*n matrix.
33
33
  function isTierIdRemoved(mapping(uint256 => uint256) storage self, uint256 index) internal view returns (bool) {
34
34
  uint256 depth = _retrieveDepth(index);
35
- return isTierIdRemoved(JBBitmapWord({currentWord: self[depth], currentDepth: depth}), index);
35
+ return isTierIdRemoved({self: JBBitmapWord({currentWord: self[depth], currentDepth: depth}), index: index});
36
36
  }
37
37
 
38
38
  /// @notice Set the bit at the given index to true, indicating that the corresponding tier has been removed.
@@ -8,7 +8,7 @@ pragma solidity ^0.8.0;
8
8
  /// @custom:member noNewTiersWithOwnerMinting A boolean indicating whether attempts to add new tiers with
9
9
  /// `allowOwnerMint` set to true will revert.
10
10
  /// @custom:member preventOverspending A boolean indicating whether payments attempting to spend more than the price of
11
- /// the NFTs being minted will revert.
11
+ /// the NFTs to mint will revert.
12
12
  /// @custom:member issueTokensForSplits A boolean indicating whether payers receive token credit for the portion of
13
13
  /// their payment that is routed to tier splits. When false (default), weight is reduced proportionally.
14
14
  struct JB721TiersHookFlags {