@ballkidz/defifa 0.0.20 → 0.0.21
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/package.json +6 -6
- package/src/DefifaDeployer.sol +18 -8
- package/src/DefifaGovernor.sol +61 -29
- package/src/DefifaHook.sol +36 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ballkidz/defifa",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.21",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0"
|
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
"url": "https://github.com/BallKidz/defifa-collection-deployer"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
17
|
-
"@bananapus/core-v6": "^0.0.
|
|
16
|
+
"@bananapus/721-hook-v6": "^0.0.32",
|
|
17
|
+
"@bananapus/core-v6": "^0.0.32",
|
|
18
18
|
"@bananapus/permission-ids-v6": "^0.0.15",
|
|
19
|
-
"@croptop/core-v6": "^0.0.
|
|
19
|
+
"@croptop/core-v6": "^0.0.31",
|
|
20
20
|
"@openzeppelin/contracts": "^5.6.1",
|
|
21
21
|
"@prb/math": "^4.1.1",
|
|
22
|
-
"@rev-net/core-v6": "^0.0.
|
|
22
|
+
"@rev-net/core-v6": "^0.0.29",
|
|
23
23
|
"scripty.sol": "^2.1.1"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@bananapus/address-registry-v6": "^0.0.17",
|
|
27
|
-
"@sphinx-labs/plugins": "^0.33.
|
|
27
|
+
"@sphinx-labs/plugins": "^0.33.3"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"test": "forge test",
|
package/src/DefifaDeployer.sol
CHANGED
|
@@ -146,13 +146,13 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
|
|
|
146
146
|
// Get a reference to the token being used by the project.
|
|
147
147
|
address token = _opsOf[gameId].token;
|
|
148
148
|
|
|
149
|
-
// Get a reference to the terminal.
|
|
149
|
+
// Get a reference to the terminal via the directory.
|
|
150
150
|
IJBTerminal terminal = CONTROLLER.DIRECTORY().primaryTerminalOf({projectId: gameId, token: token});
|
|
151
151
|
|
|
152
152
|
// Get the accounting context for the project.
|
|
153
153
|
JBAccountingContext memory context = terminal.accountingContextForTokenOf({projectId: gameId, token: token});
|
|
154
154
|
|
|
155
|
-
// Get the current balance.
|
|
155
|
+
// Get the current balance from the terminal's store.
|
|
156
156
|
uint256 pot = IJBMultiTerminal(address(terminal)).STORE()
|
|
157
157
|
.balanceOf({terminal: address(terminal), projectId: gameId, token: token});
|
|
158
158
|
|
|
@@ -221,9 +221,13 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
|
|
|
221
221
|
// Get the game's current funding cycle along with its metadata.
|
|
222
222
|
(JBRuleset memory currentRuleset, JBRulesetMetadata memory metadata) = CONTROLLER.currentRulesetOf(gameId);
|
|
223
223
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
// Cache the cycle number to avoid repeated memory reads.
|
|
225
|
+
uint256 cycleNumber = currentRuleset.cycleNumber;
|
|
226
|
+
|
|
227
|
+
// Return early for the first three phases based on cycle number.
|
|
228
|
+
if (cycleNumber == 0) return DefifaGamePhase.COUNTDOWN;
|
|
229
|
+
if (cycleNumber == 1) return DefifaGamePhase.MINT;
|
|
230
|
+
if (cycleNumber == 2 && _opsOf[gameId].refundPeriodDuration != 0) {
|
|
227
231
|
return DefifaGamePhase.REFUND;
|
|
228
232
|
}
|
|
229
233
|
|
|
@@ -234,7 +238,7 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
|
|
|
234
238
|
// If no-contest has already been triggered, stay in NO_CONTEST.
|
|
235
239
|
if (noContestTriggeredFor[gameId]) return DefifaGamePhase.NO_CONTEST;
|
|
236
240
|
|
|
237
|
-
// Get the game's ops data for the safety mechanism checks.
|
|
241
|
+
// Get the game's ops data for the safety mechanism checks. Cache to avoid repeated SLOAD.
|
|
238
242
|
DefifaOpsData memory ops = _opsOf[gameId];
|
|
239
243
|
|
|
240
244
|
// Check minimum participation threshold: if the treasury balance is below the threshold, the game is
|
|
@@ -660,8 +664,11 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
|
|
|
660
664
|
|
|
661
665
|
// Sum all absolute percents.
|
|
662
666
|
uint256 totalAbsolutePercent = nanaAbsolutePercent + defifaAbsolutePercent;
|
|
663
|
-
for (uint256 i; i < numberOfUserSplits;
|
|
667
|
+
for (uint256 i; i < numberOfUserSplits;) {
|
|
664
668
|
totalAbsolutePercent += initialSplits[i].percent;
|
|
669
|
+
unchecked {
|
|
670
|
+
++i;
|
|
671
|
+
}
|
|
665
672
|
}
|
|
666
673
|
|
|
667
674
|
// Validate that total fee splits don't exceed 100%.
|
|
@@ -677,7 +684,7 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
|
|
|
677
684
|
|
|
678
685
|
// Normalize user splits and copy them over.
|
|
679
686
|
uint256 normalizedTotal;
|
|
680
|
-
for (uint256 i; i < numberOfUserSplits;
|
|
687
|
+
for (uint256 i; i < numberOfUserSplits;) {
|
|
681
688
|
splits[i] = initialSplits[i];
|
|
682
689
|
splits[i].percent = uint32(
|
|
683
690
|
mulDiv({
|
|
@@ -685,6 +692,9 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
|
|
|
685
692
|
})
|
|
686
693
|
);
|
|
687
694
|
normalizedTotal += splits[i].percent;
|
|
695
|
+
unchecked {
|
|
696
|
+
++i;
|
|
697
|
+
}
|
|
688
698
|
}
|
|
689
699
|
|
|
690
700
|
// Add Defifa fee split (normalized).
|
package/src/DefifaGovernor.sol
CHANGED
|
@@ -136,6 +136,7 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
136
136
|
// Keep a reference to the scorecard state.
|
|
137
137
|
DefifaScorecardState state = stateOf({gameId: gameId, scorecardId: scorecardId});
|
|
138
138
|
|
|
139
|
+
// Attestations are only allowed during ACTIVE, SUCCEEDED, or QUEUED states.
|
|
139
140
|
if (
|
|
140
141
|
state != DefifaScorecardState.ACTIVE && state != DefifaScorecardState.SUCCEEDED
|
|
141
142
|
&& state != DefifaScorecardState.QUEUED
|
|
@@ -188,10 +189,10 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
188
189
|
// slither-disable-next-line unused-return
|
|
189
190
|
(, JBRulesetMetadata memory metadata) = CONTROLLER.currentRulesetOf(gameId);
|
|
190
191
|
|
|
191
|
-
// Build the calldata to the target
|
|
192
|
+
// Build the calldata to the target.
|
|
192
193
|
bytes memory scorecardCalldata = _buildScorecardCalldataFor(tierWeights);
|
|
193
194
|
|
|
194
|
-
//
|
|
195
|
+
// Hash the scorecard to derive its ID.
|
|
195
196
|
scorecardId = _hashScorecardOf({gameHook: metadata.dataHook, calldataBytes: scorecardCalldata});
|
|
196
197
|
|
|
197
198
|
// Make sure the proposal being ratified has succeeded.
|
|
@@ -222,7 +223,9 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
222
223
|
/// @param scorecardId The ID of the scorecard to revoke attestation from.
|
|
223
224
|
function revokeAttestationFrom(uint256 gameId, uint256 scorecardId) external virtual override {
|
|
224
225
|
// Only allow revocation during ACTIVE phase.
|
|
225
|
-
if (stateOf(gameId, scorecardId) != DefifaScorecardState.ACTIVE)
|
|
226
|
+
if (stateOf({gameId: gameId, scorecardId: scorecardId}) != DefifaScorecardState.ACTIVE) {
|
|
227
|
+
revert DefifaGovernor_NotAllowed();
|
|
228
|
+
}
|
|
226
229
|
|
|
227
230
|
DefifaAttestations storage attestations = _scorecardAttestationsOf[gameId][scorecardId];
|
|
228
231
|
uint256 weight = attestations.attestedWeightOf[msg.sender];
|
|
@@ -256,7 +259,7 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
256
259
|
// slither-disable-next-line incorrect-equality
|
|
257
260
|
if (_packedScorecardInfoOf[gameId] == 0) revert DefifaGovernor_GameNotFound();
|
|
258
261
|
|
|
259
|
-
//
|
|
262
|
+
// Keep a reference to the number of tier weights in the proposed scorecard.
|
|
260
263
|
uint256 numberOfTierWeights = tierWeights.length;
|
|
261
264
|
|
|
262
265
|
// Get the game's current funding cycle along with its metadata.
|
|
@@ -270,27 +273,31 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
270
273
|
|
|
271
274
|
// If there's a weight assigned to the tier, make sure there is a token backed by it.
|
|
272
275
|
// slither-disable-next-line calls-loop
|
|
273
|
-
for (uint256 i; i < numberOfTierWeights;
|
|
276
|
+
for (uint256 i; i < numberOfTierWeights;) {
|
|
274
277
|
// A nonzero cashout weight is only valid once that tier has live ownership.
|
|
275
278
|
// slither-disable-next-line calls-loop
|
|
276
279
|
uint256 currentTierSupply = IDefifaHook(metadata.dataHook).currentSupplyOfTier(tierWeights[i].id);
|
|
277
280
|
if (tierWeights[i].cashOutWeight > 0 && currentTierSupply == 0) {
|
|
278
281
|
revert DefifaGovernor_UnownedProposedCashoutValue();
|
|
279
282
|
}
|
|
283
|
+
unchecked {
|
|
284
|
+
++i;
|
|
285
|
+
}
|
|
280
286
|
}
|
|
281
287
|
|
|
288
|
+
// Cache the hook store to avoid repeated external calls.
|
|
289
|
+
IJB721TiersHookStore hookStore = IDefifaHook(metadata.dataHook).store();
|
|
290
|
+
|
|
282
291
|
// Run the same structural validation the hook will apply at ratification time so malformed
|
|
283
292
|
// scorecards fail on submission instead of reaching a misleading SUCCEEDED state first.
|
|
284
293
|
// slither-disable-next-line unused-return
|
|
285
|
-
DefifaHookLib.validateAndBuildWeights({
|
|
286
|
-
tierWeights: tierWeights, hookStore: IDefifaHook(metadata.dataHook).store(), hook: metadata.dataHook
|
|
287
|
-
});
|
|
294
|
+
DefifaHookLib.validateAndBuildWeights({tierWeights: tierWeights, hookStore: hookStore, hook: metadata.dataHook});
|
|
288
295
|
|
|
289
296
|
// Hash the scorecard.
|
|
290
297
|
scorecardId =
|
|
291
298
|
_hashScorecardOf({gameHook: metadata.dataHook, calldataBytes: _buildScorecardCalldataFor(tierWeights)});
|
|
292
299
|
|
|
293
|
-
// Store the scorecard
|
|
300
|
+
// Store the scorecard.
|
|
294
301
|
DefifaScorecard storage scorecard = _scorecardOf[gameId][scorecardId];
|
|
295
302
|
if (scorecard.attestationsBegin != 0) revert DefifaGovernor_DuplicateScorecard();
|
|
296
303
|
|
|
@@ -308,8 +315,11 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
308
315
|
scorecard.gracePeriodEnds = uint48(attestationsBegin + attestationGracePeriodOf(gameId));
|
|
309
316
|
|
|
310
317
|
// Store tier weights for BWA computation.
|
|
311
|
-
for (uint256 i; i < numberOfTierWeights;
|
|
318
|
+
for (uint256 i; i < numberOfTierWeights;) {
|
|
312
319
|
_scorecardTierWeightsOf[gameId][scorecardId][tierWeights[i].id - 1] = tierWeights[i].cashOutWeight;
|
|
320
|
+
unchecked {
|
|
321
|
+
++i;
|
|
322
|
+
}
|
|
313
323
|
}
|
|
314
324
|
|
|
315
325
|
// Snapshot each tier's pending reserves and minted attestation units at submission time.
|
|
@@ -317,20 +327,25 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
317
327
|
// after submission but before that checkpoint, clamp the live total back down to the minted
|
|
318
328
|
// units that existed at submission and only then add the snapshotted pending reserves.
|
|
319
329
|
{
|
|
320
|
-
|
|
321
|
-
uint256
|
|
330
|
+
// Cache the number of tiers to avoid re-reading from storage.
|
|
331
|
+
uint256 numberOfTiers = hookStore.maxTierIdOf(metadata.dataHook);
|
|
322
332
|
// slither-disable-next-line calls-loop
|
|
323
|
-
for (uint256 i; i <
|
|
333
|
+
for (uint256 i; i < numberOfTiers;) {
|
|
324
334
|
uint256 tierId = i + 1;
|
|
325
335
|
// slither-disable-next-line calls-loop
|
|
326
|
-
JB721Tier memory tier =
|
|
336
|
+
JB721Tier memory tier =
|
|
337
|
+
hookStore.tierOf({hook: metadata.dataHook, id: tierId, includeResolvedUri: false});
|
|
327
338
|
// slither-disable-next-line calls-loop
|
|
328
|
-
uint256 pendingReserves =
|
|
339
|
+
uint256 pendingReserves =
|
|
340
|
+
hookStore.numberOfPendingReservesFor({hook: metadata.dataHook, tierId: tierId});
|
|
329
341
|
// slither-disable-next-line calls-loop
|
|
330
342
|
uint256 submittedTierAttestationUnits =
|
|
331
343
|
IDefifaHook(metadata.dataHook).currentSupplyOfTier(tierId) * tier.votingUnits;
|
|
332
344
|
_pendingReservesSnapshotOf[gameId][scorecardId][tierId] = pendingReserves;
|
|
333
345
|
_submittedTierAttestationUnitsOf[gameId][scorecardId][tierId] = submittedTierAttestationUnits;
|
|
346
|
+
unchecked {
|
|
347
|
+
++i;
|
|
348
|
+
}
|
|
334
349
|
}
|
|
335
350
|
}
|
|
336
351
|
|
|
@@ -351,8 +366,11 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
351
366
|
// Find the largest tier weight.
|
|
352
367
|
uint256 totalCashOutWeight = IDefifaHook(metadata.dataHook).TOTAL_CASHOUT_WEIGHT();
|
|
353
368
|
uint256 maxWeight;
|
|
354
|
-
for (uint256 i; i < numberOfTierWeights;
|
|
369
|
+
for (uint256 i; i < numberOfTierWeights;) {
|
|
355
370
|
if (tierWeights[i].cashOutWeight > maxWeight) maxWeight = tierWeights[i].cashOutWeight;
|
|
371
|
+
unchecked {
|
|
372
|
+
++i;
|
|
373
|
+
}
|
|
356
374
|
}
|
|
357
375
|
|
|
358
376
|
// maxShare² in totalCashOutWeight scale (nonlinear: gentle for moderate, steep for extreme).
|
|
@@ -517,7 +535,7 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
517
535
|
// Get a reference to the number of tiers.
|
|
518
536
|
uint256 numberOfTiers = store.maxTierIdOf(metadata.dataHook);
|
|
519
537
|
|
|
520
|
-
for (uint256 i; i < numberOfTiers;
|
|
538
|
+
for (uint256 i; i < numberOfTiers;) {
|
|
521
539
|
// Tiers are 1-indexed.
|
|
522
540
|
uint256 tierId = i + 1;
|
|
523
541
|
|
|
@@ -537,7 +555,7 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
537
555
|
// pending reserves decrease by the same amount — so no one's voting power shifts.
|
|
538
556
|
{
|
|
539
557
|
// slither-disable-next-line calls-loop
|
|
540
|
-
uint256 pendingReserves = store.numberOfPendingReservesFor(metadata.dataHook, tierId);
|
|
558
|
+
uint256 pendingReserves = store.numberOfPendingReservesFor({hook: metadata.dataHook, tierId: tierId});
|
|
541
559
|
if (pendingReserves != 0) {
|
|
542
560
|
// slither-disable-next-line calls-loop
|
|
543
561
|
JB721Tier memory tier =
|
|
@@ -547,7 +565,7 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
547
565
|
}
|
|
548
566
|
|
|
549
567
|
// Scale the account's share of the tier to MAX_ATTESTATION_POWER_TIER.
|
|
550
|
-
// e.g. holding 3 of 10 tokens
|
|
568
|
+
// e.g. holding 3 of 10 tokens -> 3/10 * MAX_ATTESTATION_POWER_TIER attestation power from this tier.
|
|
551
569
|
unchecked {
|
|
552
570
|
if (tierAttestationUnitsForAccount != 0) {
|
|
553
571
|
attestationPower += mulDiv({
|
|
@@ -556,6 +574,7 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
556
574
|
denominator: tierTotalAttestationUnits
|
|
557
575
|
});
|
|
558
576
|
}
|
|
577
|
+
++i;
|
|
559
578
|
}
|
|
560
579
|
}
|
|
561
580
|
}
|
|
@@ -597,7 +616,7 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
597
616
|
// Cache the total cashout weight denominator from the hook.
|
|
598
617
|
uint256 totalCashOutWeight = hook.TOTAL_CASHOUT_WEIGHT();
|
|
599
618
|
|
|
600
|
-
for (uint256 i; i < numberOfTiers;
|
|
619
|
+
for (uint256 i; i < numberOfTiers;) {
|
|
601
620
|
// Tiers are 1-indexed.
|
|
602
621
|
uint256 tierId = i + 1;
|
|
603
622
|
|
|
@@ -614,10 +633,12 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
614
633
|
uint256 tierTotalAttestationUnits =
|
|
615
634
|
hook.getPastTierTotalAttestationUnitsOf({tier: tierId, timestamp: timestamp});
|
|
616
635
|
uint256 submittedTierAttestationUnits = _submittedTierAttestationUnitsOf[gameId][scorecardId][tierId];
|
|
636
|
+
// Clamp the total to the submitted snapshot to exclude post-submission reserve mints.
|
|
617
637
|
if (tierTotalAttestationUnits > submittedTierAttestationUnits) {
|
|
618
638
|
tierTotalAttestationUnits = submittedTierAttestationUnits;
|
|
619
639
|
}
|
|
620
640
|
|
|
641
|
+
// Add back the snapshotted pending reserves.
|
|
621
642
|
uint256 pendingReserves = _pendingReservesSnapshotOf[gameId][scorecardId][tierId];
|
|
622
643
|
if (pendingReserves != 0) {
|
|
623
644
|
// slither-disable-next-line calls-loop
|
|
@@ -627,14 +648,21 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
627
648
|
}
|
|
628
649
|
|
|
629
650
|
// Raw power for this tier.
|
|
630
|
-
uint256 rawPower =
|
|
631
|
-
|
|
651
|
+
uint256 rawPower = mulDiv({
|
|
652
|
+
x: MAX_ATTESTATION_POWER_TIER,
|
|
653
|
+
y: tierAttestationUnitsForAccount,
|
|
654
|
+
denominator: tierTotalAttestationUnits
|
|
655
|
+
});
|
|
632
656
|
|
|
633
657
|
// BWA reduction: power * (1 - tierWeight / totalCashOutWeight).
|
|
634
658
|
uint256 tierWeight = _scorecardTierWeightsOf[gameId][scorecardId][i];
|
|
635
659
|
uint256 bwaMultiplier = totalCashOutWeight - tierWeight;
|
|
636
660
|
|
|
637
|
-
bwaAttestationPower += mulDiv(rawPower, bwaMultiplier, totalCashOutWeight);
|
|
661
|
+
bwaAttestationPower += mulDiv({x: rawPower, y: bwaMultiplier, denominator: totalCashOutWeight});
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
unchecked {
|
|
665
|
+
++i;
|
|
638
666
|
}
|
|
639
667
|
}
|
|
640
668
|
}
|
|
@@ -663,7 +691,7 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
663
691
|
// Keep a reference to the total eligible tier weight.
|
|
664
692
|
uint256 eligibleTierWeights;
|
|
665
693
|
|
|
666
|
-
for (uint256 i; i < numberOfTiers;
|
|
694
|
+
for (uint256 i; i < numberOfTiers;) {
|
|
667
695
|
uint256 tierId = i + 1;
|
|
668
696
|
|
|
669
697
|
// A tier contributes to quorum if it has circulating tokens OR unminted pending reserves.
|
|
@@ -673,10 +701,14 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
673
701
|
// slither-disable-next-line calls-loop
|
|
674
702
|
uint256 currentTierSupply = hook.currentSupplyOfTier(tierId);
|
|
675
703
|
// slither-disable-next-line calls-loop
|
|
676
|
-
uint256 pendingReserves = store.numberOfPendingReservesFor(metadata.dataHook, tierId);
|
|
704
|
+
uint256 pendingReserves = store.numberOfPendingReservesFor({hook: metadata.dataHook, tierId: tierId});
|
|
677
705
|
if (currentTierSupply != 0 || pendingReserves != 0) {
|
|
678
706
|
eligibleTierWeights += MAX_ATTESTATION_POWER_TIER;
|
|
679
707
|
}
|
|
708
|
+
|
|
709
|
+
unchecked {
|
|
710
|
+
++i;
|
|
711
|
+
}
|
|
680
712
|
}
|
|
681
713
|
|
|
682
714
|
// Quorum = 50% of all participated tiers' attestation power.
|
|
@@ -724,8 +756,8 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
724
756
|
|
|
725
757
|
// If quorum has been reached (using the concentration-adjusted snapshot), check timelock.
|
|
726
758
|
if (scorecard.quorumSnapshot <= _scorecardAttestationsOf[gameId][scorecardId].count) {
|
|
727
|
-
uint256
|
|
728
|
-
if (
|
|
759
|
+
uint256 timelockDur = timelockDurationOf(gameId);
|
|
760
|
+
if (timelockDur > 0 && block.timestamp < uint256(scorecard.gracePeriodEnds) + timelockDur) {
|
|
729
761
|
return DefifaScorecardState.QUEUED;
|
|
730
762
|
}
|
|
731
763
|
return DefifaScorecardState.SUCCEEDED;
|
|
@@ -749,7 +781,7 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
749
781
|
// ----------------------- internal helpers -------------------------- //
|
|
750
782
|
//*********************************************************************//
|
|
751
783
|
|
|
752
|
-
/// @notice Build the normalized calldata.
|
|
784
|
+
/// @notice Build the normalized calldata for ratification.
|
|
753
785
|
/// @param tierWeights The weights of each tier in the scorecard data.
|
|
754
786
|
/// @return The calldata to send alongside the transactions.
|
|
755
787
|
function _buildScorecardCalldataFor(DefifaTierCashOutWeight[] calldata tierWeights)
|
|
@@ -757,7 +789,7 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
757
789
|
pure
|
|
758
790
|
returns (bytes memory)
|
|
759
791
|
{
|
|
760
|
-
// Build the calldata from the tier weights.
|
|
792
|
+
// Build the calldata from the tier weights using the hook's selector.
|
|
761
793
|
return abi.encodeWithSelector(DefifaHook.setTierCashOutWeightsTo.selector, (tierWeights));
|
|
762
794
|
}
|
|
763
795
|
|
package/src/DefifaHook.sol
CHANGED
|
@@ -279,9 +279,12 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
279
279
|
// Get the current game phase.
|
|
280
280
|
DefifaGamePhase gamePhase = gamePhaseReporter.currentGamePhaseOf(context.projectId);
|
|
281
281
|
|
|
282
|
+
// Cache the store reference in a local variable to avoid repeated SLOAD.
|
|
283
|
+
IJB721TiersHookStore hookStore = store;
|
|
284
|
+
|
|
282
285
|
// Calculate the amount paid to mint the tokens that are being burned.
|
|
283
286
|
uint256 cumulativeMintPrice = DefifaHookLib.computeCumulativeMintPrice({
|
|
284
|
-
tokenIds: decodedTokenIds, hookStore:
|
|
287
|
+
tokenIds: decodedTokenIds, hookStore: hookStore, hook: address(this)
|
|
285
288
|
});
|
|
286
289
|
|
|
287
290
|
// Use this contract as the only cash out hook.
|
|
@@ -315,6 +318,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
315
318
|
override
|
|
316
319
|
returns (uint256 cumulativeWeight)
|
|
317
320
|
{
|
|
321
|
+
// Cache the store in a local variable to avoid repeated SLOAD.
|
|
318
322
|
cumulativeWeight = DefifaHookLib.computeCashOutWeightBatch({
|
|
319
323
|
tokenIds: tokenIds,
|
|
320
324
|
hookStore: store,
|
|
@@ -339,6 +343,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
339
343
|
|
|
340
344
|
/// @notice The amount of tokens of a tier that are currently in circulation.
|
|
341
345
|
/// @param tierId The ID of the tier to get the current supply of.
|
|
346
|
+
/// @return The current supply count.
|
|
342
347
|
function currentSupplyOfTier(uint256 tierId) public view returns (uint256) {
|
|
343
348
|
return DefifaHookLib.computeCurrentSupply({hookStore: store, hook: address(this), tierId: tierId});
|
|
344
349
|
}
|
|
@@ -546,9 +551,12 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
546
551
|
(JBRulesetMetadataResolver.metadata(rulesets.currentOf(PROJECT_ID)))
|
|
547
552
|
)) revert DefifaHook_ReservedTokenMintingPaused();
|
|
548
553
|
|
|
554
|
+
// Cache the store reference in a local variable to avoid repeated SLOAD.
|
|
555
|
+
IJB721TiersHookStore hookStore = store;
|
|
556
|
+
|
|
549
557
|
// Keep a reference to the reserved token beneficiary.
|
|
550
558
|
// slither-disable-next-line calls-loop
|
|
551
|
-
address reservedTokenBeneficiary =
|
|
559
|
+
address reservedTokenBeneficiary = hookStore.reserveBeneficiaryOf({hook: address(this), tierId: tierId});
|
|
552
560
|
|
|
553
561
|
// Get a reference to the old delegate.
|
|
554
562
|
address oldDelegate = _tierDelegation[reservedTokenBeneficiary][tierId];
|
|
@@ -566,14 +574,14 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
566
574
|
|
|
567
575
|
// Record the minted reserves for the tier.
|
|
568
576
|
// slither-disable-next-line calls-loop
|
|
569
|
-
uint256[] memory tokenIds =
|
|
577
|
+
uint256[] memory tokenIds = hookStore.recordMintReservesFor({tierId: tierId, count: count});
|
|
570
578
|
|
|
571
579
|
// Keep a reference to the token ID being iterated on.
|
|
572
580
|
uint256 tokenId;
|
|
573
581
|
|
|
574
582
|
// Fetch the tier details (needed for votingUnits below).
|
|
575
583
|
// slither-disable-next-line calls-loop
|
|
576
|
-
JB721Tier memory tier =
|
|
584
|
+
JB721Tier memory tier = hookStore.tierOf({hook: address(this), id: tierId, includeResolvedUri: false});
|
|
577
585
|
|
|
578
586
|
// Increment _totalMintCost so reserved recipients can claim their share of fee tokens ($DEFIFA/$NANA).
|
|
579
587
|
// Note: reserved mints dilute existing fee token claimants because they increase the total mint cost
|
|
@@ -586,7 +594,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
586
594
|
// Set the token ID.
|
|
587
595
|
tokenId = tokenIds[i];
|
|
588
596
|
|
|
589
|
-
// Mint the token.
|
|
597
|
+
// Mint the token to the reserve beneficiary.
|
|
590
598
|
// slither-disable-next-line reentrancy-no-eth
|
|
591
599
|
_mint({to: reservedTokenBeneficiary, tokenId: tokenId});
|
|
592
600
|
|
|
@@ -647,26 +655,34 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
647
655
|
// Keep track of whether the cashOut is happening during the complete phase.
|
|
648
656
|
bool isComplete = gamePhaseReporter.currentGamePhaseOf(PROJECT_ID) == DefifaGamePhase.COMPLETE;
|
|
649
657
|
|
|
658
|
+
// Cache the store reference in a local variable to avoid repeated SLOAD in the loop.
|
|
659
|
+
IJB721TiersHookStore hookStore = store;
|
|
660
|
+
|
|
650
661
|
// Iterate through all tokens, burning them if the owner is correct.
|
|
651
|
-
for (uint256 i; i < numberOfTokenIds;
|
|
662
|
+
for (uint256 i; i < numberOfTokenIds;) {
|
|
652
663
|
// Set the token's ID.
|
|
653
664
|
tokenId = decodedTokenIds[i];
|
|
654
665
|
|
|
655
666
|
// Make sure the token's owner is correct.
|
|
656
667
|
address tokenOwner = _ownerOf(tokenId);
|
|
657
668
|
if (tokenOwner != context.holder) {
|
|
658
|
-
revert DefifaHook_Unauthorized(tokenId, tokenOwner, context.holder);
|
|
669
|
+
revert DefifaHook_Unauthorized({tokenId: tokenId, owner: tokenOwner, caller: context.holder});
|
|
659
670
|
}
|
|
660
671
|
|
|
661
672
|
// Burn the token.
|
|
662
673
|
_burn(tokenId);
|
|
663
674
|
|
|
675
|
+
// Track per-tier redemptions during the complete phase.
|
|
664
676
|
if (isComplete) {
|
|
665
677
|
unchecked {
|
|
666
678
|
// slither-disable-next-line reentrancy-no-eth,calls-loop
|
|
667
|
-
++tokensRedeemedFrom[
|
|
679
|
+
++tokensRedeemedFrom[hookStore.tierIdOfToken(tokenId)];
|
|
668
680
|
}
|
|
669
681
|
}
|
|
682
|
+
|
|
683
|
+
unchecked {
|
|
684
|
+
++i;
|
|
685
|
+
}
|
|
670
686
|
}
|
|
671
687
|
|
|
672
688
|
// Call the hook.
|
|
@@ -800,17 +816,16 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
800
816
|
/// cannot claim a disproportionate share before reserves are minted.
|
|
801
817
|
/// @return cost The total mint cost of pending reserves.
|
|
802
818
|
function _pendingReserveMintCost() internal view returns (uint256 cost) {
|
|
803
|
-
IJB721TiersHookStore
|
|
804
|
-
|
|
805
|
-
uint256 numberOfTiers = _store.maxTierIdOf(hook);
|
|
819
|
+
IJB721TiersHookStore hookStore = store;
|
|
820
|
+
uint256 numberOfTiers = hookStore.maxTierIdOf(address(this));
|
|
806
821
|
|
|
807
822
|
for (uint256 i; i < numberOfTiers;) {
|
|
808
823
|
uint256 tierId = i + 1;
|
|
809
824
|
// slither-disable-next-line calls-loop
|
|
810
|
-
uint256 pendingReserves =
|
|
825
|
+
uint256 pendingReserves = hookStore.numberOfPendingReservesFor({hook: address(this), tierId: tierId});
|
|
811
826
|
if (pendingReserves != 0) {
|
|
812
827
|
// slither-disable-next-line calls-loop
|
|
813
|
-
JB721Tier memory tier =
|
|
828
|
+
JB721Tier memory tier = hookStore.tierOf({hook: address(this), id: tierId, includeResolvedUri: false});
|
|
814
829
|
cost += pendingReserves * tier.price;
|
|
815
830
|
}
|
|
816
831
|
unchecked {
|
|
@@ -1090,10 +1105,16 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1090
1105
|
/// @notice Before transferring an NFT, register its first owner (if necessary).
|
|
1091
1106
|
/// @param to The address the NFT is being transferred to.
|
|
1092
1107
|
/// @param tokenId The token ID of the NFT being transferred.
|
|
1108
|
+
/// @param auth The address authorizing the transfer.
|
|
1109
|
+
/// @return from The address the token was transferred from.
|
|
1093
1110
|
function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address from) {
|
|
1111
|
+
// Cache the store reference in a local variable to avoid repeated SLOAD.
|
|
1112
|
+
IJB721TiersHookStore hookStore = store;
|
|
1113
|
+
|
|
1094
1114
|
// Get a reference to the tier.
|
|
1095
1115
|
// slither-disable-next-line calls-loop
|
|
1096
|
-
JB721Tier memory tier =
|
|
1116
|
+
JB721Tier memory tier =
|
|
1117
|
+
hookStore.tierOfTokenId({hook: address(this), tokenId: tokenId, includeResolvedUri: false});
|
|
1097
1118
|
|
|
1098
1119
|
// Record the transfers and keep a reference to where the token is coming from.
|
|
1099
1120
|
from = super._update(to, tokenId, auth);
|
|
@@ -1127,7 +1148,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1127
1148
|
|
|
1128
1149
|
// Record the transfer after local delegation state has been finalized.
|
|
1129
1150
|
// slither-disable-next-line calls-loop
|
|
1130
|
-
|
|
1151
|
+
hookStore.recordTransferForTier({tierId: tier.id, from: from, to: to});
|
|
1131
1152
|
|
|
1132
1153
|
return from;
|
|
1133
1154
|
}
|