@ballkidz/defifa 0.0.20 → 0.0.22
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 +7 -7
- package/src/DefifaDeployer.sol +18 -8
- package/src/DefifaGovernor.sol +61 -29
- package/src/DefifaHook.sol +41 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ballkidz/defifa",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.22",
|
|
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.
|
|
18
|
-
"@bananapus/permission-ids-v6": "^0.0.
|
|
19
|
-
"@croptop/core-v6": "^0.0.
|
|
16
|
+
"@bananapus/721-hook-v6": "^0.0.35",
|
|
17
|
+
"@bananapus/core-v6": "^0.0.34",
|
|
18
|
+
"@bananapus/permission-ids-v6": "^0.0.17",
|
|
19
|
+
"@croptop/core-v6": "^0.0.33",
|
|
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.32",
|
|
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
|
@@ -249,6 +249,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
249
249
|
/// @return cashOutTaxRate The cash out tax rate influencing the reclaim amount.
|
|
250
250
|
/// @return cashOutCount The amount of tokens that should be considered cashed out.
|
|
251
251
|
/// @return totalSupply The total amount of tokens that are considered to be existing.
|
|
252
|
+
/// @return effectiveSurplusValue The effective surplus value to use for the cash out.
|
|
252
253
|
/// @return hookSpecifications The amount and data to send to cash out hooks (this contract) instead of returning to
|
|
253
254
|
/// the beneficiary.
|
|
254
255
|
function beforeCashOutRecordedWith(JBBeforeCashOutRecordedContext calldata context)
|
|
@@ -260,6 +261,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
260
261
|
uint256 cashOutTaxRate,
|
|
261
262
|
uint256 cashOutCount,
|
|
262
263
|
uint256 totalSupply,
|
|
264
|
+
uint256 effectiveSurplusValue,
|
|
263
265
|
JBCashOutHookSpecification[] memory hookSpecifications
|
|
264
266
|
)
|
|
265
267
|
{
|
|
@@ -279,9 +281,12 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
279
281
|
// Get the current game phase.
|
|
280
282
|
DefifaGamePhase gamePhase = gamePhaseReporter.currentGamePhaseOf(context.projectId);
|
|
281
283
|
|
|
284
|
+
// Cache the store reference in a local variable to avoid repeated SLOAD.
|
|
285
|
+
IJB721TiersHookStore hookStore = store;
|
|
286
|
+
|
|
282
287
|
// Calculate the amount paid to mint the tokens that are being burned.
|
|
283
288
|
uint256 cumulativeMintPrice = DefifaHookLib.computeCumulativeMintPrice({
|
|
284
|
-
tokenIds: decodedTokenIds, hookStore:
|
|
289
|
+
tokenIds: decodedTokenIds, hookStore: hookStore, hook: address(this)
|
|
285
290
|
});
|
|
286
291
|
|
|
287
292
|
// Use this contract as the only cash out hook.
|
|
@@ -301,6 +306,9 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
301
306
|
// Use the surplus as the total supply.
|
|
302
307
|
totalSupply = context.surplus.value;
|
|
303
308
|
|
|
309
|
+
// Use the surplus as the effective surplus value.
|
|
310
|
+
effectiveSurplusValue = context.surplus.value;
|
|
311
|
+
|
|
304
312
|
// Use the cash out tax rate from the context.
|
|
305
313
|
cashOutTaxRate = context.cashOutTaxRate;
|
|
306
314
|
}
|
|
@@ -315,6 +323,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
315
323
|
override
|
|
316
324
|
returns (uint256 cumulativeWeight)
|
|
317
325
|
{
|
|
326
|
+
// Cache the store in a local variable to avoid repeated SLOAD.
|
|
318
327
|
cumulativeWeight = DefifaHookLib.computeCashOutWeightBatch({
|
|
319
328
|
tokenIds: tokenIds,
|
|
320
329
|
hookStore: store,
|
|
@@ -339,6 +348,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
339
348
|
|
|
340
349
|
/// @notice The amount of tokens of a tier that are currently in circulation.
|
|
341
350
|
/// @param tierId The ID of the tier to get the current supply of.
|
|
351
|
+
/// @return The current supply count.
|
|
342
352
|
function currentSupplyOfTier(uint256 tierId) public view returns (uint256) {
|
|
343
353
|
return DefifaHookLib.computeCurrentSupply({hookStore: store, hook: address(this), tierId: tierId});
|
|
344
354
|
}
|
|
@@ -546,9 +556,12 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
546
556
|
(JBRulesetMetadataResolver.metadata(rulesets.currentOf(PROJECT_ID)))
|
|
547
557
|
)) revert DefifaHook_ReservedTokenMintingPaused();
|
|
548
558
|
|
|
559
|
+
// Cache the store reference in a local variable to avoid repeated SLOAD.
|
|
560
|
+
IJB721TiersHookStore hookStore = store;
|
|
561
|
+
|
|
549
562
|
// Keep a reference to the reserved token beneficiary.
|
|
550
563
|
// slither-disable-next-line calls-loop
|
|
551
|
-
address reservedTokenBeneficiary =
|
|
564
|
+
address reservedTokenBeneficiary = hookStore.reserveBeneficiaryOf({hook: address(this), tierId: tierId});
|
|
552
565
|
|
|
553
566
|
// Get a reference to the old delegate.
|
|
554
567
|
address oldDelegate = _tierDelegation[reservedTokenBeneficiary][tierId];
|
|
@@ -566,14 +579,14 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
566
579
|
|
|
567
580
|
// Record the minted reserves for the tier.
|
|
568
581
|
// slither-disable-next-line calls-loop
|
|
569
|
-
uint256[] memory tokenIds =
|
|
582
|
+
uint256[] memory tokenIds = hookStore.recordMintReservesFor({tierId: tierId, count: count});
|
|
570
583
|
|
|
571
584
|
// Keep a reference to the token ID being iterated on.
|
|
572
585
|
uint256 tokenId;
|
|
573
586
|
|
|
574
587
|
// Fetch the tier details (needed for votingUnits below).
|
|
575
588
|
// slither-disable-next-line calls-loop
|
|
576
|
-
JB721Tier memory tier =
|
|
589
|
+
JB721Tier memory tier = hookStore.tierOf({hook: address(this), id: tierId, includeResolvedUri: false});
|
|
577
590
|
|
|
578
591
|
// Increment _totalMintCost so reserved recipients can claim their share of fee tokens ($DEFIFA/$NANA).
|
|
579
592
|
// Note: reserved mints dilute existing fee token claimants because they increase the total mint cost
|
|
@@ -586,7 +599,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
586
599
|
// Set the token ID.
|
|
587
600
|
tokenId = tokenIds[i];
|
|
588
601
|
|
|
589
|
-
// Mint the token.
|
|
602
|
+
// Mint the token to the reserve beneficiary.
|
|
590
603
|
// slither-disable-next-line reentrancy-no-eth
|
|
591
604
|
_mint({to: reservedTokenBeneficiary, tokenId: tokenId});
|
|
592
605
|
|
|
@@ -647,26 +660,34 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
647
660
|
// Keep track of whether the cashOut is happening during the complete phase.
|
|
648
661
|
bool isComplete = gamePhaseReporter.currentGamePhaseOf(PROJECT_ID) == DefifaGamePhase.COMPLETE;
|
|
649
662
|
|
|
663
|
+
// Cache the store reference in a local variable to avoid repeated SLOAD in the loop.
|
|
664
|
+
IJB721TiersHookStore hookStore = store;
|
|
665
|
+
|
|
650
666
|
// Iterate through all tokens, burning them if the owner is correct.
|
|
651
|
-
for (uint256 i; i < numberOfTokenIds;
|
|
667
|
+
for (uint256 i; i < numberOfTokenIds;) {
|
|
652
668
|
// Set the token's ID.
|
|
653
669
|
tokenId = decodedTokenIds[i];
|
|
654
670
|
|
|
655
671
|
// Make sure the token's owner is correct.
|
|
656
672
|
address tokenOwner = _ownerOf(tokenId);
|
|
657
673
|
if (tokenOwner != context.holder) {
|
|
658
|
-
revert DefifaHook_Unauthorized(tokenId, tokenOwner, context.holder);
|
|
674
|
+
revert DefifaHook_Unauthorized({tokenId: tokenId, owner: tokenOwner, caller: context.holder});
|
|
659
675
|
}
|
|
660
676
|
|
|
661
677
|
// Burn the token.
|
|
662
678
|
_burn(tokenId);
|
|
663
679
|
|
|
680
|
+
// Track per-tier redemptions during the complete phase.
|
|
664
681
|
if (isComplete) {
|
|
665
682
|
unchecked {
|
|
666
683
|
// slither-disable-next-line reentrancy-no-eth,calls-loop
|
|
667
|
-
++tokensRedeemedFrom[
|
|
684
|
+
++tokensRedeemedFrom[hookStore.tierIdOfToken(tokenId)];
|
|
668
685
|
}
|
|
669
686
|
}
|
|
687
|
+
|
|
688
|
+
unchecked {
|
|
689
|
+
++i;
|
|
690
|
+
}
|
|
670
691
|
}
|
|
671
692
|
|
|
672
693
|
// Call the hook.
|
|
@@ -800,17 +821,16 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
800
821
|
/// cannot claim a disproportionate share before reserves are minted.
|
|
801
822
|
/// @return cost The total mint cost of pending reserves.
|
|
802
823
|
function _pendingReserveMintCost() internal view returns (uint256 cost) {
|
|
803
|
-
IJB721TiersHookStore
|
|
804
|
-
|
|
805
|
-
uint256 numberOfTiers = _store.maxTierIdOf(hook);
|
|
824
|
+
IJB721TiersHookStore hookStore = store;
|
|
825
|
+
uint256 numberOfTiers = hookStore.maxTierIdOf(address(this));
|
|
806
826
|
|
|
807
827
|
for (uint256 i; i < numberOfTiers;) {
|
|
808
828
|
uint256 tierId = i + 1;
|
|
809
829
|
// slither-disable-next-line calls-loop
|
|
810
|
-
uint256 pendingReserves =
|
|
830
|
+
uint256 pendingReserves = hookStore.numberOfPendingReservesFor({hook: address(this), tierId: tierId});
|
|
811
831
|
if (pendingReserves != 0) {
|
|
812
832
|
// slither-disable-next-line calls-loop
|
|
813
|
-
JB721Tier memory tier =
|
|
833
|
+
JB721Tier memory tier = hookStore.tierOf({hook: address(this), id: tierId, includeResolvedUri: false});
|
|
814
834
|
cost += pendingReserves * tier.price;
|
|
815
835
|
}
|
|
816
836
|
unchecked {
|
|
@@ -1090,10 +1110,16 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1090
1110
|
/// @notice Before transferring an NFT, register its first owner (if necessary).
|
|
1091
1111
|
/// @param to The address the NFT is being transferred to.
|
|
1092
1112
|
/// @param tokenId The token ID of the NFT being transferred.
|
|
1113
|
+
/// @param auth The address authorizing the transfer.
|
|
1114
|
+
/// @return from The address the token was transferred from.
|
|
1093
1115
|
function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address from) {
|
|
1116
|
+
// Cache the store reference in a local variable to avoid repeated SLOAD.
|
|
1117
|
+
IJB721TiersHookStore hookStore = store;
|
|
1118
|
+
|
|
1094
1119
|
// Get a reference to the tier.
|
|
1095
1120
|
// slither-disable-next-line calls-loop
|
|
1096
|
-
JB721Tier memory tier =
|
|
1121
|
+
JB721Tier memory tier =
|
|
1122
|
+
hookStore.tierOfTokenId({hook: address(this), tokenId: tokenId, includeResolvedUri: false});
|
|
1097
1123
|
|
|
1098
1124
|
// Record the transfers and keep a reference to where the token is coming from.
|
|
1099
1125
|
from = super._update(to, tokenId, auth);
|
|
@@ -1127,7 +1153,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1127
1153
|
|
|
1128
1154
|
// Record the transfer after local delegation state has been finalized.
|
|
1129
1155
|
// slither-disable-next-line calls-loop
|
|
1130
|
-
|
|
1156
|
+
hookStore.recordTransferForTier({tierId: tier.id, from: from, to: to});
|
|
1131
1157
|
|
|
1132
1158
|
return from;
|
|
1133
1159
|
}
|