@ballkidz/defifa 0.0.6 → 0.0.8
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/ADMINISTRATION.md +3 -3
- package/AUDIT_INSTRUCTIONS.md +422 -0
- package/CRYPTO_ECON.md +5 -5
- package/RISKS.md +38 -335
- package/SKILLS.md +1 -1
- package/STYLE_GUIDE.md +14 -1
- package/USER_JOURNEYS.md +691 -0
- package/package.json +7 -5
- package/script/Deploy.s.sol +26 -13
- package/script/helpers/DefifaDeploymentLib.sol +30 -14
- package/src/DefifaDeployer.sol +225 -187
- package/src/DefifaGovernor.sol +291 -281
- package/src/DefifaHook.sol +81 -42
- package/src/DefifaProjectOwner.sol +27 -4
- package/src/DefifaTokenUriResolver.sol +137 -134
- package/src/enums/DefifaGamePhase.sol +1 -1
- package/src/enums/DefifaScorecardState.sol +1 -1
- package/src/interfaces/IDefifaDeployer.sol +52 -50
- package/src/interfaces/IDefifaGamePhaseReporter.sol +2 -2
- package/src/interfaces/IDefifaGamePotReporter.sol +1 -1
- package/src/interfaces/IDefifaGovernor.sol +53 -54
- package/src/interfaces/IDefifaHook.sol +104 -103
- package/src/interfaces/IDefifaTokenUriResolver.sol +2 -2
- package/src/libraries/DefifaFontImporter.sol +11 -9
- package/src/libraries/DefifaHookLib.sol +68 -53
- package/src/structs/DefifaAttestations.sol +1 -1
- package/src/structs/DefifaDelegation.sol +1 -1
- package/src/structs/DefifaLaunchProjectData.sol +4 -4
- package/src/structs/DefifaOpsData.sol +1 -1
- package/src/structs/DefifaScorecard.sol +1 -1
- package/src/structs/DefifaTierCashOutWeight.sol +1 -1
- package/src/structs/DefifaTierParams.sol +2 -1
- package/test/DefifaAdversarialQuorum.t.sol +602 -0
- package/test/DefifaAuditLowGuards.t.sol +304 -0
- package/test/DefifaFeeAccounting.t.sol +37 -16
- package/test/DefifaGovernor.t.sol +37 -11
- package/test/DefifaHookRegressions.t.sol +14 -12
- package/test/DefifaMintCostInvariant.t.sol +31 -12
- package/test/DefifaNoContest.t.sol +33 -13
- package/test/DefifaSecurity.t.sol +45 -25
- package/test/DefifaUSDC.t.sol +44 -34
- package/test/Fork.t.sol +42 -40
- package/test/SVG.t.sol +2 -2
- package/test/TestAuditGaps.sol +982 -0
- package/test/TestQALastMile.t.sol +511 -0
- package/test/regression/FulfillmentBlocksRatification.t.sol +36 -30
- package/test/regression/GracePeriodBypass.t.sol +15 -10
package/src/DefifaHook.sol
CHANGED
|
@@ -36,7 +36,6 @@ import {DefifaTierCashOutWeight} from "./structs/DefifaTierCashOutWeight.sol";
|
|
|
36
36
|
import {DefifaGamePhase} from "./enums/DefifaGamePhase.sol";
|
|
37
37
|
import {DefifaHookLib} from "./libraries/DefifaHookLib.sol";
|
|
38
38
|
|
|
39
|
-
/// @title DefifaHook
|
|
40
39
|
/// @notice A hook that transforms Juicebox treasury interactions into a Defifa game.
|
|
41
40
|
contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
42
41
|
using Checkpoints for Checkpoints.Trace208;
|
|
@@ -106,17 +105,17 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
106
105
|
//*********************************************************************//
|
|
107
106
|
|
|
108
107
|
/// @notice The $DEFIFA token that is expected to be issued from paying fees.
|
|
109
|
-
IERC20 public immutable override
|
|
108
|
+
IERC20 public immutable override DEFIFA_TOKEN;
|
|
110
109
|
|
|
111
110
|
/// @notice The $BASE_PROTOCOL token that is expected to be issued from paying fees.
|
|
112
|
-
IERC20 public immutable override
|
|
111
|
+
IERC20 public immutable override BASE_PROTOCOL_TOKEN;
|
|
113
112
|
|
|
114
113
|
//*********************************************************************//
|
|
115
114
|
// --------------------- public stored properties -------------------- //
|
|
116
115
|
//*********************************************************************//
|
|
117
116
|
|
|
118
117
|
/// @notice The address of the origin 'DefifaHook', used to check in the init if the contract is the original or not
|
|
119
|
-
address public immutable override
|
|
118
|
+
address public immutable override CODE_ORIGIN;
|
|
120
119
|
|
|
121
120
|
/// @notice The contract that stores and manages the NFT's data.
|
|
122
121
|
IJB721TiersHookStore public override store;
|
|
@@ -266,8 +265,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
266
265
|
if (context.cashOutCount > 0) revert JB721Hook_UnexpectedTokenCashedOut();
|
|
267
266
|
|
|
268
267
|
// Fetch the cash out hook metadata using the corresponding metadata ID.
|
|
269
|
-
(bool metadataExists, bytes memory metadata) =
|
|
270
|
-
JBMetadataResolver.
|
|
268
|
+
(bool metadataExists, bytes memory metadata) = JBMetadataResolver.getDataFor({
|
|
269
|
+
id: JBMetadataResolver.getId({purpose: "cashOut", target: CODE_ORIGIN}), metadata: context.metadata
|
|
270
|
+
});
|
|
271
271
|
|
|
272
272
|
uint256[] memory decodedTokenIds;
|
|
273
273
|
|
|
@@ -278,19 +278,21 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
278
278
|
DefifaGamePhase _gamePhase = gamePhaseReporter.currentGamePhaseOf(context.projectId);
|
|
279
279
|
|
|
280
280
|
// Calculate the amount paid to mint the tokens that are being burned.
|
|
281
|
-
uint256 _cumulativeMintPrice =
|
|
282
|
-
|
|
281
|
+
uint256 _cumulativeMintPrice = DefifaHookLib.computeCumulativeMintPrice({
|
|
282
|
+
tokenIds: decodedTokenIds, hookStore: store, hook: address(this)
|
|
283
|
+
});
|
|
283
284
|
|
|
284
285
|
// Use this contract as the only cash out hook.
|
|
285
286
|
hookSpecifications = new JBCashOutHookSpecification[](1);
|
|
286
|
-
hookSpecifications[0] =
|
|
287
|
+
hookSpecifications[0] =
|
|
288
|
+
JBCashOutHookSpecification({hook: this, amount: 0, metadata: abi.encode(_cumulativeMintPrice)});
|
|
287
289
|
|
|
288
290
|
// Compute the cash out count based on the game phase.
|
|
289
291
|
cashOutCount = DefifaHookLib.computeCashOutCount({
|
|
290
292
|
gamePhase: _gamePhase,
|
|
291
293
|
cumulativeMintPrice: _cumulativeMintPrice,
|
|
292
294
|
surplusValue: context.surplus.value,
|
|
293
|
-
|
|
295
|
+
totalAmountRedeemed: amountRedeemed,
|
|
294
296
|
cumulativeCashOutWeight: cashOutWeightOf(decodedTokenIds)
|
|
295
297
|
});
|
|
296
298
|
|
|
@@ -313,7 +315,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
313
315
|
{
|
|
314
316
|
cumulativeWeight = DefifaHookLib.computeCashOutWeightBatch({
|
|
315
317
|
tokenIds: tokenIds,
|
|
316
|
-
|
|
318
|
+
hookStore: store,
|
|
317
319
|
hook: address(this),
|
|
318
320
|
tierCashOutWeights: _tierCashOutWeights,
|
|
319
321
|
tokensRedeemedFrom: tokensRedeemedFrom
|
|
@@ -326,7 +328,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
326
328
|
function cashOutWeightOf(uint256 tokenId) public view override returns (uint256) {
|
|
327
329
|
return DefifaHookLib.computeCashOutWeight({
|
|
328
330
|
tokenId: tokenId,
|
|
329
|
-
|
|
331
|
+
hookStore: store,
|
|
330
332
|
hook: address(this),
|
|
331
333
|
tierCashOutWeights: _tierCashOutWeights,
|
|
332
334
|
tokensRedeemedFrom: tokensRedeemedFrom
|
|
@@ -336,7 +338,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
336
338
|
/// @notice The amount of tokens of a tier that are currently in circulation.
|
|
337
339
|
/// @param tierId The ID of the tier to get the current supply of.
|
|
338
340
|
function currentSupplyOfTier(uint256 tierId) public view returns (uint256) {
|
|
339
|
-
return DefifaHookLib.computeCurrentSupply({
|
|
341
|
+
return DefifaHookLib.computeCurrentSupply({hookStore: store, hook: address(this), tierId: tierId});
|
|
340
342
|
}
|
|
341
343
|
|
|
342
344
|
/// @notice Indicates if this contract adheres to the specified interface.
|
|
@@ -354,8 +356,8 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
354
356
|
view
|
|
355
357
|
returns (uint256 defifaTokenAllocation, uint256 baseProtocolTokenAllocation)
|
|
356
358
|
{
|
|
357
|
-
defifaTokenAllocation =
|
|
358
|
-
baseProtocolTokenAllocation =
|
|
359
|
+
defifaTokenAllocation = DEFIFA_TOKEN.balanceOf(address(this));
|
|
360
|
+
baseProtocolTokenAllocation = BASE_PROTOCOL_TOKEN.balanceOf(address(this));
|
|
359
361
|
}
|
|
360
362
|
|
|
361
363
|
/// @notice The metadata URI of the provided token ID.
|
|
@@ -382,11 +384,11 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
382
384
|
// slither-disable-next-line unused-return
|
|
383
385
|
return DefifaHookLib.computeTokensClaim({
|
|
384
386
|
tokenIds: tokenIds,
|
|
385
|
-
|
|
387
|
+
hookStore: store,
|
|
386
388
|
hook: address(this),
|
|
387
389
|
totalMintCost: _totalMintCost,
|
|
388
|
-
defifaBalance:
|
|
389
|
-
baseProtocolBalance:
|
|
390
|
+
defifaBalance: DEFIFA_TOKEN.balanceOf(address(this)),
|
|
391
|
+
baseProtocolBalance: BASE_PROTOCOL_TOKEN.balanceOf(address(this))
|
|
390
392
|
});
|
|
391
393
|
}
|
|
392
394
|
|
|
@@ -410,9 +412,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
410
412
|
JB721Hook(_directory)
|
|
411
413
|
Ownable(msg.sender)
|
|
412
414
|
{
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
415
|
+
CODE_ORIGIN = address(this);
|
|
416
|
+
DEFIFA_TOKEN = _defifaToken;
|
|
417
|
+
BASE_PROTOCOL_TOKEN = _baseProtocolToken;
|
|
416
418
|
}
|
|
417
419
|
|
|
418
420
|
//*********************************************************************//
|
|
@@ -481,7 +483,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
481
483
|
override
|
|
482
484
|
{
|
|
483
485
|
// Make the original un-initializable.
|
|
484
|
-
if (address(this) ==
|
|
486
|
+
if (address(this) == CODE_ORIGIN) revert();
|
|
485
487
|
|
|
486
488
|
// Stop re-initialization.
|
|
487
489
|
if (address(store) != address(0)) revert();
|
|
@@ -566,6 +568,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
566
568
|
JB721Tier memory _tier = store.tierOf({hook: address(this), id: tierId, includeResolvedUri: false});
|
|
567
569
|
|
|
568
570
|
// Increment _totalMintCost so reserved recipients can claim their share of fee tokens ($DEFIFA/$NANA).
|
|
571
|
+
// Note: reserved mints dilute existing fee token claimants because they increase the total mint cost
|
|
572
|
+
// denominator without contributing new funds to the fee token balances. This is the intended design —
|
|
573
|
+
// reserved recipients receive a proportional claim on fee tokens as if they had paid to mint.
|
|
569
574
|
_totalMintCost += _tier.price * count;
|
|
570
575
|
|
|
571
576
|
for (uint256 _i; _i < count;) {
|
|
@@ -573,7 +578,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
573
578
|
_tokenId = _tokenIds[_i];
|
|
574
579
|
|
|
575
580
|
// Mint the token.
|
|
576
|
-
_mint(_reservedTokenBeneficiary, _tokenId);
|
|
581
|
+
_mint({to: _reservedTokenBeneficiary, tokenId: _tokenId});
|
|
577
582
|
|
|
578
583
|
emit MintReservedToken(_tokenId, tierId, _reservedTokenBeneficiary, msg.sender);
|
|
579
584
|
|
|
@@ -614,9 +619,10 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
614
619
|
) revert JB721Hook_InvalidCashOut();
|
|
615
620
|
|
|
616
621
|
// Fetch the cash out hook metadata using the corresponding metadata ID.
|
|
617
|
-
(bool metadataExists, bytes memory metadata) = JBMetadataResolver.getDataFor(
|
|
618
|
-
JBMetadataResolver.getId("cashOut", METADATA_ID_TARGET),
|
|
619
|
-
|
|
622
|
+
(bool metadataExists, bytes memory metadata) = JBMetadataResolver.getDataFor({
|
|
623
|
+
id: JBMetadataResolver.getId({purpose: "cashOut", target: METADATA_ID_TARGET}),
|
|
624
|
+
metadata: context.cashOutMetadata
|
|
625
|
+
});
|
|
620
626
|
|
|
621
627
|
if (!metadataExists) {
|
|
622
628
|
revert();
|
|
@@ -673,6 +679,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
673
679
|
}
|
|
674
680
|
|
|
675
681
|
// If there's nothing being claimed and we did not distribute fee tokens, revert to prevent burning for nothing.
|
|
682
|
+
// Tokens in 0-weight tiers (losing teams) cannot burn to reclaim fees if no fee tokens were
|
|
683
|
+
// distributed. This is correct behavior — 0-weight means the tier has no claim on the pot. Burning would
|
|
684
|
+
// return 0 value regardless.
|
|
676
685
|
if (context.reclaimedAmount.value == 0 && !_beneficiaryReceivedTokens) revert DefifaHook_NothingToClaim();
|
|
677
686
|
|
|
678
687
|
// Decrement the paid mint cost by the cumulative mint price of the tokens being burned.
|
|
@@ -715,7 +724,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
715
724
|
|
|
716
725
|
// Validate weights and build the array. Reverts on invalid input.
|
|
717
726
|
_tierCashOutWeights =
|
|
718
|
-
DefifaHookLib.validateAndBuildWeights({tierWeights: tierWeights,
|
|
727
|
+
DefifaHookLib.validateAndBuildWeights({tierWeights: tierWeights, hookStore: store, hook: address(this)});
|
|
719
728
|
|
|
720
729
|
// Mark the cashOut weight as set.
|
|
721
730
|
cashOutWeightIsSet = true;
|
|
@@ -727,6 +736,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
727
736
|
/// @param delegatee The account to delegate tier attestation units to.
|
|
728
737
|
/// @param tierId The ID of the tier to delegate attestation units for.
|
|
729
738
|
function setTierDelegateTo(address delegatee, uint256 tierId) public virtual override {
|
|
739
|
+
// Make sure a delegate is specified.
|
|
740
|
+
if (delegatee == address(0)) revert DefifaHook_DelegateAddressZero();
|
|
741
|
+
|
|
730
742
|
// Make sure the current game phase is the minting phase.
|
|
731
743
|
if (gamePhaseReporter.currentGamePhaseOf(PROJECT_ID) != DefifaGamePhase.MINT) {
|
|
732
744
|
revert DefifaHook_DelegateChangesUnavailableInThisPhase();
|
|
@@ -782,11 +794,11 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
782
794
|
returns (bool beneficiaryReceivedTokens)
|
|
783
795
|
{
|
|
784
796
|
return DefifaHookLib.claimTokensFor({
|
|
785
|
-
|
|
797
|
+
beneficiary: _beneficiary,
|
|
786
798
|
shareToBeneficiary: shareToBeneficiary,
|
|
787
799
|
outOfTotal: outOfTotal,
|
|
788
|
-
|
|
789
|
-
|
|
800
|
+
defifaToken: DEFIFA_TOKEN,
|
|
801
|
+
baseProtocolToken: BASE_PROTOCOL_TOKEN
|
|
790
802
|
});
|
|
791
803
|
}
|
|
792
804
|
|
|
@@ -844,6 +856,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
844
856
|
uint256[] memory _tokenIds;
|
|
845
857
|
|
|
846
858
|
// Record the mint. The returned token IDs correspond to the tiers passed in.
|
|
859
|
+
// slither-disable-next-line reentrancy-benign
|
|
847
860
|
(_tokenIds, leftoverAmount) = store.recordMint({
|
|
848
861
|
amount: _amount,
|
|
849
862
|
tierIds: _mintTierIds,
|
|
@@ -865,7 +878,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
865
878
|
_tokenId = _tokenIds[_i];
|
|
866
879
|
|
|
867
880
|
// Mint the tokens.
|
|
868
|
-
_mint(_beneficiary, _tokenId);
|
|
881
|
+
_mint({to: _beneficiary, tokenId: _tokenId});
|
|
869
882
|
|
|
870
883
|
emit Mint(_tokenId, _mintTierIds[_i], _beneficiary, _amount, msg.sender);
|
|
871
884
|
|
|
@@ -889,8 +902,15 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
889
902
|
// Get the current amount for the sending delegate.
|
|
890
903
|
uint208 _current = _delegateTierCheckpoints[_from][_tierId].latest();
|
|
891
904
|
// Set the new amount for the sending delegate.
|
|
892
|
-
|
|
893
|
-
|
|
905
|
+
// uint208 is sufficient for attestation values: each tier's attestation units are bounded by the NFT
|
|
906
|
+
// supply (max ~999_999_999 per tier * 128 tiers), well within uint208's ~4.1e62 range.
|
|
907
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
908
|
+
(uint256 _oldValue, uint256 _newValue) = _delegateTierCheckpoints[_from][_tierId].push({
|
|
909
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
910
|
+
key: uint48(block.timestamp),
|
|
911
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
912
|
+
value: _current - uint208(_amount)
|
|
913
|
+
});
|
|
894
914
|
emit TierDelegateAttestationsChanged(_from, _tierId, _oldValue, _newValue, msg.sender);
|
|
895
915
|
}
|
|
896
916
|
|
|
@@ -899,8 +919,13 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
899
919
|
// Get the current amount for the receiving delegate.
|
|
900
920
|
uint208 _current = _delegateTierCheckpoints[_to][_tierId].latest();
|
|
901
921
|
// Set the new amount for the receiving delegate.
|
|
902
|
-
|
|
903
|
-
|
|
922
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
923
|
+
(uint256 _oldValue, uint256 _newValue) = _delegateTierCheckpoints[_to][_tierId].push({
|
|
924
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
925
|
+
key: uint48(block.timestamp),
|
|
926
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
927
|
+
value: _current + uint208(_amount)
|
|
928
|
+
});
|
|
904
929
|
emit TierDelegateAttestationsChanged(_to, _tierId, _oldValue, _newValue, msg.sender);
|
|
905
930
|
}
|
|
906
931
|
}
|
|
@@ -912,8 +937,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
912
937
|
if (context.amount.currency != pricingCurrency) revert DefifaHook_WrongCurrency();
|
|
913
938
|
|
|
914
939
|
// Resolve the metadata.
|
|
915
|
-
(bool found, bytes memory metadata) =
|
|
916
|
-
JBMetadataResolver.
|
|
940
|
+
(bool found, bytes memory metadata) = JBMetadataResolver.getDataFor({
|
|
941
|
+
id: JBMetadataResolver.getId({purpose: "pay", target: CODE_ORIGIN}), metadata: context.payerMetadata
|
|
942
|
+
});
|
|
917
943
|
|
|
918
944
|
if (!found) revert DefifaHook_NothingToMint();
|
|
919
945
|
|
|
@@ -929,8 +955,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
929
955
|
if (_tierIdsToMint.length == 0) revert DefifaHook_NothingToMint();
|
|
930
956
|
|
|
931
957
|
// Compute attestation units per unique tier (validates ascending order, reverts on bad order).
|
|
932
|
-
(uint256[] memory _tierIds, uint256[] memory _attestationAmounts, uint256 _uniqueTierCount) =
|
|
933
|
-
|
|
958
|
+
(uint256[] memory _tierIds, uint256[] memory _attestationAmounts, uint256 _uniqueTierCount) = DefifaHookLib.computeAttestationUnits({
|
|
959
|
+
tierIdsToMint: _tierIdsToMint, hookStore: store, hook: address(this)
|
|
960
|
+
});
|
|
934
961
|
|
|
935
962
|
// Apply attestation units for each unique tier.
|
|
936
963
|
for (uint256 _i; _i < _uniqueTierCount;) {
|
|
@@ -986,19 +1013,31 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
986
1013
|
|
|
987
1014
|
// If minting, add to the total tier checkpoints.
|
|
988
1015
|
if (_from == address(0)) {
|
|
1016
|
+
// Casting to uint208/uint48 is safe because attestation unit amounts are bounded by NFT supply counts.
|
|
1017
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1018
|
+
uint208 newValue = _current + uint208(_amount);
|
|
1019
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
989
1020
|
// slither-disable-next-line unused-return
|
|
990
|
-
_totalTierCheckpoints[_tierId].push(uint48(block.timestamp),
|
|
1021
|
+
_totalTierCheckpoints[_tierId].push({key: uint48(block.timestamp), value: newValue});
|
|
991
1022
|
}
|
|
992
1023
|
|
|
993
1024
|
// If burning, subtract from the total tier checkpoints.
|
|
994
1025
|
if (_to == address(0)) {
|
|
1026
|
+
// Casting to uint208/uint48 is safe because attestation unit amounts are bounded by NFT supply counts.
|
|
1027
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1028
|
+
uint208 newValue = _current - uint208(_amount);
|
|
1029
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
995
1030
|
// slither-disable-next-line unused-return
|
|
996
|
-
_totalTierCheckpoints[_tierId].push(uint48(block.timestamp),
|
|
1031
|
+
_totalTierCheckpoints[_tierId].push({key: uint48(block.timestamp), value: newValue});
|
|
997
1032
|
}
|
|
998
1033
|
}
|
|
999
1034
|
|
|
1000
1035
|
// Resolve the recipient's delegate. If the recipient has no delegate set, auto-delegate to themselves to
|
|
1001
1036
|
// prevent attestation units from being permanently lost.
|
|
1037
|
+
// Note: delegation persists after token transfers. If Alice delegates to Bob, then transfers her token
|
|
1038
|
+
// to Carol, Carol's attestation units auto-delegate to Carol (not Bob). However, Alice's delegation
|
|
1039
|
+
// to Bob persists — if Alice later receives another token, her units still go to Bob. This matches
|
|
1040
|
+
// ERC5805Votes behavior where delegation is an account-level setting, not a token-level one.
|
|
1002
1041
|
address _toDelegate = _tierDelegation[_to][_tierId];
|
|
1003
1042
|
if (_toDelegate == address(0) && _to != address(0)) {
|
|
1004
1043
|
_toDelegate = _to;
|
|
@@ -1045,7 +1084,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1045
1084
|
}
|
|
1046
1085
|
|
|
1047
1086
|
// Record the transfer.
|
|
1048
|
-
// slither-disable-next-line
|
|
1087
|
+
// slither-disable-next-line reentrancy-events,calls-loop
|
|
1049
1088
|
store.recordTransferForTier({tierId: tier.id, from: from, to: to});
|
|
1050
1089
|
|
|
1051
1090
|
// Dont transfer on mint since the delegation will be transferred more efficiently in _processPayment.
|
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
-
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
5
|
-
import {DefifaDeployer} from "./DefifaDeployer.sol";
|
|
6
4
|
import {IJBPermissions, JBPermissionsData} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
7
5
|
import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
|
|
8
6
|
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
7
|
+
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
8
|
+
|
|
9
|
+
import {DefifaDeployer} from "./DefifaDeployer.sol";
|
|
9
10
|
|
|
10
11
|
/// @notice A contract that can be sent a project to be burned, while still allowing defifa permissions.
|
|
11
12
|
/// @dev Once the project NFT is transferred here, it cannot be recovered. This contract permanently
|
|
12
13
|
/// holds the project NFT and grants SET_SPLIT_GROUPS permission to the Defifa deployer.
|
|
13
14
|
contract DefifaProjectOwner is IERC721Receiver {
|
|
15
|
+
//*********************************************************************//
|
|
16
|
+
// --------------------------- custom errors ------------------------- //
|
|
17
|
+
//*********************************************************************//
|
|
18
|
+
|
|
19
|
+
error DefifaProjectOwner_InvalidSender();
|
|
20
|
+
|
|
21
|
+
//*********************************************************************//
|
|
22
|
+
// --------------- public immutable stored properties ---------------- //
|
|
23
|
+
//*********************************************************************//
|
|
24
|
+
|
|
14
25
|
/// @notice The contract where operator permissions are stored.
|
|
15
26
|
IJBPermissions public immutable PERMISSIONS;
|
|
16
27
|
|
|
@@ -20,6 +31,10 @@ contract DefifaProjectOwner is IERC721Receiver {
|
|
|
20
31
|
/// @notice The Defifa deployer.
|
|
21
32
|
DefifaDeployer public immutable DEPLOYER;
|
|
22
33
|
|
|
34
|
+
//*********************************************************************//
|
|
35
|
+
// -------------------------- constructor ---------------------------- //
|
|
36
|
+
//*********************************************************************//
|
|
37
|
+
|
|
23
38
|
/// @param permissions The contract where operator permissions are stored.
|
|
24
39
|
/// @param projects The contract from which projects are minted.
|
|
25
40
|
/// @param deployer The Defifa deployer which will receive permissions to set splits.
|
|
@@ -29,6 +44,10 @@ contract DefifaProjectOwner is IERC721Receiver {
|
|
|
29
44
|
DEPLOYER = deployer;
|
|
30
45
|
}
|
|
31
46
|
|
|
47
|
+
//*********************************************************************//
|
|
48
|
+
// ---------------------- external transactions ---------------------- //
|
|
49
|
+
//*********************************************************************//
|
|
50
|
+
|
|
32
51
|
/// @notice Give the defifa deployer permission to set splits on this contract's behalf.
|
|
33
52
|
function onERC721Received(
|
|
34
53
|
address operator,
|
|
@@ -44,7 +63,7 @@ contract DefifaProjectOwner is IERC721Receiver {
|
|
|
44
63
|
operator;
|
|
45
64
|
|
|
46
65
|
// Make sure the 721 received is the JBProjects contract.
|
|
47
|
-
if (msg.sender != address(PROJECTS)) revert();
|
|
66
|
+
if (msg.sender != address(PROJECTS)) revert DefifaProjectOwner_InvalidSender();
|
|
48
67
|
|
|
49
68
|
// Set the correct permission.
|
|
50
69
|
uint8[] memory permissionIds = new uint8[](1);
|
|
@@ -54,7 +73,11 @@ contract DefifaProjectOwner is IERC721Receiver {
|
|
|
54
73
|
PERMISSIONS.setPermissionsFor({
|
|
55
74
|
account: address(this),
|
|
56
75
|
permissionsData: JBPermissionsData({
|
|
57
|
-
|
|
76
|
+
// Casting to uint64 is safe because Juicebox project IDs are sequential and will not exceed uint64.
|
|
77
|
+
operator: address(DEPLOYER),
|
|
78
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
79
|
+
projectId: uint64(tokenId),
|
|
80
|
+
permissionIds: permissionIds
|
|
58
81
|
})
|
|
59
82
|
});
|
|
60
83
|
|