@ballkidz/defifa 0.0.11 → 0.0.13
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/AUDIT_INSTRUCTIONS.md +2 -2
- package/CHANGE_LOG.md +60 -5
- package/CRYPTO_ECON.md +505 -270
- package/CRYPTO_ECON.pdf +0 -0
- package/CRYPTO_ECON.tex +438 -241
- package/RISKS.md +9 -1
- package/SKILLS.md +3 -2
- package/STYLE_GUIDE.md +2 -2
- package/foundry.toml +1 -1
- package/package.json +7 -7
- package/script/Deploy.s.sol +1 -1
- package/script/helpers/DefifaDeploymentLib.sol +1 -1
- package/src/DefifaDeployer.sol +129 -131
- package/src/DefifaGovernor.sol +279 -84
- package/src/DefifaHook.sol +159 -172
- package/src/DefifaProjectOwner.sol +1 -1
- package/src/DefifaTokenUriResolver.sol +1 -1
- package/src/enums/DefifaScorecardState.sol +1 -0
- package/src/interfaces/IDefifaGovernor.sol +41 -2
- package/src/libraries/DefifaFontImporter.sol +1 -1
- package/src/libraries/DefifaHookLib.sol +70 -63
- package/src/structs/DefifaAttestations.sol +3 -3
- package/src/structs/DefifaLaunchProjectData.sol +1 -0
- package/src/structs/DefifaScorecard.sol +2 -0
- package/test/BWAFunctionComparison.t.sol +1320 -0
- package/test/DefifaAdversarialQuorum.t.sol +53 -38
- package/test/DefifaAuditLowGuards.t.sol +10 -6
- package/test/DefifaFeeAccounting.t.sol +3 -2
- package/test/DefifaGovernanceHardening.t.sol +1311 -0
- package/test/DefifaGovernor.t.sol +5 -3
- package/test/DefifaHookRegressions.t.sol +3 -2
- package/test/DefifaMintCostInvariant.t.sol +3 -2
- package/test/DefifaNoContest.t.sol +4 -3
- package/test/DefifaSecurity.t.sol +55 -42
- package/test/DefifaUSDC.t.sol +4 -3
- package/test/Fork.t.sol +12 -13
- package/test/SVG.t.sol +1 -1
- package/test/TestAuditGaps.sol +7 -5
- package/test/TestQALastMile.t.sol +5 -3
- package/test/audit/{CodexAttestationDoubleCount.t.sol → AttestationDoubleCount.t.sol} +4 -3
- package/test/audit/FixPendingReserveDilution.t.sol +366 -0
- package/test/audit/PendingReserveDilution.t.sol +298 -0
- package/test/audit/PendingReserveQuorumGrief.t.sol +355 -0
- package/test/deployScript.t.sol +1 -1
- package/test/regression/AttestationDelegateBeneficiary.t.sol +3 -2
- package/test/regression/FulfillmentBlocksRatification.t.sol +3 -2
- package/test/regression/GracePeriodBypass.t.sol +3 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: UNLICENSED
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {TestBaseWorkflow} from "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
|
|
5
5
|
|
|
@@ -295,7 +295,12 @@ contract DefifaAdversarialQuorumTest is JBTest, TestBaseWorkflow {
|
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
// =========================================================================
|
|
298
|
-
// TEST 7:
|
|
298
|
+
// TEST 7: Three out of four attestors can reach the HHI-adjusted quorum.
|
|
299
|
+
// With BWA (Benefit-Weighted Attestation), each attestor's power is reduced
|
|
300
|
+
// by their tier's share of the scorecard. For an equal 4-tier scorecard:
|
|
301
|
+
// BWA power per user = MAX_ATTESTATION_POWER_TIER * 0.75 = 750_000_000
|
|
302
|
+
// HHI-adjusted quorum = baseQuorum * 1.125 = 2_250_000_000
|
|
303
|
+
// So 3 users (2_250_000_000) just meets quorum, but 2 users (1_500_000_000) does not.
|
|
299
304
|
// =========================================================================
|
|
300
305
|
function test_halfAttestorsCanReachQuorum() external {
|
|
301
306
|
_setupGame(4, 1 ether);
|
|
@@ -310,18 +315,20 @@ contract DefifaAdversarialQuorumTest is JBTest, TestBaseWorkflow {
|
|
|
310
315
|
uint256 proposalId = _gov.submitScorecardFor(_gameId, sc);
|
|
311
316
|
vm.warp(_tsReader.ts() + _gov.attestationStartTimeOf(_gameId) + 1);
|
|
312
317
|
|
|
313
|
-
// Users 0 and
|
|
318
|
+
// Users 0, 1, and 2 attest (75% of raw power, but BWA-adjusted to meet quorum).
|
|
314
319
|
vm.prank(_users[0]);
|
|
315
320
|
_gov.attestToScorecardFrom(_gameId, proposalId);
|
|
316
321
|
vm.prank(_users[1]);
|
|
317
322
|
_gov.attestToScorecardFrom(_gameId, proposalId);
|
|
323
|
+
vm.prank(_users[2]);
|
|
324
|
+
_gov.attestToScorecardFrom(_gameId, proposalId);
|
|
318
325
|
|
|
319
326
|
// After grace period.
|
|
320
327
|
vm.warp(_tsReader.ts() + _gov.attestationGracePeriodOf(_gameId) + 1);
|
|
321
328
|
|
|
322
329
|
// The proposal should be SUCCEEDED.
|
|
323
330
|
DefifaScorecardState state = _gov.stateOf(_gameId, proposalId);
|
|
324
|
-
assertEq(uint256(state), uint256(DefifaScorecardState.SUCCEEDED), "
|
|
331
|
+
assertEq(uint256(state), uint256(DefifaScorecardState.SUCCEEDED), "3/4 attestation should reach quorum");
|
|
325
332
|
|
|
326
333
|
// Ratification should succeed.
|
|
327
334
|
_gov.ratifyScorecardFrom(_gameId, sc);
|
|
@@ -353,8 +360,9 @@ contract DefifaAdversarialQuorumTest is JBTest, TestBaseWorkflow {
|
|
|
353
360
|
// Wait for attestation.
|
|
354
361
|
vm.warp(_tsReader.ts() + _gov.attestationStartTimeOf(_gameId) + 1);
|
|
355
362
|
|
|
356
|
-
//
|
|
363
|
+
// User 0 (tier 1, 100% beneficiary of scorecard A) has BWA power = 0 and cannot attest.
|
|
357
364
|
vm.prank(_users[0]);
|
|
365
|
+
vm.expectRevert(DefifaGovernor.DefifaGovernor_NotAllowed.selector);
|
|
358
366
|
_gov.attestToScorecardFrom(_gameId, proposalA);
|
|
359
367
|
|
|
360
368
|
// Users 1, 2, 3 like scorecard B (3/4, quorum met).
|
|
@@ -400,66 +408,72 @@ contract DefifaAdversarialQuorumTest is JBTest, TestBaseWorkflow {
|
|
|
400
408
|
// This proves the game handles burn-to-lower-quorum gracefully.
|
|
401
409
|
// The quorum() function uses live supply (currentSupplyOfTier) rather
|
|
402
410
|
// than a snapshot, so when tokens are burned the quorum threshold
|
|
403
|
-
// decreases. This is documented and accepted behavior
|
|
404
|
-
//
|
|
411
|
+
// decreases. This is documented and accepted behavior.
|
|
412
|
+
//
|
|
413
|
+
// With BWA + HHI-adjusted quorum, a minimum of 4 remaining tiers is needed
|
|
414
|
+
// for a balanced scorecard to reach quorum (since total BWA power for n tiers
|
|
415
|
+
// is MAX*(n-1) and the adjusted quorum for n=2 always exceeds that).
|
|
416
|
+
// We use 6 tiers, burn 2, leaving 4 tiers where 3/4 attestors suffice.
|
|
405
417
|
// =========================================================================
|
|
406
418
|
function test_burnTiersLowersQuorumAllowsRatification() external {
|
|
407
|
-
// --- Step 1: Setup
|
|
408
|
-
_setupGame(
|
|
419
|
+
// --- Step 1: Setup 6 tiers, 1 user per tier ---
|
|
420
|
+
_setupGame(6, 1 ether);
|
|
409
421
|
|
|
410
|
-
// Verify initial quorum:
|
|
422
|
+
// Verify initial quorum: 6 minted tiers -> quorum = 3 * MAX_ATTESTATION_POWER_TIER.
|
|
411
423
|
uint256 initialQuorum = _gov.quorum(_gameId);
|
|
412
424
|
uint256 maxTier = _gov.MAX_ATTESTATION_POWER_TIER();
|
|
413
|
-
assertEq(initialQuorum, (
|
|
414
|
-
|
|
415
|
-
// With 4 tiers, a single attestor (25%) cannot reach quorum.
|
|
416
|
-
// We will demonstrate that after 2 tiers are burned, 1 attestor CAN reach quorum.
|
|
425
|
+
assertEq(initialQuorum, (6 * maxTier) / 2, "initial quorum = 50% of 6 tiers");
|
|
417
426
|
|
|
418
|
-
// --- Step 2: Warp to REFUND phase, users
|
|
419
|
-
// _setupGame leaves us in MINT phase; advance to REFUND.
|
|
427
|
+
// --- Step 2: Warp to REFUND phase, users 4 and 5 refund ---
|
|
420
428
|
_toRefund();
|
|
421
429
|
|
|
422
|
-
// Users in tiers
|
|
423
|
-
_cashOut(_users[
|
|
424
|
-
_cashOut(_users[
|
|
430
|
+
// Users in tiers 5 and 6 refund (burn their tokens).
|
|
431
|
+
_cashOut(_users[4], 5, 1);
|
|
432
|
+
_cashOut(_users[5], 6, 1);
|
|
425
433
|
|
|
426
|
-
// Verify tiers
|
|
427
|
-
assertEq(_nft.currentSupplyOfTier(
|
|
428
|
-
assertEq(_nft.currentSupplyOfTier(
|
|
434
|
+
// Verify tiers 5 and 6 now have zero supply.
|
|
435
|
+
assertEq(_nft.currentSupplyOfTier(5), 0, "tier 5 supply = 0 after refund");
|
|
436
|
+
assertEq(_nft.currentSupplyOfTier(6), 0, "tier 6 supply = 0 after refund");
|
|
429
437
|
|
|
430
|
-
// Quorum should now reflect only
|
|
438
|
+
// Quorum should now reflect only 4 minted tiers.
|
|
431
439
|
uint256 newQuorum = _gov.quorum(_gameId);
|
|
432
|
-
assertEq(newQuorum, (
|
|
440
|
+
assertEq(newQuorum, (4 * maxTier) / 2, "quorum drops to 50% of 4 remaining tiers");
|
|
433
441
|
assertLt(newQuorum, initialQuorum, "new quorum < initial quorum");
|
|
434
442
|
|
|
435
443
|
// --- Step 3: Advance to SCORING phase ---
|
|
436
444
|
_toScoring();
|
|
437
445
|
|
|
438
446
|
// --- Step 4: Submit scorecard ---
|
|
439
|
-
//
|
|
440
|
-
DefifaTierCashOutWeight[] memory sc = _buildScorecard(
|
|
447
|
+
// Equal split across remaining tiers 1-4; burned tiers 5+6 get 0.
|
|
448
|
+
DefifaTierCashOutWeight[] memory sc = _buildScorecard(6);
|
|
441
449
|
uint256 tw = _nft.TOTAL_CASHOUT_WEIGHT();
|
|
442
|
-
sc[0].cashOutWeight = tw /
|
|
443
|
-
sc[1].cashOutWeight = tw /
|
|
444
|
-
|
|
445
|
-
|
|
450
|
+
sc[0].cashOutWeight = tw / 4;
|
|
451
|
+
sc[1].cashOutWeight = tw / 4;
|
|
452
|
+
sc[2].cashOutWeight = tw / 4;
|
|
453
|
+
sc[3].cashOutWeight = tw / 4;
|
|
454
|
+
// sc[4].cashOutWeight = 0; (default, tier 5 burned)
|
|
455
|
+
// sc[5].cashOutWeight = 0; (default, tier 6 burned)
|
|
446
456
|
uint256 proposalId = _gov.submitScorecardFor(_gameId, sc);
|
|
447
457
|
|
|
448
|
-
// --- Step 5:
|
|
458
|
+
// --- Step 5: Users 0, 1, and 2 attest (3 of 4 remaining tiers) ---
|
|
459
|
+
// BWA power per user (25% tier weight): 1e9 * 0.75 = 750_000_000.
|
|
460
|
+
// HHI-adjusted quorum for equal 4-tier scorecard = 2e9 * 1.125 = 2_250_000_000.
|
|
461
|
+
// 3 users * 750M = 2_250_000_000, meeting the adjusted quorum exactly.
|
|
449
462
|
vm.warp(_tsReader.ts() + _gov.attestationStartTimeOf(_gameId) + 1);
|
|
450
463
|
|
|
451
464
|
vm.prank(_users[0]);
|
|
452
465
|
_gov.attestToScorecardFrom(_gameId, proposalId);
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
466
|
+
vm.prank(_users[1]);
|
|
467
|
+
_gov.attestToScorecardFrom(_gameId, proposalId);
|
|
468
|
+
vm.prank(_users[2]);
|
|
469
|
+
_gov.attestToScorecardFrom(_gameId, proposalId);
|
|
456
470
|
|
|
457
471
|
// --- Step 6: After grace period, proposal should be SUCCEEDED ---
|
|
458
472
|
vm.warp(_tsReader.ts() + _gov.attestationGracePeriodOf(_gameId) + 1);
|
|
459
473
|
|
|
460
474
|
DefifaScorecardState state = _gov.stateOf(_gameId, proposalId);
|
|
461
475
|
assertEq(
|
|
462
|
-
uint256(state), uint256(DefifaScorecardState.SUCCEEDED), "
|
|
476
|
+
uint256(state), uint256(DefifaScorecardState.SUCCEEDED), "3 attestors reach quorum after tiers 5+6 burned"
|
|
463
477
|
);
|
|
464
478
|
|
|
465
479
|
// --- Step 7: Ratification should succeed ---
|
|
@@ -467,10 +481,10 @@ contract DefifaAdversarialQuorumTest is JBTest, TestBaseWorkflow {
|
|
|
467
481
|
assertTrue(_nft.cashOutWeightIsSet(), "scorecard ratified - weights are set");
|
|
468
482
|
|
|
469
483
|
// --- Step 8: Verify game resilience ---
|
|
470
|
-
// Tiers 1 and 2 still have supply, meaning their holders can cash out
|
|
471
|
-
// once commitments are fulfilled. The game is in a healthy state.
|
|
472
484
|
assertEq(_nft.currentSupplyOfTier(1), 1, "tier 1 supply intact");
|
|
473
485
|
assertEq(_nft.currentSupplyOfTier(2), 1, "tier 2 supply intact");
|
|
486
|
+
assertEq(_nft.currentSupplyOfTier(3), 1, "tier 3 supply intact");
|
|
487
|
+
assertEq(_nft.currentSupplyOfTier(4), 1, "tier 4 supply intact");
|
|
474
488
|
}
|
|
475
489
|
|
|
476
490
|
// =========================================================================
|
|
@@ -525,7 +539,8 @@ contract DefifaAdversarialQuorumTest is JBTest, TestBaseWorkflow {
|
|
|
525
539
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
526
540
|
terminal: jbMultiTerminal(),
|
|
527
541
|
minParticipation: 0,
|
|
528
|
-
scorecardTimeout: 0
|
|
542
|
+
scorecardTimeout: 0,
|
|
543
|
+
timelockDuration: 0
|
|
529
544
|
});
|
|
530
545
|
}
|
|
531
546
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: UNLICENSED
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {TestBaseWorkflow} from "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
|
|
5
5
|
|
|
@@ -129,13 +129,13 @@ contract DefifaAuditLowGuardsTest is JBTest, TestBaseWorkflow {
|
|
|
129
129
|
// First initialization should succeed.
|
|
130
130
|
uint256 gameId = 42;
|
|
131
131
|
_standaloneGov.initializeGame({
|
|
132
|
-
gameId: gameId, attestationStartTime: block.timestamp, attestationGracePeriod: 2 days
|
|
132
|
+
gameId: gameId, attestationStartTime: block.timestamp, attestationGracePeriod: 2 days, timelockDuration: 0
|
|
133
133
|
});
|
|
134
134
|
|
|
135
135
|
// Second initialization for the same gameId should revert.
|
|
136
136
|
vm.expectRevert(DefifaGovernor.DefifaGovernor_AlreadyInitialized.selector);
|
|
137
137
|
_standaloneGov.initializeGame({
|
|
138
|
-
gameId: gameId, attestationStartTime: block.timestamp, attestationGracePeriod: 2 days
|
|
138
|
+
gameId: gameId, attestationStartTime: block.timestamp, attestationGracePeriod: 2 days, timelockDuration: 0
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -163,7 +163,7 @@ contract DefifaAuditLowGuardsTest is JBTest, TestBaseWorkflow {
|
|
|
163
163
|
|
|
164
164
|
vm.expectRevert(DefifaGovernor.DefifaGovernor_Uint48Overflow.selector);
|
|
165
165
|
_standaloneGov.initializeGame({
|
|
166
|
-
gameId: 99, attestationStartTime: overflowStartTime, attestationGracePeriod: 2 days
|
|
166
|
+
gameId: 99, attestationStartTime: overflowStartTime, attestationGracePeriod: 2 days, timelockDuration: 0
|
|
167
167
|
});
|
|
168
168
|
}
|
|
169
169
|
|
|
@@ -178,7 +178,10 @@ contract DefifaAuditLowGuardsTest is JBTest, TestBaseWorkflow {
|
|
|
178
178
|
|
|
179
179
|
vm.expectRevert(DefifaGovernor.DefifaGovernor_Uint48Overflow.selector);
|
|
180
180
|
_standaloneGov.initializeGame({
|
|
181
|
-
gameId: 100,
|
|
181
|
+
gameId: 100,
|
|
182
|
+
attestationStartTime: block.timestamp,
|
|
183
|
+
attestationGracePeriod: overflowGracePeriod,
|
|
184
|
+
timelockDuration: 0
|
|
182
185
|
});
|
|
183
186
|
}
|
|
184
187
|
|
|
@@ -271,7 +274,8 @@ contract DefifaAuditLowGuardsTest is JBTest, TestBaseWorkflow {
|
|
|
271
274
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
272
275
|
terminal: jbMultiTerminal(),
|
|
273
276
|
minParticipation: 0,
|
|
274
|
-
scorecardTimeout: 0
|
|
277
|
+
scorecardTimeout: 0,
|
|
278
|
+
timelockDuration: 0
|
|
275
279
|
});
|
|
276
280
|
}
|
|
277
281
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: UNLICENSED
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {DefifaGovernor} from "../src/DefifaGovernor.sol";
|
|
5
5
|
import {DefifaDeployer} from "../src/DefifaDeployer.sol";
|
|
@@ -463,7 +463,8 @@ contract DefifaFeeAccountingTest is JBTest, TestBaseWorkflow {
|
|
|
463
463
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
464
464
|
terminal: jbMultiTerminal(),
|
|
465
465
|
minParticipation: 0,
|
|
466
|
-
scorecardTimeout: 0
|
|
466
|
+
scorecardTimeout: 0,
|
|
467
|
+
timelockDuration: 0
|
|
467
468
|
});
|
|
468
469
|
}
|
|
469
470
|
|