@ballkidz/defifa 0.0.29 → 0.0.31

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.
@@ -47,24 +47,19 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
47
47
  // --------------------------- custom errors ------------------------- //
48
48
  //*********************************************************************//
49
49
 
50
- error DefifaHook_BadTierOrder();
51
- error DefifaHook_IdenticalTokens();
52
- error DefifaHook_DelegateAddressZero();
53
- error DefifaHook_DelegateChangesUnavailableInThisPhase();
54
- error DefifaHook_GameIsntScoringYet();
55
- error DefifaHook_InvalidTierId();
56
- error DefifaHook_InvalidCashoutWeights();
57
- error DefifaHook_NothingToClaim();
58
- error DefifaHook_NothingToMint();
59
- error DefifaHook_WrongCurrency();
60
- error DefifaHook_Overspending();
61
- error DefifaHook_CashoutWeightsAlreadySet();
62
- error DefifaHook_ReservedTokenMintingPaused();
63
- error DefifaHook_TransfersPaused();
50
+ error DefifaHook_IdenticalTokens(address defifaToken, address baseProtocolToken);
51
+ error DefifaHook_DelegateAddressZero(uint256 tierId);
52
+ error DefifaHook_DelegateChangesUnavailableInThisPhase(uint256 projectId, DefifaGamePhase phase);
53
+ error DefifaHook_GameIsntScoringYet(uint256 projectId, DefifaGamePhase phase);
54
+ error DefifaHook_NothingToClaim(uint256 reclaimedAmount, bool beneficiaryReceivedTokens);
55
+ error DefifaHook_NothingToMint(bool metadataFound, uint256 tierCount);
56
+ error DefifaHook_WrongCurrency(uint256 expectedCurrency, uint256 actualCurrency);
57
+ error DefifaHook_Overspending(uint256 leftoverAmount);
58
+ error DefifaHook_CashoutWeightsAlreadySet(uint256 projectId);
59
+ error DefifaHook_ReservedTokenMintingPaused(uint256 projectId, uint256 tierId);
60
+ error DefifaHook_TransfersPaused(uint256 projectId, uint256 tokenId, address from, address to);
64
61
  error DefifaHook_Unauthorized(uint256 tokenId, address owner, address caller);
65
62
 
66
- event PricingCurrencySet(uint256 currency, address caller);
67
-
68
63
  //*********************************************************************//
69
64
  // --------------------- public constant properties ------------------ //
70
65
  //*********************************************************************//
@@ -175,34 +170,10 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
175
170
  /// between burns and reserves is not linear — it depends on the tier's reserve frequency.
176
171
  /// @param tierId The tier ID.
177
172
  /// @return The adjusted pending reserve count (floored at 0).
178
- // slither-disable-next-line calls-loop
179
173
  function adjustedPendingReservesFor(uint256 tierId) public view returns (uint256) {
180
- uint256 refundBurns = refundedBurnsFrom[tierId];
181
-
182
- // If no refund burns, return the store's value directly.
183
- if (refundBurns == 0) return store.numberOfPendingReservesFor({hook: address(this), tierId: tierId});
184
-
185
- // Get the tier to access reserveFrequency and supply data.
186
- JB721Tier memory tier = store.tierOf({hook: address(this), id: tierId, includeResolvedUri: false});
187
-
188
- // No reserves if no reserve frequency.
189
- if (tier.reserveFrequency == 0) return 0;
190
-
191
- // Calculate the number of reserves already minted.
192
- uint256 reservesMinted = store.numberOfReservesMintedFor({hook: address(this), tierId: tierId});
193
-
194
- // Calculate non-reserve mints: initialSupply - remainingSupply - reservesMinted.
195
- uint256 nonReserveMints = tier.initialSupply - tier.remainingSupply - reservesMinted;
196
-
197
- // Subtract refund burns from non-reserve mints (burns can't exceed non-reserve mints).
198
- uint256 adjustedMints = nonReserveMints > refundBurns ? nonReserveMints - refundBurns : 0;
199
-
200
- // Recalculate available reserves: ceil(adjustedMints / reserveFrequency).
201
- uint256 availableReserves = adjustedMints / tier.reserveFrequency;
202
- if (adjustedMints % tier.reserveFrequency > 0) ++availableReserves;
203
-
204
- // Return pending = available - already minted (floored at 0).
205
- return availableReserves > reservesMinted ? availableReserves - reservesMinted : 0;
174
+ return DefifaHookLib.adjustedPendingReservesFor({
175
+ tierId: tierId, hookStore: store, hook: address(this), refundBurns: refundedBurnsFrom[tierId]
176
+ });
206
177
  }
207
178
 
208
179
  /// @notice The first owner of each token ID, which corresponds to the address that originally contributed to the
@@ -314,7 +285,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
314
285
  )
315
286
  {
316
287
  // Make sure (fungible) project tokens aren't also being cashed out.
317
- if (context.cashOutCount > 0) revert JB721Hook_UnexpectedTokenCashedOut();
288
+ if (context.cashOutCount > 0) revert JB721Hook_UnexpectedTokenCashedOut({cashOutCount: context.cashOutCount});
318
289
 
319
290
  // Fetch the cash out hook metadata using the corresponding metadata ID.
320
291
  (bool metadataExists, bytes memory metadata) = JBMetadataResolver.getDataFor({
@@ -332,30 +303,14 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
332
303
  // Cache the store reference in a local variable to avoid repeated SLOAD.
333
304
  IJB721TiersHookStore hookStore = store;
334
305
 
335
- // Calculate the amount paid to mint the tokens that are being burned.
336
- uint256 cumulativeMintPrice = DefifaHookLib.computeCumulativeMintPrice({
337
- tokenIds: decodedTokenIds, hookStore: hookStore, hook: address(this)
338
- });
339
-
340
- // During refund phases, exclude reserve-minted tokens — they were minted for free and have no paid amount
341
- // to refund.
342
- if (
343
- gamePhase == DefifaGamePhase.MINT || gamePhase == DefifaGamePhase.REFUND
306
+ // During refund phases, reserve-minted tokens were minted for free and have no paid amount to refund.
307
+ uint256 cumulativeMintPrice = DefifaHookLib.computeCumulativeMintPriceForCashOut({
308
+ tokenIds: decodedTokenIds,
309
+ hookStore: hookStore,
310
+ hook: address(this),
311
+ excludeReserveMints: gamePhase == DefifaGamePhase.MINT || gamePhase == DefifaGamePhase.REFUND
344
312
  || gamePhase == DefifaGamePhase.NO_CONTEST
345
- ) {
346
- for (uint256 i; i < decodedTokenIds.length;) {
347
- if (isReserveMint[decodedTokenIds[i]]) {
348
- // slither-disable-next-line calls-loop
349
- cumulativeMintPrice -= hookStore.tierOfTokenId({
350
- hook: address(this), tokenId: decodedTokenIds[i], includeResolvedUri: false
351
- }).price;
352
- }
353
-
354
- unchecked {
355
- ++i;
356
- }
357
- }
358
- }
313
+ });
359
314
 
360
315
  // Use this contract as the only cash out hook.
361
316
  hookSpecifications = new JBCashOutHookSpecification[](1);
@@ -428,18 +383,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
428
383
  return interfaceId == type(IDefifaHook).interfaceId || super.supportsInterface(interfaceId);
429
384
  }
430
385
 
431
- /// @notice The amount of $DEFIFA and $BASE_PROTOCOL tokens this game was allocated from paying the network fee.
432
- /// @return defifaTokenAllocation The $DEFIFA token allocation.
433
- /// @return baseProtocolTokenAllocation The $BASE_PROTOCOL token allocation.
434
- function tokenAllocations()
435
- public
436
- view
437
- returns (uint256 defifaTokenAllocation, uint256 baseProtocolTokenAllocation)
438
- {
439
- defifaTokenAllocation = DEFIFA_TOKEN.balanceOf(address(this));
440
- baseProtocolTokenAllocation = BASE_PROTOCOL_TOKEN.balanceOf(address(this));
441
- }
442
-
443
386
  /// @notice The metadata URI of the provided token ID.
444
387
  /// @dev Defer to the tokenUriResolver if set, otherwise, use the tokenUri set with the token's tier.
445
388
  /// @param tokenId The ID of the token to get the tier URI for.
@@ -459,12 +402,11 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
459
402
  returns (uint256 defifaTokenAmount, uint256 baseProtocolTokenAmount)
460
403
  {
461
404
  // If the game isn't complete, we do not have any tokens to claim.
462
- if (gamePhaseReporter.currentGamePhaseOf(PROJECT_ID) != DefifaGamePhase.COMPLETE) return (0, 0);
405
+ if (_currentGamePhaseOf(PROJECT_ID) != DefifaGamePhase.COMPLETE) return (0, 0);
463
406
 
464
407
  // Include unminted reserves in the denominator. Once reserves are pending, their future recipients are
465
408
  // entitled to fee-token claims as if the reserve NFTs had already been minted; otherwise paid holders could
466
409
  // claim too large a share before the reserve mint transaction lands.
467
- // slither-disable-next-line unused-return
468
410
  return DefifaHookLib.computeTokensClaim({
469
411
  tokenIds: tokenIds,
470
412
  hookStore: store,
@@ -495,7 +437,11 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
495
437
  JB721Hook(_directory)
496
438
  Ownable(msg.sender)
497
439
  {
498
- if (address(_defifaToken) == address(_baseProtocolToken)) revert DefifaHook_IdenticalTokens();
440
+ if (address(_defifaToken) == address(_baseProtocolToken)) {
441
+ revert DefifaHook_IdenticalTokens({
442
+ defifaToken: address(_defifaToken), baseProtocolToken: address(_baseProtocolToken)
443
+ });
444
+ }
499
445
 
500
446
  CODE_ORIGIN = address(this);
501
447
  DEFIFA_TOKEN = _defifaToken;
@@ -509,7 +455,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
509
455
  /// @notice Mints one or more NFTs to the `context.beneficiary` upon payment if conditions are met.
510
456
  /// @dev Reverts if the calling contract is not one of the project's terminals.
511
457
  /// @param context The payment context passed in by the terminal.
512
- // slither-disable-next-line locked-ether
513
458
  function afterPayRecordedWith(JBAfterPayRecordedContext calldata context)
514
459
  external
515
460
  payable
@@ -520,10 +465,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
520
465
 
521
466
  // Make sure the caller is a terminal of the project, and that the call is being made on behalf of an
522
467
  // interaction with the correct project.
523
- if (
524
- msg.value != 0 || !DIRECTORY.isTerminalOf({projectId: projectId, terminal: IJBTerminal(msg.sender)})
525
- || context.projectId != projectId
526
- ) revert JB721Hook_InvalidPay();
468
+ if (msg.value != 0 || !_isProjectTerminal(projectId) || context.projectId != projectId) {
469
+ revert JB721Hook_InvalidPay({caller: msg.sender, contextProjectId: context.projectId, projectId: projectId});
470
+ }
527
471
 
528
472
  // Process the payment.
529
473
  _processPayment(context);
@@ -582,7 +526,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
582
526
  pricingCurrency = _currency;
583
527
  gamePhaseReporter = _gamePhaseReporter;
584
528
  gamePotReporter = _gamePotReporter;
585
- // slither-disable-next-line missing-zero-check
586
529
  defaultAttestationDelegate = _defaultAttestationDelegate;
587
530
 
588
531
  // Store the base URI if provided.
@@ -597,7 +540,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
597
540
  }
598
541
 
599
542
  // Record the provided tiers.
600
- // slither-disable-next-line unused-return
601
543
  _store.recordAddTiers(_tiers);
602
544
 
603
545
  // Keep a reference to the number of tier names.
@@ -615,26 +557,21 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
615
557
 
616
558
  // Transfer ownership to the initializer.
617
559
  _transferOwnership(msg.sender);
618
-
619
- emit PricingCurrencySet(_currency, msg.sender);
620
560
  }
621
561
 
622
562
  /// @notice Mint reserved tokens within the tier for the provided value.
623
563
  /// @param tierId The ID of the tier to mint within.
624
564
  /// @param count The number of reserved tokens to mint.
625
- // slither-disable-next-line reentrancy-benign,reentrancy-no-eth
626
565
  function mintReservesFor(uint256 tierId, uint256 count) public override {
627
566
  // Minting reserves must not be paused.
628
- // slither-disable-next-line calls-loop
629
- if (JB721TiersRulesetMetadataResolver.mintPendingReservesPaused(
630
- (JBRulesetMetadataResolver.metadata(rulesets.currentOf(PROJECT_ID)))
631
- )) revert DefifaHook_ReservedTokenMintingPaused();
567
+ if (JB721TiersRulesetMetadataResolver.mintPendingReservesPaused(_rulesetMetadata())) {
568
+ revert DefifaHook_ReservedTokenMintingPaused({projectId: PROJECT_ID, tierId: tierId});
569
+ }
632
570
 
633
571
  // Cache the store reference in a local variable to avoid repeated SLOAD.
634
572
  IJB721TiersHookStore hookStore = store;
635
573
 
636
574
  // Keep a reference to the reserved token beneficiary.
637
- // slither-disable-next-line calls-loop
638
575
  address reservedTokenBeneficiary = hookStore.reserveBeneficiaryOf({hook: address(this), tierId: tierId});
639
576
 
640
577
  // Get a reference to the old delegate.
@@ -652,21 +589,18 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
652
589
  }
653
590
 
654
591
  // Record the minted reserves for the tier.
655
- // slither-disable-next-line calls-loop
656
592
  uint256[] memory tokenIds = hookStore.recordMintReservesFor({tierId: tierId, count: count});
657
593
 
658
594
  // Keep a reference to the token ID being iterated on.
659
595
  uint256 tokenId;
660
596
 
661
597
  // Fetch the tier details (needed for votingUnits below).
662
- // slither-disable-next-line calls-loop
663
598
  JB721Tier memory tier = hookStore.tierOf({hook: address(this), id: tierId, includeResolvedUri: false});
664
599
 
665
600
  // Increment _totalMintCost so reserved recipients can claim their share of fee tokens ($DEFIFA/$NANA).
666
601
  // Note: reserved mints dilute existing fee token claimants because they increase the total mint cost
667
602
  // denominator without contributing new funds to the fee token balances. This is the intended design —
668
603
  // reserved recipients receive a proportional claim on fee tokens as if they had paid to mint.
669
- // slither-disable-next-line reentrancy-benign
670
604
  _totalMintCost += tier.price * count;
671
605
 
672
606
  for (uint256 i; i < count;) {
@@ -677,10 +611,11 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
677
611
  isReserveMint[tokenId] = true;
678
612
 
679
613
  // Mint the token to the reserve beneficiary.
680
- // slither-disable-next-line reentrancy-no-eth
681
614
  _mint({to: reservedTokenBeneficiary, tokenId: tokenId});
682
615
 
683
- emit MintReservedToken(tokenId, tierId, reservedTokenBeneficiary, msg.sender);
616
+ emit MintReservedToken({
617
+ tokenId: tokenId, tierId: tierId, beneficiary: reservedTokenBeneficiary, caller: msg.sender
618
+ });
684
619
 
685
620
  unchecked {
686
621
  ++i;
@@ -688,7 +623,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
688
623
  }
689
624
 
690
625
  // Transfer the attestation units to the delegate.
691
- // slither-disable-next-line reentrancy-no-eth
692
626
  _transferTierAttestationUnits({
693
627
  from: address(0), to: reservedTokenBeneficiary, tierId: tierId, amount: tier.votingUnits * tokenIds.length
694
628
  });
@@ -710,10 +644,11 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
710
644
  {
711
645
  // Make sure the caller is a terminal of the project, and that the call is being made on behalf of an
712
646
  // interaction with the correct project.
713
- if (
714
- msg.value != 0 || !DIRECTORY.isTerminalOf({projectId: PROJECT_ID, terminal: IJBTerminal(msg.sender)})
715
- || context.projectId != PROJECT_ID
716
- ) revert JB721Hook_InvalidCashOut();
647
+ if (msg.value != 0 || !_isProjectTerminal(PROJECT_ID) || context.projectId != PROJECT_ID) {
648
+ revert JB721Hook_InvalidCashOut({
649
+ caller: msg.sender, contextProjectId: context.projectId, projectId: PROJECT_ID, msgValue: msg.value
650
+ });
651
+ }
717
652
 
718
653
  // Fetch the cash out hook metadata using the corresponding metadata ID.
719
654
  (bool metadataExists, bytes memory metadata) = JBMetadataResolver.getDataFor({
@@ -735,7 +670,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
735
670
  uint256 tokenId;
736
671
 
737
672
  // Keep track of whether the cashOut is happening during the complete phase.
738
- bool isComplete = gamePhaseReporter.currentGamePhaseOf(PROJECT_ID) == DefifaGamePhase.COMPLETE;
673
+ bool isComplete = _currentGamePhaseOf(PROJECT_ID) == DefifaGamePhase.COMPLETE;
739
674
 
740
675
  // Cache the store reference in a local variable to avoid repeated SLOAD in the loop.
741
676
  IJB721TiersHookStore hookStore = store;
@@ -752,10 +687,8 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
752
687
  }
753
688
 
754
689
  // Burn the token.
755
- // slither-disable-next-line reentrancy-no-eth
756
690
  _burn(tokenId);
757
691
 
758
- // slither-disable-next-line calls-loop
759
692
  uint256 tierId = hookStore.tierIdOfToken(tokenId);
760
693
  if (isComplete) {
761
694
  // Track per-tier redemptions during the complete phase.
@@ -783,13 +716,11 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
783
716
  // Increment the amount redeemed if this is the complete phase.
784
717
  bool beneficiaryReceivedTokens;
785
718
  if (isComplete) {
786
- // slither-disable-next-line reentrancy-benign
787
719
  amountRedeemed += context.reclaimedAmount.value;
788
720
 
789
721
  // Claim the $DEFIFA and $NANA tokens for the user.
790
722
  // Include pending reserve mint cost in the denominator so that unminted reserves
791
723
  // are accounted for, preventing paid holders from claiming a disproportionate share.
792
- // slither-disable-next-line reentrancy-events
793
724
  beneficiaryReceivedTokens = _claimTokensFor({
794
725
  beneficiary: context.beneficiary,
795
726
  shareToBeneficiary: cumulativeMintPrice,
@@ -801,10 +732,13 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
801
732
  // Tokens in 0-weight tiers (losing teams) cannot burn to reclaim fees if no fee tokens were
802
733
  // distributed. This is correct behavior — 0-weight means the tier has no claim on the pot. Burning would
803
734
  // return 0 value regardless.
804
- if (context.reclaimedAmount.value == 0 && !beneficiaryReceivedTokens) revert DefifaHook_NothingToClaim();
735
+ if (context.reclaimedAmount.value == 0 && !beneficiaryReceivedTokens) {
736
+ revert DefifaHook_NothingToClaim({
737
+ reclaimedAmount: context.reclaimedAmount.value, beneficiaryReceivedTokens: beneficiaryReceivedTokens
738
+ });
739
+ }
805
740
 
806
741
  // Decrement the paid mint cost by the cumulative mint price of the tokens being burned.
807
- // slither-disable-next-line reentrancy-benign
808
742
  _totalMintCost -= cumulativeMintPrice;
809
743
  }
810
744
 
@@ -819,7 +753,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
819
753
  JB721TiersMintReservesConfig memory data = mintReservesForTiersData[i];
820
754
 
821
755
  // Mint for the tier.
822
- mintReservesFor(data.tierId, data.count);
756
+ mintReservesFor({tierId: data.tierId, count: data.count});
823
757
 
824
758
  unchecked {
825
759
  ++i;
@@ -832,15 +766,15 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
832
766
  /// @param tierWeights The tier weights to set.
833
767
  function setTierCashOutWeightsTo(DefifaTierCashOutWeight[] memory tierWeights) external override onlyOwner {
834
768
  // Get a reference to the game phase.
835
- DefifaGamePhase gamePhase = gamePhaseReporter.currentGamePhaseOf(PROJECT_ID);
769
+ DefifaGamePhase gamePhase = _currentGamePhaseOf(PROJECT_ID);
836
770
 
837
771
  // Make sure the game has ended.
838
772
  if (gamePhase != DefifaGamePhase.SCORING) {
839
- revert DefifaHook_GameIsntScoringYet();
773
+ revert DefifaHook_GameIsntScoringYet({projectId: PROJECT_ID, phase: gamePhase});
840
774
  }
841
775
 
842
776
  // Make sure the cashOut weights haven't already been set.
843
- if (cashOutWeightIsSet) revert DefifaHook_CashoutWeightsAlreadySet();
777
+ if (cashOutWeightIsSet) revert DefifaHook_CashoutWeightsAlreadySet({projectId: PROJECT_ID});
844
778
 
845
779
  // Validate weights and build the array. Reverts on invalid input.
846
780
  _tierCashOutWeights =
@@ -849,7 +783,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
849
783
  // Mark the cashOut weight as set.
850
784
  cashOutWeightIsSet = true;
851
785
 
852
- emit TierCashOutWeightsSet(tierWeights, msg.sender);
786
+ emit TierCashOutWeightsSet({tierWeights: tierWeights, caller: msg.sender});
853
787
  }
854
788
 
855
789
  /// @notice Delegate this account's voting power for a single tier to a specified delegate.
@@ -857,11 +791,12 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
857
791
  /// @param tierId The ID of the tier to delegate attestation units for.
858
792
  function setTierDelegateTo(address delegatee, uint256 tierId) public virtual override {
859
793
  // Make sure a delegate is specified.
860
- if (delegatee == address(0)) revert DefifaHook_DelegateAddressZero();
794
+ if (delegatee == address(0)) revert DefifaHook_DelegateAddressZero({tierId: tierId});
861
795
 
862
796
  // Make sure the current game phase is the minting phase.
863
- if (gamePhaseReporter.currentGamePhaseOf(PROJECT_ID) != DefifaGamePhase.MINT) {
864
- revert DefifaHook_DelegateChangesUnavailableInThisPhase();
797
+ DefifaGamePhase gamePhase = _currentGamePhaseOf(PROJECT_ID);
798
+ if (gamePhase != DefifaGamePhase.MINT) {
799
+ revert DefifaHook_DelegateChangesUnavailableInThisPhase({projectId: PROJECT_ID, phase: gamePhase});
865
800
  }
866
801
 
867
802
  _delegateTier({account: msg.sender, delegatee: delegatee, tierId: tierId});
@@ -871,8 +806,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
871
806
  /// @param delegations An array of tiers to set delegates for.
872
807
  function setTierDelegatesTo(DefifaDelegation[] memory delegations) external virtual override {
873
808
  // Make sure the current game phase is the minting phase.
874
- if (gamePhaseReporter.currentGamePhaseOf(PROJECT_ID) != DefifaGamePhase.MINT) {
875
- revert DefifaHook_DelegateChangesUnavailableInThisPhase();
809
+ DefifaGamePhase gamePhase = _currentGamePhaseOf(PROJECT_ID);
810
+ if (gamePhase != DefifaGamePhase.MINT) {
811
+ revert DefifaHook_DelegateChangesUnavailableInThisPhase({projectId: PROJECT_ID, phase: gamePhase});
876
812
  }
877
813
 
878
814
  // Keep a reference to the number of tier delegates.
@@ -886,7 +822,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
886
822
  data = delegations[i];
887
823
 
888
824
  // Make sure a delegate is specified.
889
- if (data.delegatee == address(0)) revert DefifaHook_DelegateAddressZero();
825
+ if (data.delegatee == address(0)) revert DefifaHook_DelegateAddressZero({tierId: data.tierId});
890
826
 
891
827
  _delegateTier({account: msg.sender, delegatee: data.delegatee, tierId: data.tierId});
892
828
 
@@ -897,33 +833,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
897
833
  }
898
834
 
899
835
  //*********************************************************************//
900
- // ------------------------ internal functions ----------------------- //
836
+ // ---------------------- internal transactions ---------------------- //
901
837
  //*********************************************************************//
902
838
 
903
- /// @notice Computes the total mint cost of all pending (unminted) reserve NFTs across all tiers.
904
- /// @dev Used to include pending reserves in the fee token claim denominator so that paid holders
905
- /// cannot claim a disproportionate share before reserves are minted.
906
- /// @return cost The total mint cost of pending reserves.
907
- function _pendingReserveMintCost() internal view returns (uint256 cost) {
908
- IJB721TiersHookStore hookStore = store;
909
- uint256 numberOfTiers = hookStore.maxTierIdOf(address(this));
910
-
911
- for (uint256 i; i < numberOfTiers;) {
912
- uint256 tierId = i + 1;
913
- uint256 pendingReserves = adjustedPendingReservesFor(tierId);
914
- if (pendingReserves != 0) {
915
- // slither-disable-next-line calls-loop
916
- JB721Tier memory tier = hookStore.tierOf({hook: address(this), id: tierId, includeResolvedUri: false});
917
-
918
- // Pending reserves dilute claims by the same economic weight as paid mints at this tier's price.
919
- cost += pendingReserves * tier.price;
920
- }
921
- unchecked {
922
- ++i;
923
- }
924
- }
925
- }
926
-
927
839
  /// @notice Claims the defifa and base protocol tokens for a beneficiary.
928
840
  /// @param beneficiary The address to claim tokens for.
929
841
  /// @param shareToBeneficiary The share relative to the `outOfTotal` to send the user.
@@ -957,7 +869,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
957
869
  // Store the new delegatee
958
870
  _tierDelegation[account][tierId] = delegatee;
959
871
 
960
- emit DelegateChanged(account, oldDelegate, delegatee);
872
+ emit DelegateChanged({delegator: account, fromDelegate: oldDelegate, toDelegate: delegatee});
961
873
 
962
874
  // Move the attestations.
963
875
  _moveTierDelegateAttestations({
@@ -975,15 +887,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
975
887
  store.recordBurn(tokenIds);
976
888
  }
977
889
 
978
- /// @notice Gets the amount of attestation units an address has for a particular tier.
979
- /// @param account The account to get attestation units for.
980
- /// @param tierId The ID of the tier to get attestation units for.
981
- /// @return The attestation units.
982
- function _getTierAttestationUnits(address account, uint256 tierId) internal view virtual returns (uint256) {
983
- // slither-disable-next-line calls-loop
984
- return store.tierVotingUnitsOf({hook: address(this), account: account, tierId: tierId});
985
- }
986
-
987
890
  /// @notice Mints a token in all provided tiers.
988
891
  /// @param amount The amount to base the mints on. All mints' price floors must fit in this amount.
989
892
  /// @param mintTierIds An array of tier IDs that are intended to be minted.
@@ -1001,7 +904,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1001
904
  uint256[] memory tokenIds;
1002
905
 
1003
906
  // Record the mint. The returned token IDs correspond to the tiers passed in.
1004
- // slither-disable-next-line unused-return,reentrancy-benign
1005
907
  (tokenIds, leftoverAmount,) = store.recordMint({
1006
908
  amount: amount,
1007
909
  tierIds: mintTierIds,
@@ -1025,7 +927,13 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1025
927
  // Mint the tokens.
1026
928
  _mint({to: beneficiary, tokenId: tokenId});
1027
929
 
1028
- emit Mint(tokenId, mintTierIds[i], beneficiary, amount, msg.sender);
930
+ emit Mint({
931
+ tokenId: tokenId,
932
+ tierId: mintTierIds[i],
933
+ beneficiary: beneficiary,
934
+ totalAmountContributed: amount,
935
+ caller: msg.sender
936
+ });
1029
937
 
1030
938
  unchecked {
1031
939
  ++i;
@@ -1056,7 +964,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1056
964
  // forge-lint: disable-next-line(unsafe-typecast)
1057
965
  value: current - uint208(amount)
1058
966
  });
1059
- emit TierDelegateAttestationsChanged(from, tierId, oldValue, newValue, msg.sender);
967
+ emit TierDelegateAttestationsChanged({
968
+ delegate: from, tierId: tierId, previousBalance: oldValue, newBalance: newValue, caller: msg.sender
969
+ });
1060
970
  }
1061
971
 
1062
972
  // If not moving to the zero address, update the checkpoints to add the amount.
@@ -1071,7 +981,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1071
981
  // forge-lint: disable-next-line(unsafe-typecast)
1072
982
  value: current + uint208(amount)
1073
983
  });
1074
- emit TierDelegateAttestationsChanged(to, tierId, oldValue, newValue, msg.sender);
984
+ emit TierDelegateAttestationsChanged({
985
+ delegate: to, tierId: tierId, previousBalance: oldValue, newBalance: newValue, caller: msg.sender
986
+ });
1075
987
  }
1076
988
  }
1077
989
 
@@ -1079,14 +991,18 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1079
991
  /// @param context The Juicebox standard project payment data.
1080
992
  function _processPayment(JBAfterPayRecordedContext calldata context) internal override {
1081
993
  // Make sure the game is being played in the correct currency.
1082
- if (context.amount.currency != pricingCurrency) revert DefifaHook_WrongCurrency();
994
+ if (context.amount.currency != pricingCurrency) {
995
+ revert DefifaHook_WrongCurrency({
996
+ expectedCurrency: pricingCurrency, actualCurrency: context.amount.currency
997
+ });
998
+ }
1083
999
 
1084
1000
  // Resolve the metadata.
1085
1001
  (bool found, bytes memory metadata) = JBMetadataResolver.getDataFor({
1086
1002
  id: JBMetadataResolver.getId({purpose: "pay", target: CODE_ORIGIN}), metadata: context.payerMetadata
1087
1003
  });
1088
1004
 
1089
- if (!found) revert DefifaHook_NothingToMint();
1005
+ if (!found) revert DefifaHook_NothingToMint({metadataFound: found, tierCount: 0});
1090
1006
 
1091
1007
  // Decode the metadata.
1092
1008
  (address attestationDelegate, uint16[] memory tierIdsToMint) = abi.decode(metadata, (address, uint16[]));
@@ -1098,7 +1014,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1098
1014
  }
1099
1015
 
1100
1016
  // Make sure something is being minted.
1101
- if (tierIdsToMint.length == 0) revert DefifaHook_NothingToMint();
1017
+ if (tierIdsToMint.length == 0) {
1018
+ revert DefifaHook_NothingToMint({metadataFound: found, tierCount: tierIdsToMint.length});
1019
+ }
1102
1020
 
1103
1021
  // Compute attestation units per unique tier (validates ascending order, reverts on bad order).
1104
1022
  (uint256[] memory tierIds, uint256[] memory attestationAmounts, uint256 uniqueTierCount) =
@@ -1138,7 +1056,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1138
1056
  _mintAll({amount: context.amount.value, mintTierIds: tierIdsToMint, beneficiary: context.beneficiary});
1139
1057
 
1140
1058
  // Make sure the buyer isn't overspending.
1141
- if (leftoverAmount != 0) revert DefifaHook_Overspending();
1059
+ if (leftoverAmount != 0) revert DefifaHook_Overspending({leftoverAmount: leftoverAmount});
1142
1060
  }
1143
1061
 
1144
1062
  /// @notice Transfers, mints, or burns tier attestation units. To register a mint, `from` should be zero. To
@@ -1158,7 +1076,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1158
1076
  // forge-lint: disable-next-line(unsafe-typecast)
1159
1077
  uint208 newValue = current + uint208(amount);
1160
1078
  // forge-lint: disable-next-line(unsafe-typecast)
1161
- // slither-disable-next-line unused-return
1162
1079
  _totalTierCheckpoints[tierId].push({key: uint48(block.timestamp), value: newValue});
1163
1080
  }
1164
1081
 
@@ -1168,7 +1085,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1168
1085
  // forge-lint: disable-next-line(unsafe-typecast)
1169
1086
  uint208 newValue = current - uint208(amount);
1170
1087
  // forge-lint: disable-next-line(unsafe-typecast)
1171
- // slither-disable-next-line unused-return
1172
1088
  _totalTierCheckpoints[tierId].push({key: uint48(block.timestamp), value: newValue});
1173
1089
  }
1174
1090
  }
@@ -1183,7 +1099,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1183
1099
  if (toDelegate == address(0) && to != address(0)) {
1184
1100
  toDelegate = to;
1185
1101
  _tierDelegation[to][tierId] = to;
1186
- emit DelegateChanged(to, address(0), to);
1102
+ emit DelegateChanged({delegator: to, fromDelegate: address(0), toDelegate: to});
1187
1103
  }
1188
1104
 
1189
1105
  // Move delegated attestations.
@@ -1202,7 +1118,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1202
1118
  IJB721TiersHookStore hookStore = store;
1203
1119
 
1204
1120
  // Get a reference to the tier.
1205
- // slither-disable-next-line calls-loop
1206
1121
  JB721Tier memory tier =
1207
1122
  hookStore.tierOfTokenId({hook: address(this), tokenId: tokenId, includeResolvedUri: false});
1208
1123
 
@@ -1213,21 +1128,13 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1213
1128
  if (from != address(0)) {
1214
1129
  // If transfers are pausable, check if they're paused.
1215
1130
  if (tier.flags.transfersPausable) {
1216
- // Get a reference to the project's current ruleset.
1217
- // slither-disable-next-line calls-loop
1218
- JBRuleset memory ruleset = rulesets.currentOf(PROJECT_ID);
1219
-
1220
1131
  // If transfers are paused and the NFT isn't being transferred to the zero address, revert.
1221
- if (
1222
- to != address(0)
1223
- && JB721TiersRulesetMetadataResolver.transfersPaused(
1224
- (JBRulesetMetadataResolver.metadata(ruleset))
1225
- )
1226
- ) revert DefifaHook_TransfersPaused();
1132
+ if (to != address(0) && JB721TiersRulesetMetadataResolver.transfersPaused(_rulesetMetadata())) {
1133
+ revert DefifaHook_TransfersPaused({projectId: PROJECT_ID, tokenId: tokenId, from: from, to: to});
1134
+ }
1227
1135
  }
1228
1136
 
1229
1137
  // If the token isn't already associated with a first owner, store the sender as the first owner.
1230
- // slither-disable-next-line calls-loop
1231
1138
  if (_firstOwnerOf[tokenId] == address(0)) _firstOwnerOf[tokenId] = from;
1232
1139
  }
1233
1140
 
@@ -1237,9 +1144,48 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1237
1144
  }
1238
1145
 
1239
1146
  // Record the transfer after local delegation state has been finalized.
1240
- // slither-disable-next-line calls-loop
1241
1147
  hookStore.recordTransferForTier({tierId: tier.id, from: from, to: to});
1242
1148
 
1243
1149
  return from;
1244
1150
  }
1151
+
1152
+ //*********************************************************************//
1153
+ // -------------------------- internal views ------------------------- //
1154
+ //*********************************************************************//
1155
+
1156
+ /// @notice Returns the current phase for the project.
1157
+ /// @param projectId The project ID to check.
1158
+ /// @return The current game phase.
1159
+ function _currentGamePhaseOf(uint256 projectId) internal view returns (DefifaGamePhase) {
1160
+ return gamePhaseReporter.currentGamePhaseOf(projectId);
1161
+ }
1162
+
1163
+ /// @notice Gets the amount of attestation units an address has for a particular tier.
1164
+ /// @param account The account to get attestation units for.
1165
+ /// @param tierId The ID of the tier to get attestation units for.
1166
+ /// @return The attestation units.
1167
+ function _getTierAttestationUnits(address account, uint256 tierId) internal view virtual returns (uint256) {
1168
+ return store.tierVotingUnitsOf({hook: address(this), account: account, tierId: tierId});
1169
+ }
1170
+
1171
+ /// @notice Returns whether the caller is a terminal for the project.
1172
+ /// @param projectId The project ID to check.
1173
+ /// @return True if the caller is a terminal of the project.
1174
+ function _isProjectTerminal(uint256 projectId) internal view returns (bool) {
1175
+ return DIRECTORY.isTerminalOf({projectId: projectId, terminal: IJBTerminal(msg.sender)});
1176
+ }
1177
+
1178
+ /// @notice Computes the total mint cost of all pending (unminted) reserve NFTs across all tiers.
1179
+ /// @dev Used to include pending reserves in the fee token claim denominator so that paid holders
1180
+ /// cannot claim a disproportionate share before reserves are minted.
1181
+ /// @return cost The total mint cost of pending reserves.
1182
+ function _pendingReserveMintCost() internal view returns (uint256 cost) {
1183
+ return DefifaHookLib.pendingReserveMintCost({hookStore: store, hook: address(this)});
1184
+ }
1185
+
1186
+ /// @notice Returns the current ruleset metadata for this project.
1187
+ /// @return The packed ruleset metadata.
1188
+ function _rulesetMetadata() internal view returns (uint256) {
1189
+ return JBRulesetMetadataResolver.metadata(rulesets.currentOf(PROJECT_ID));
1190
+ }
1245
1191
  }