@ballkidz/defifa 0.0.29 → 0.0.30
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.
- package/ARCHITECTURE.md +2 -2
- package/CHANGELOG.md +2 -2
- package/CRYPTO_ECON.md +1 -1
- package/README.md +3 -3
- package/RISKS.md +1 -1
- package/SKILLS.md +1 -1
- package/STYLE_GUIDE.md +2 -50
- package/package.json +2 -2
- package/src/DefifaDeployer.sol +62 -50
- package/src/DefifaGovernor.sol +74 -63
- package/src/DefifaHook.sol +127 -183
- package/src/DefifaProjectOwner.sol +4 -2
- package/src/DefifaTokenUriResolver.sol +15 -13
- package/src/interfaces/IDefifaDeployer.sol +0 -16
- package/src/interfaces/IDefifaHook.sol +0 -4
- package/src/libraries/DefifaHookLib.sol +261 -151
package/src/DefifaHook.sol
CHANGED
|
@@ -47,24 +47,19 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
47
47
|
// --------------------------- custom errors ------------------------- //
|
|
48
48
|
//*********************************************************************//
|
|
49
49
|
|
|
50
|
-
error
|
|
51
|
-
error
|
|
52
|
-
error
|
|
53
|
-
error
|
|
54
|
-
error
|
|
55
|
-
error
|
|
56
|
-
error
|
|
57
|
-
error
|
|
58
|
-
error
|
|
59
|
-
error
|
|
60
|
-
error
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
|
@@ -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
|
-
//
|
|
336
|
-
uint256 cumulativeMintPrice = DefifaHookLib.
|
|
337
|
-
tokenIds: decodedTokenIds,
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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 (
|
|
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))
|
|
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
|
-
|
|
525
|
-
|
|
526
|
-
) revert JB721Hook_InvalidPay();
|
|
468
|
+
if (msg.value != 0 || !_isProjectTerminal(projectId) || context.projectId != projectId) {
|
|
469
|
+
revert JB721Hook_InvalidPay();
|
|
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
|
-
|
|
629
|
-
|
|
630
|
-
|
|
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(
|
|
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,9 @@ 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
|
-
|
|
715
|
-
|
|
716
|
-
) revert JB721Hook_InvalidCashOut();
|
|
647
|
+
if (msg.value != 0 || !_isProjectTerminal(PROJECT_ID) || context.projectId != PROJECT_ID) {
|
|
648
|
+
revert JB721Hook_InvalidCashOut();
|
|
649
|
+
}
|
|
717
650
|
|
|
718
651
|
// Fetch the cash out hook metadata using the corresponding metadata ID.
|
|
719
652
|
(bool metadataExists, bytes memory metadata) = JBMetadataResolver.getDataFor({
|
|
@@ -735,7 +668,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
735
668
|
uint256 tokenId;
|
|
736
669
|
|
|
737
670
|
// Keep track of whether the cashOut is happening during the complete phase.
|
|
738
|
-
bool isComplete =
|
|
671
|
+
bool isComplete = _currentGamePhaseOf(PROJECT_ID) == DefifaGamePhase.COMPLETE;
|
|
739
672
|
|
|
740
673
|
// Cache the store reference in a local variable to avoid repeated SLOAD in the loop.
|
|
741
674
|
IJB721TiersHookStore hookStore = store;
|
|
@@ -752,10 +685,8 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
752
685
|
}
|
|
753
686
|
|
|
754
687
|
// Burn the token.
|
|
755
|
-
// slither-disable-next-line reentrancy-no-eth
|
|
756
688
|
_burn(tokenId);
|
|
757
689
|
|
|
758
|
-
// slither-disable-next-line calls-loop
|
|
759
690
|
uint256 tierId = hookStore.tierIdOfToken(tokenId);
|
|
760
691
|
if (isComplete) {
|
|
761
692
|
// Track per-tier redemptions during the complete phase.
|
|
@@ -783,13 +714,11 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
783
714
|
// Increment the amount redeemed if this is the complete phase.
|
|
784
715
|
bool beneficiaryReceivedTokens;
|
|
785
716
|
if (isComplete) {
|
|
786
|
-
// slither-disable-next-line reentrancy-benign
|
|
787
717
|
amountRedeemed += context.reclaimedAmount.value;
|
|
788
718
|
|
|
789
719
|
// Claim the $DEFIFA and $NANA tokens for the user.
|
|
790
720
|
// Include pending reserve mint cost in the denominator so that unminted reserves
|
|
791
721
|
// are accounted for, preventing paid holders from claiming a disproportionate share.
|
|
792
|
-
// slither-disable-next-line reentrancy-events
|
|
793
722
|
beneficiaryReceivedTokens = _claimTokensFor({
|
|
794
723
|
beneficiary: context.beneficiary,
|
|
795
724
|
shareToBeneficiary: cumulativeMintPrice,
|
|
@@ -801,10 +730,13 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
801
730
|
// Tokens in 0-weight tiers (losing teams) cannot burn to reclaim fees if no fee tokens were
|
|
802
731
|
// distributed. This is correct behavior — 0-weight means the tier has no claim on the pot. Burning would
|
|
803
732
|
// return 0 value regardless.
|
|
804
|
-
if (context.reclaimedAmount.value == 0 && !beneficiaryReceivedTokens)
|
|
733
|
+
if (context.reclaimedAmount.value == 0 && !beneficiaryReceivedTokens) {
|
|
734
|
+
revert DefifaHook_NothingToClaim({
|
|
735
|
+
reclaimedAmount: context.reclaimedAmount.value, beneficiaryReceivedTokens: beneficiaryReceivedTokens
|
|
736
|
+
});
|
|
737
|
+
}
|
|
805
738
|
|
|
806
739
|
// Decrement the paid mint cost by the cumulative mint price of the tokens being burned.
|
|
807
|
-
// slither-disable-next-line reentrancy-benign
|
|
808
740
|
_totalMintCost -= cumulativeMintPrice;
|
|
809
741
|
}
|
|
810
742
|
|
|
@@ -819,7 +751,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
819
751
|
JB721TiersMintReservesConfig memory data = mintReservesForTiersData[i];
|
|
820
752
|
|
|
821
753
|
// Mint for the tier.
|
|
822
|
-
mintReservesFor(data.tierId, data.count);
|
|
754
|
+
mintReservesFor({tierId: data.tierId, count: data.count});
|
|
823
755
|
|
|
824
756
|
unchecked {
|
|
825
757
|
++i;
|
|
@@ -832,15 +764,15 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
832
764
|
/// @param tierWeights The tier weights to set.
|
|
833
765
|
function setTierCashOutWeightsTo(DefifaTierCashOutWeight[] memory tierWeights) external override onlyOwner {
|
|
834
766
|
// Get a reference to the game phase.
|
|
835
|
-
DefifaGamePhase gamePhase =
|
|
767
|
+
DefifaGamePhase gamePhase = _currentGamePhaseOf(PROJECT_ID);
|
|
836
768
|
|
|
837
769
|
// Make sure the game has ended.
|
|
838
770
|
if (gamePhase != DefifaGamePhase.SCORING) {
|
|
839
|
-
revert DefifaHook_GameIsntScoringYet();
|
|
771
|
+
revert DefifaHook_GameIsntScoringYet({projectId: PROJECT_ID, phase: gamePhase});
|
|
840
772
|
}
|
|
841
773
|
|
|
842
774
|
// Make sure the cashOut weights haven't already been set.
|
|
843
|
-
if (cashOutWeightIsSet) revert DefifaHook_CashoutWeightsAlreadySet();
|
|
775
|
+
if (cashOutWeightIsSet) revert DefifaHook_CashoutWeightsAlreadySet({projectId: PROJECT_ID});
|
|
844
776
|
|
|
845
777
|
// Validate weights and build the array. Reverts on invalid input.
|
|
846
778
|
_tierCashOutWeights =
|
|
@@ -849,7 +781,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
849
781
|
// Mark the cashOut weight as set.
|
|
850
782
|
cashOutWeightIsSet = true;
|
|
851
783
|
|
|
852
|
-
emit TierCashOutWeightsSet(tierWeights, msg.sender);
|
|
784
|
+
emit TierCashOutWeightsSet({tierWeights: tierWeights, caller: msg.sender});
|
|
853
785
|
}
|
|
854
786
|
|
|
855
787
|
/// @notice Delegate this account's voting power for a single tier to a specified delegate.
|
|
@@ -857,11 +789,12 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
857
789
|
/// @param tierId The ID of the tier to delegate attestation units for.
|
|
858
790
|
function setTierDelegateTo(address delegatee, uint256 tierId) public virtual override {
|
|
859
791
|
// Make sure a delegate is specified.
|
|
860
|
-
if (delegatee == address(0)) revert DefifaHook_DelegateAddressZero();
|
|
792
|
+
if (delegatee == address(0)) revert DefifaHook_DelegateAddressZero({tierId: tierId});
|
|
861
793
|
|
|
862
794
|
// Make sure the current game phase is the minting phase.
|
|
863
|
-
|
|
864
|
-
|
|
795
|
+
DefifaGamePhase gamePhase = _currentGamePhaseOf(PROJECT_ID);
|
|
796
|
+
if (gamePhase != DefifaGamePhase.MINT) {
|
|
797
|
+
revert DefifaHook_DelegateChangesUnavailableInThisPhase({projectId: PROJECT_ID, phase: gamePhase});
|
|
865
798
|
}
|
|
866
799
|
|
|
867
800
|
_delegateTier({account: msg.sender, delegatee: delegatee, tierId: tierId});
|
|
@@ -871,8 +804,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
871
804
|
/// @param delegations An array of tiers to set delegates for.
|
|
872
805
|
function setTierDelegatesTo(DefifaDelegation[] memory delegations) external virtual override {
|
|
873
806
|
// Make sure the current game phase is the minting phase.
|
|
874
|
-
|
|
875
|
-
|
|
807
|
+
DefifaGamePhase gamePhase = _currentGamePhaseOf(PROJECT_ID);
|
|
808
|
+
if (gamePhase != DefifaGamePhase.MINT) {
|
|
809
|
+
revert DefifaHook_DelegateChangesUnavailableInThisPhase({projectId: PROJECT_ID, phase: gamePhase});
|
|
876
810
|
}
|
|
877
811
|
|
|
878
812
|
// Keep a reference to the number of tier delegates.
|
|
@@ -886,7 +820,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
886
820
|
data = delegations[i];
|
|
887
821
|
|
|
888
822
|
// Make sure a delegate is specified.
|
|
889
|
-
if (data.delegatee == address(0)) revert DefifaHook_DelegateAddressZero();
|
|
823
|
+
if (data.delegatee == address(0)) revert DefifaHook_DelegateAddressZero({tierId: data.tierId});
|
|
890
824
|
|
|
891
825
|
_delegateTier({account: msg.sender, delegatee: data.delegatee, tierId: data.tierId});
|
|
892
826
|
|
|
@@ -897,33 +831,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
897
831
|
}
|
|
898
832
|
|
|
899
833
|
//*********************************************************************//
|
|
900
|
-
//
|
|
834
|
+
// ---------------------- internal transactions ---------------------- //
|
|
901
835
|
//*********************************************************************//
|
|
902
836
|
|
|
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
837
|
/// @notice Claims the defifa and base protocol tokens for a beneficiary.
|
|
928
838
|
/// @param beneficiary The address to claim tokens for.
|
|
929
839
|
/// @param shareToBeneficiary The share relative to the `outOfTotal` to send the user.
|
|
@@ -957,7 +867,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
957
867
|
// Store the new delegatee
|
|
958
868
|
_tierDelegation[account][tierId] = delegatee;
|
|
959
869
|
|
|
960
|
-
emit DelegateChanged(account, oldDelegate, delegatee);
|
|
870
|
+
emit DelegateChanged({delegator: account, fromDelegate: oldDelegate, toDelegate: delegatee});
|
|
961
871
|
|
|
962
872
|
// Move the attestations.
|
|
963
873
|
_moveTierDelegateAttestations({
|
|
@@ -975,15 +885,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
975
885
|
store.recordBurn(tokenIds);
|
|
976
886
|
}
|
|
977
887
|
|
|
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
888
|
/// @notice Mints a token in all provided tiers.
|
|
988
889
|
/// @param amount The amount to base the mints on. All mints' price floors must fit in this amount.
|
|
989
890
|
/// @param mintTierIds An array of tier IDs that are intended to be minted.
|
|
@@ -1001,7 +902,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1001
902
|
uint256[] memory tokenIds;
|
|
1002
903
|
|
|
1003
904
|
// Record the mint. The returned token IDs correspond to the tiers passed in.
|
|
1004
|
-
// slither-disable-next-line unused-return,reentrancy-benign
|
|
1005
905
|
(tokenIds, leftoverAmount,) = store.recordMint({
|
|
1006
906
|
amount: amount,
|
|
1007
907
|
tierIds: mintTierIds,
|
|
@@ -1025,7 +925,13 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1025
925
|
// Mint the tokens.
|
|
1026
926
|
_mint({to: beneficiary, tokenId: tokenId});
|
|
1027
927
|
|
|
1028
|
-
emit Mint(
|
|
928
|
+
emit Mint({
|
|
929
|
+
tokenId: tokenId,
|
|
930
|
+
tierId: mintTierIds[i],
|
|
931
|
+
beneficiary: beneficiary,
|
|
932
|
+
totalAmountContributed: amount,
|
|
933
|
+
caller: msg.sender
|
|
934
|
+
});
|
|
1029
935
|
|
|
1030
936
|
unchecked {
|
|
1031
937
|
++i;
|
|
@@ -1056,7 +962,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1056
962
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1057
963
|
value: current - uint208(amount)
|
|
1058
964
|
});
|
|
1059
|
-
emit TierDelegateAttestationsChanged(
|
|
965
|
+
emit TierDelegateAttestationsChanged({
|
|
966
|
+
delegate: from, tierId: tierId, previousBalance: oldValue, newBalance: newValue, caller: msg.sender
|
|
967
|
+
});
|
|
1060
968
|
}
|
|
1061
969
|
|
|
1062
970
|
// If not moving to the zero address, update the checkpoints to add the amount.
|
|
@@ -1071,7 +979,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1071
979
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1072
980
|
value: current + uint208(amount)
|
|
1073
981
|
});
|
|
1074
|
-
emit TierDelegateAttestationsChanged(
|
|
982
|
+
emit TierDelegateAttestationsChanged({
|
|
983
|
+
delegate: to, tierId: tierId, previousBalance: oldValue, newBalance: newValue, caller: msg.sender
|
|
984
|
+
});
|
|
1075
985
|
}
|
|
1076
986
|
}
|
|
1077
987
|
|
|
@@ -1079,14 +989,18 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1079
989
|
/// @param context The Juicebox standard project payment data.
|
|
1080
990
|
function _processPayment(JBAfterPayRecordedContext calldata context) internal override {
|
|
1081
991
|
// Make sure the game is being played in the correct currency.
|
|
1082
|
-
if (context.amount.currency != pricingCurrency)
|
|
992
|
+
if (context.amount.currency != pricingCurrency) {
|
|
993
|
+
revert DefifaHook_WrongCurrency({
|
|
994
|
+
expectedCurrency: pricingCurrency, actualCurrency: context.amount.currency
|
|
995
|
+
});
|
|
996
|
+
}
|
|
1083
997
|
|
|
1084
998
|
// Resolve the metadata.
|
|
1085
999
|
(bool found, bytes memory metadata) = JBMetadataResolver.getDataFor({
|
|
1086
1000
|
id: JBMetadataResolver.getId({purpose: "pay", target: CODE_ORIGIN}), metadata: context.payerMetadata
|
|
1087
1001
|
});
|
|
1088
1002
|
|
|
1089
|
-
if (!found) revert DefifaHook_NothingToMint();
|
|
1003
|
+
if (!found) revert DefifaHook_NothingToMint({metadataFound: found, tierCount: 0});
|
|
1090
1004
|
|
|
1091
1005
|
// Decode the metadata.
|
|
1092
1006
|
(address attestationDelegate, uint16[] memory tierIdsToMint) = abi.decode(metadata, (address, uint16[]));
|
|
@@ -1098,7 +1012,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1098
1012
|
}
|
|
1099
1013
|
|
|
1100
1014
|
// Make sure something is being minted.
|
|
1101
|
-
if (tierIdsToMint.length == 0)
|
|
1015
|
+
if (tierIdsToMint.length == 0) {
|
|
1016
|
+
revert DefifaHook_NothingToMint({metadataFound: found, tierCount: tierIdsToMint.length});
|
|
1017
|
+
}
|
|
1102
1018
|
|
|
1103
1019
|
// Compute attestation units per unique tier (validates ascending order, reverts on bad order).
|
|
1104
1020
|
(uint256[] memory tierIds, uint256[] memory attestationAmounts, uint256 uniqueTierCount) =
|
|
@@ -1138,7 +1054,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1138
1054
|
_mintAll({amount: context.amount.value, mintTierIds: tierIdsToMint, beneficiary: context.beneficiary});
|
|
1139
1055
|
|
|
1140
1056
|
// Make sure the buyer isn't overspending.
|
|
1141
|
-
if (leftoverAmount != 0) revert DefifaHook_Overspending();
|
|
1057
|
+
if (leftoverAmount != 0) revert DefifaHook_Overspending({leftoverAmount: leftoverAmount});
|
|
1142
1058
|
}
|
|
1143
1059
|
|
|
1144
1060
|
/// @notice Transfers, mints, or burns tier attestation units. To register a mint, `from` should be zero. To
|
|
@@ -1158,7 +1074,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1158
1074
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1159
1075
|
uint208 newValue = current + uint208(amount);
|
|
1160
1076
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1161
|
-
// slither-disable-next-line unused-return
|
|
1162
1077
|
_totalTierCheckpoints[tierId].push({key: uint48(block.timestamp), value: newValue});
|
|
1163
1078
|
}
|
|
1164
1079
|
|
|
@@ -1168,7 +1083,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1168
1083
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1169
1084
|
uint208 newValue = current - uint208(amount);
|
|
1170
1085
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1171
|
-
// slither-disable-next-line unused-return
|
|
1172
1086
|
_totalTierCheckpoints[tierId].push({key: uint48(block.timestamp), value: newValue});
|
|
1173
1087
|
}
|
|
1174
1088
|
}
|
|
@@ -1183,7 +1097,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1183
1097
|
if (toDelegate == address(0) && to != address(0)) {
|
|
1184
1098
|
toDelegate = to;
|
|
1185
1099
|
_tierDelegation[to][tierId] = to;
|
|
1186
|
-
emit DelegateChanged(to, address(0), to);
|
|
1100
|
+
emit DelegateChanged({delegator: to, fromDelegate: address(0), toDelegate: to});
|
|
1187
1101
|
}
|
|
1188
1102
|
|
|
1189
1103
|
// Move delegated attestations.
|
|
@@ -1202,7 +1116,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1202
1116
|
IJB721TiersHookStore hookStore = store;
|
|
1203
1117
|
|
|
1204
1118
|
// Get a reference to the tier.
|
|
1205
|
-
// slither-disable-next-line calls-loop
|
|
1206
1119
|
JB721Tier memory tier =
|
|
1207
1120
|
hookStore.tierOfTokenId({hook: address(this), tokenId: tokenId, includeResolvedUri: false});
|
|
1208
1121
|
|
|
@@ -1213,21 +1126,13 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1213
1126
|
if (from != address(0)) {
|
|
1214
1127
|
// If transfers are pausable, check if they're paused.
|
|
1215
1128
|
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
1129
|
// If transfers are paused and the NFT isn't being transferred to the zero address, revert.
|
|
1221
|
-
if (
|
|
1222
|
-
to
|
|
1223
|
-
|
|
1224
|
-
(JBRulesetMetadataResolver.metadata(ruleset))
|
|
1225
|
-
)
|
|
1226
|
-
) revert DefifaHook_TransfersPaused();
|
|
1130
|
+
if (to != address(0) && JB721TiersRulesetMetadataResolver.transfersPaused(_rulesetMetadata())) {
|
|
1131
|
+
revert DefifaHook_TransfersPaused({projectId: PROJECT_ID, tokenId: tokenId, from: from, to: to});
|
|
1132
|
+
}
|
|
1227
1133
|
}
|
|
1228
1134
|
|
|
1229
1135
|
// 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
1136
|
if (_firstOwnerOf[tokenId] == address(0)) _firstOwnerOf[tokenId] = from;
|
|
1232
1137
|
}
|
|
1233
1138
|
|
|
@@ -1237,9 +1142,48 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1237
1142
|
}
|
|
1238
1143
|
|
|
1239
1144
|
// Record the transfer after local delegation state has been finalized.
|
|
1240
|
-
// slither-disable-next-line calls-loop
|
|
1241
1145
|
hookStore.recordTransferForTier({tierId: tier.id, from: from, to: to});
|
|
1242
1146
|
|
|
1243
1147
|
return from;
|
|
1244
1148
|
}
|
|
1149
|
+
|
|
1150
|
+
//*********************************************************************//
|
|
1151
|
+
// -------------------------- internal views ------------------------- //
|
|
1152
|
+
//*********************************************************************//
|
|
1153
|
+
|
|
1154
|
+
/// @notice Returns the current phase for the project.
|
|
1155
|
+
/// @param projectId The project ID to check.
|
|
1156
|
+
/// @return The current game phase.
|
|
1157
|
+
function _currentGamePhaseOf(uint256 projectId) internal view returns (DefifaGamePhase) {
|
|
1158
|
+
return gamePhaseReporter.currentGamePhaseOf(projectId);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/// @notice Gets the amount of attestation units an address has for a particular tier.
|
|
1162
|
+
/// @param account The account to get attestation units for.
|
|
1163
|
+
/// @param tierId The ID of the tier to get attestation units for.
|
|
1164
|
+
/// @return The attestation units.
|
|
1165
|
+
function _getTierAttestationUnits(address account, uint256 tierId) internal view virtual returns (uint256) {
|
|
1166
|
+
return store.tierVotingUnitsOf({hook: address(this), account: account, tierId: tierId});
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
/// @notice Returns whether the caller is a terminal for the project.
|
|
1170
|
+
/// @param projectId The project ID to check.
|
|
1171
|
+
/// @return True if the caller is a terminal of the project.
|
|
1172
|
+
function _isProjectTerminal(uint256 projectId) internal view returns (bool) {
|
|
1173
|
+
return DIRECTORY.isTerminalOf({projectId: projectId, terminal: IJBTerminal(msg.sender)});
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/// @notice Computes the total mint cost of all pending (unminted) reserve NFTs across all tiers.
|
|
1177
|
+
/// @dev Used to include pending reserves in the fee token claim denominator so that paid holders
|
|
1178
|
+
/// cannot claim a disproportionate share before reserves are minted.
|
|
1179
|
+
/// @return cost The total mint cost of pending reserves.
|
|
1180
|
+
function _pendingReserveMintCost() internal view returns (uint256 cost) {
|
|
1181
|
+
return DefifaHookLib.pendingReserveMintCost({hookStore: store, hook: address(this)});
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
/// @notice Returns the current ruleset metadata for this project.
|
|
1185
|
+
/// @return The packed ruleset metadata.
|
|
1186
|
+
function _rulesetMetadata() internal view returns (uint256) {
|
|
1187
|
+
return JBRulesetMetadataResolver.metadata(rulesets.currentOf(PROJECT_ID));
|
|
1188
|
+
}
|
|
1245
1189
|
}
|