@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.
Files changed (47) hide show
  1. package/AUDIT_INSTRUCTIONS.md +2 -2
  2. package/CHANGE_LOG.md +60 -5
  3. package/CRYPTO_ECON.md +505 -270
  4. package/CRYPTO_ECON.pdf +0 -0
  5. package/CRYPTO_ECON.tex +438 -241
  6. package/RISKS.md +9 -1
  7. package/SKILLS.md +3 -2
  8. package/STYLE_GUIDE.md +2 -2
  9. package/foundry.toml +1 -1
  10. package/package.json +7 -7
  11. package/script/Deploy.s.sol +1 -1
  12. package/script/helpers/DefifaDeploymentLib.sol +1 -1
  13. package/src/DefifaDeployer.sol +129 -131
  14. package/src/DefifaGovernor.sol +279 -84
  15. package/src/DefifaHook.sol +159 -172
  16. package/src/DefifaProjectOwner.sol +1 -1
  17. package/src/DefifaTokenUriResolver.sol +1 -1
  18. package/src/enums/DefifaScorecardState.sol +1 -0
  19. package/src/interfaces/IDefifaGovernor.sol +41 -2
  20. package/src/libraries/DefifaFontImporter.sol +1 -1
  21. package/src/libraries/DefifaHookLib.sol +70 -63
  22. package/src/structs/DefifaAttestations.sol +3 -3
  23. package/src/structs/DefifaLaunchProjectData.sol +1 -0
  24. package/src/structs/DefifaScorecard.sol +2 -0
  25. package/test/BWAFunctionComparison.t.sol +1320 -0
  26. package/test/DefifaAdversarialQuorum.t.sol +53 -38
  27. package/test/DefifaAuditLowGuards.t.sol +10 -6
  28. package/test/DefifaFeeAccounting.t.sol +3 -2
  29. package/test/DefifaGovernanceHardening.t.sol +1311 -0
  30. package/test/DefifaGovernor.t.sol +5 -3
  31. package/test/DefifaHookRegressions.t.sol +3 -2
  32. package/test/DefifaMintCostInvariant.t.sol +3 -2
  33. package/test/DefifaNoContest.t.sol +4 -3
  34. package/test/DefifaSecurity.t.sol +55 -42
  35. package/test/DefifaUSDC.t.sol +4 -3
  36. package/test/Fork.t.sol +12 -13
  37. package/test/SVG.t.sol +1 -1
  38. package/test/TestAuditGaps.sol +7 -5
  39. package/test/TestQALastMile.t.sol +5 -3
  40. package/test/audit/{CodexAttestationDoubleCount.t.sol → AttestationDoubleCount.t.sol} +4 -3
  41. package/test/audit/FixPendingReserveDilution.t.sol +366 -0
  42. package/test/audit/PendingReserveDilution.t.sol +298 -0
  43. package/test/audit/PendingReserveQuorumGrief.t.sol +355 -0
  44. package/test/deployScript.t.sol +1 -1
  45. package/test/regression/AttestationDelegateBeneficiary.t.sol +3 -2
  46. package/test/regression/FulfillmentBlocksRatification.t.sol +3 -2
  47. package/test/regression/GracePeriodBypass.t.sol +3 -2
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: UNLICENSED
2
- pragma solidity ^0.8.26;
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: Two out of four attestors (50%) can reach quorum.
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 1 attest (50% of total power).
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), "50% attestation should reach quorum");
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
- // Only user 0 likes scorecard A (1/4, not quorum).
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 — see
404
- // DefifaGovernor.sol lines 203-207.
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 4 tiers, 1 user per tier ---
408
- _setupGame(4, 1 ether);
419
+ // --- Step 1: Setup 6 tiers, 1 user per tier ---
420
+ _setupGame(6, 1 ether);
409
421
 
410
- // Verify initial quorum: 4 minted tiers -> quorum = 2 * MAX_ATTESTATION_POWER_TIER.
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, (4 * maxTier) / 2, "initial quorum = 50% of 4 tiers");
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 2 and 3 refund ---
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 3 and 4 refund (burn their tokens).
423
- _cashOut(_users[2], 3, 1);
424
- _cashOut(_users[3], 4, 1);
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 3 and 4 now have zero supply.
427
- assertEq(_nft.currentSupplyOfTier(3), 0, "tier 3 supply = 0 after refund");
428
- assertEq(_nft.currentSupplyOfTier(4), 0, "tier 4 supply = 0 after refund");
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 2 minted tiers.
438
+ // Quorum should now reflect only 4 minted tiers.
431
439
  uint256 newQuorum = _gov.quorum(_gameId);
432
- assertEq(newQuorum, (2 * maxTier) / 2, "quorum drops to 50% of 2 remaining tiers");
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
- // Tiers 1 and 2 split the pot; tiers 3 and 4 get 0 (no supply).
440
- DefifaTierCashOutWeight[] memory sc = _buildScorecard(4);
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 / 2;
443
- sc[1].cashOutWeight = tw / 2;
444
- // sc[2].cashOutWeight = 0; (default)
445
- // sc[3].cashOutWeight = 0; (default)
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: Only user 0 attests (1 of 2 remaining tiers = 50%) ---
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
- // A single attestor provides MAX_ATTESTATION_POWER_TIER = the new quorum.
455
- // 1 * MAX >= (2 * MAX) / 2 -> quorum met.
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), "1 attestor reaches quorum after tiers 3+4 burned"
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 ^0.8.26;
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, attestationStartTime: block.timestamp, attestationGracePeriod: overflowGracePeriod
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 ^0.8.26;
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