@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.
- 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 +3 -3
- package/src/DefifaDeployer.sol +62 -50
- package/src/DefifaGovernor.sol +74 -63
- package/src/DefifaHook.sol +130 -184
- 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
|
|
@@ -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
|
-
//
|
|
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
|
-
msg.
|
|
525
|
-
|
|
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
|
-
|
|
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,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
|
-
|
|
715
|
-
|
|
716
|
-
|
|
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 =
|
|
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)
|
|
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 =
|
|
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
|
-
|
|
864
|
-
|
|
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
|
-
|
|
875
|
-
|
|
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
|
-
//
|
|
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(
|
|
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(
|
|
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(
|
|
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)
|
|
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)
|
|
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
|
|
1223
|
-
|
|
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
|
}
|