@ballkidz/defifa 0.0.12 → 0.0.14
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/ARCHITECTURE.md +3 -2
- package/AUDIT_INSTRUCTIONS.md +5 -5
- package/CHANGE_LOG.md +62 -5
- package/CRYPTO_ECON.md +506 -271
- package/CRYPTO_ECON.pdf +0 -0
- package/CRYPTO_ECON.tex +438 -241
- package/RISKS.md +13 -1
- package/SKILLS.md +5 -3
- package/USER_JOURNEYS.md +4 -3
- package/package.json +6 -6
- package/src/DefifaDeployer.sol +128 -130
- package/src/DefifaGovernor.sol +304 -83
- package/src/DefifaHook.sol +184 -171
- package/src/enums/DefifaScorecardState.sol +1 -0
- package/src/interfaces/IDefifaGovernor.sol +42 -2
- package/src/libraries/DefifaHookLib.sol +69 -62
- 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 +52 -37
- package/test/DefifaAuditLowGuards.t.sol +9 -5
- package/test/DefifaFeeAccounting.t.sol +2 -1
- package/test/DefifaGovernanceHardening.t.sol +1315 -0
- package/test/DefifaGovernor.t.sol +8 -4
- package/test/DefifaHookRegressions.t.sol +2 -1
- package/test/DefifaMintCostInvariant.t.sol +2 -1
- package/test/DefifaNoContest.t.sol +3 -2
- package/test/DefifaSecurity.t.sol +55 -47
- package/test/DefifaUSDC.t.sol +3 -2
- package/test/Fork.t.sol +37 -32
- package/test/TestAuditGaps.sol +6 -4
- package/test/TestQALastMile.t.sol +6 -3
- package/test/audit/{CodexAttestationDoubleCount.t.sol → AttestationDoubleCount.t.sol} +3 -2
- 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/audit/PendingReserveSnapshotBypass.t.sol +279 -0
- package/test/regression/AttestationDelegateBeneficiary.t.sol +2 -1
- package/test/regression/FulfillmentBlocksRatification.t.sol +2 -1
- package/test/regression/GracePeriodBypass.t.sol +2 -1
- package/test/SVG.t.sol +0 -164
- package/test/deployScript.t.sol +0 -144
|
@@ -531,9 +531,10 @@ contract DefifaGovernorTest is JBTest, TestBaseWorkflow {
|
|
|
531
531
|
// 0 = Against
|
|
532
532
|
// 1 = For
|
|
533
533
|
// 2 = Abstain
|
|
534
|
+
// BWA may reduce beneficiaries' power to zero; skip those gracefully.
|
|
534
535
|
for (uint256 i = 0; i < _users.length; i++) {
|
|
535
536
|
vm.prank(_users[i]);
|
|
536
|
-
_governor.attestToScorecardFrom(_gameId, _proposalId)
|
|
537
|
+
try _governor.attestToScorecardFrom(_gameId, _proposalId) {} catch {}
|
|
537
538
|
}
|
|
538
539
|
// each block is of 12 secs
|
|
539
540
|
vm.warp(block.timestamp + _governor.attestationGracePeriodOf(_gameId));
|
|
@@ -871,9 +872,10 @@ contract DefifaGovernorTest is JBTest, TestBaseWorkflow {
|
|
|
871
872
|
// 0 = Against
|
|
872
873
|
// 1 = For
|
|
873
874
|
// 2 = Abstain
|
|
875
|
+
// BWA may reduce beneficiaries' power to zero; skip those gracefully.
|
|
874
876
|
for (uint256 i = 0; i < _users.length; i++) {
|
|
875
877
|
vm.prank(_users[i]);
|
|
876
|
-
_governor.attestToScorecardFrom(_gameId, _proposalId)
|
|
878
|
+
try _governor.attestToScorecardFrom(_gameId, _proposalId) {} catch {}
|
|
877
879
|
}
|
|
878
880
|
}
|
|
879
881
|
|
|
@@ -977,7 +979,8 @@ contract DefifaGovernorTest is JBTest, TestBaseWorkflow {
|
|
|
977
979
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
978
980
|
terminal: jbMultiTerminal(),
|
|
979
981
|
minParticipation: 0,
|
|
980
|
-
scorecardTimeout: 0
|
|
982
|
+
scorecardTimeout: 0,
|
|
983
|
+
timelockDuration: 0
|
|
981
984
|
});
|
|
982
985
|
(uint256 _projectId, DefifaHook _nft,) = createDefifaProject(_launchData);
|
|
983
986
|
// Wait until the phase 1 start
|
|
@@ -1236,7 +1239,8 @@ contract DefifaGovernorTest is JBTest, TestBaseWorkflow {
|
|
|
1236
1239
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
1237
1240
|
terminal: jbMultiTerminal(),
|
|
1238
1241
|
minParticipation: 0,
|
|
1239
|
-
scorecardTimeout: 0
|
|
1242
|
+
scorecardTimeout: 0,
|
|
1243
|
+
timelockDuration: 0
|
|
1240
1244
|
});
|
|
1241
1245
|
}
|
|
1242
1246
|
|
|
@@ -383,7 +383,8 @@ contract DefifaHookRegressions is JBTest, TestBaseWorkflow {
|
|
|
383
383
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
384
384
|
terminal: jbMultiTerminal(),
|
|
385
385
|
minParticipation: 0,
|
|
386
|
-
scorecardTimeout: 0
|
|
386
|
+
scorecardTimeout: 0,
|
|
387
|
+
timelockDuration: 0
|
|
387
388
|
});
|
|
388
389
|
}
|
|
389
390
|
|
|
@@ -270,7 +270,8 @@ contract DefifaMintCostInvariantTest is JBTest, TestBaseWorkflow {
|
|
|
270
270
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
271
271
|
terminal: jbMultiTerminal(),
|
|
272
272
|
minParticipation: 0,
|
|
273
|
-
scorecardTimeout: 0
|
|
273
|
+
scorecardTimeout: 0,
|
|
274
|
+
timelockDuration: 0
|
|
274
275
|
});
|
|
275
276
|
|
|
276
277
|
uint256 pid = deployer.launchGameWith(d);
|
|
@@ -826,7 +826,8 @@ contract DefifaNoContestTest is JBTest, TestBaseWorkflow {
|
|
|
826
826
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
827
827
|
terminal: jbMultiTerminal(),
|
|
828
828
|
minParticipation: minParticipation,
|
|
829
|
-
scorecardTimeout: scorecardTimeout
|
|
829
|
+
scorecardTimeout: scorecardTimeout,
|
|
830
|
+
timelockDuration: 0
|
|
830
831
|
});
|
|
831
832
|
}
|
|
832
833
|
|
|
@@ -886,7 +887,7 @@ contract DefifaNoContestTest is JBTest, TestBaseWorkflow {
|
|
|
886
887
|
vm.warp(block.timestamp + _gov.attestationStartTimeOf(_gameId) + 1);
|
|
887
888
|
for (uint256 i; i < _users.length; i++) {
|
|
888
889
|
vm.prank(_users[i]);
|
|
889
|
-
_gov.attestToScorecardFrom(_gameId, pid)
|
|
890
|
+
try _gov.attestToScorecardFrom(_gameId, pid) {} catch {}
|
|
890
891
|
}
|
|
891
892
|
vm.warp(block.timestamp + _gov.attestationGracePeriodOf(_gameId) + 1);
|
|
892
893
|
}
|
|
@@ -219,20 +219,25 @@ contract DefifaSecurityTest is JBTest, TestBaseWorkflow {
|
|
|
219
219
|
|
|
220
220
|
// =========================================================================
|
|
221
221
|
// ROUNDING: extreme weights at 1000 ETH per tier
|
|
222
|
+
// With BWA + HHI, highly concentrated scorecards on fewer than 4 tiers
|
|
223
|
+
// cannot reach quorum (total BWA = MAX*(n-1) < adjusted quorum for HHI~1).
|
|
224
|
+
// Using 5 tiers ensures total BWA (4*MAX) exceeds the adjusted quorum.
|
|
222
225
|
// =========================================================================
|
|
223
226
|
function testRounding_extremeWeights() external {
|
|
224
|
-
_setupGame(
|
|
227
|
+
_setupGame(5, 1000 ether);
|
|
225
228
|
_toScoring();
|
|
226
229
|
|
|
227
|
-
DefifaTierCashOutWeight[] memory sc = _buildScorecard(
|
|
230
|
+
DefifaTierCashOutWeight[] memory sc = _buildScorecard(5);
|
|
228
231
|
sc[0].cashOutWeight = 1;
|
|
229
|
-
sc[1].cashOutWeight = _nft.TOTAL_CASHOUT_WEIGHT() -
|
|
232
|
+
sc[1].cashOutWeight = _nft.TOTAL_CASHOUT_WEIGHT() - 4;
|
|
230
233
|
sc[2].cashOutWeight = 1;
|
|
234
|
+
sc[3].cashOutWeight = 1;
|
|
235
|
+
sc[4].cashOutWeight = 1;
|
|
231
236
|
|
|
232
237
|
_attestAndRatify(sc);
|
|
233
238
|
uint256 pot = _surplus();
|
|
234
239
|
uint256 out = _cashOutAllUsers();
|
|
235
|
-
assertApproxEqAbs(out + _surplus(), pot,
|
|
240
|
+
assertApproxEqAbs(out + _surplus(), pot, 5, "fund conservation");
|
|
236
241
|
assertGt(_users[1].balance, pot * 99 / 100, "tier 2 > 99%");
|
|
237
242
|
}
|
|
238
243
|
|
|
@@ -287,7 +292,9 @@ contract DefifaSecurityTest is JBTest, TestBaseWorkflow {
|
|
|
287
292
|
// FUZZ: fund conservation across varying tier/player counts
|
|
288
293
|
// =========================================================================
|
|
289
294
|
function testFuzz_fundConservation(uint8 rawTiers, uint8 rawPlayers) external {
|
|
290
|
-
|
|
295
|
+
// Minimum 3 tiers: with BWA + HHI-adjusted quorum, 2-tier games with equal scorecards
|
|
296
|
+
// can never reach quorum (total BWA = MAX*(n-1) < adjusted quorum when n < 3).
|
|
297
|
+
uint8 nTiers = uint8(bound(rawTiers, 3, 12));
|
|
291
298
|
uint8 nPpt = uint8(bound(rawPlayers, 1, 3));
|
|
292
299
|
|
|
293
300
|
_setupMultiN(nTiers, nPpt, 1 ether);
|
|
@@ -342,13 +349,17 @@ contract DefifaSecurityTest is JBTest, TestBaseWorkflow {
|
|
|
342
349
|
|
|
343
350
|
// =========================================================================
|
|
344
351
|
// C-D3: reserved minters get proportional fee tokens ($DEFIFA/$NANA)
|
|
352
|
+
// With BWA + HHI, 2-tier games cannot reach quorum (total BWA power for
|
|
353
|
+
// n tiers = MAX*(n-1) which is always less than HHI-adjusted quorum for n=2).
|
|
354
|
+
// We use 4 tiers with equal weight, all having reserveRate=1. This ensures
|
|
355
|
+
// enough attestation power from all participants to meet the adjusted quorum.
|
|
345
356
|
// =========================================================================
|
|
346
357
|
function testC_D3_reservedMintersGetFeeTokens() external {
|
|
347
|
-
// Setup:
|
|
358
|
+
// Setup: 4 tiers, reservedRate=1, reserveBeneficiary = _reserveAddr
|
|
348
359
|
address _reserveAddr = address(bytes20(keccak256("reserveBeneficiary")));
|
|
349
360
|
|
|
350
|
-
DefifaTierParams[] memory tp = new DefifaTierParams[](
|
|
351
|
-
for (uint256 i; i <
|
|
361
|
+
DefifaTierParams[] memory tp = new DefifaTierParams[](4);
|
|
362
|
+
for (uint256 i; i < 4; i++) {
|
|
352
363
|
tp[i] = DefifaTierParams({
|
|
353
364
|
reservedRate: 1, // 1 reserve per 1 paid mint
|
|
354
365
|
reservedTokenBeneficiary: _reserveAddr,
|
|
@@ -376,33 +387,34 @@ contract DefifaSecurityTest is JBTest, TestBaseWorkflow {
|
|
|
376
387
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
377
388
|
terminal: jbMultiTerminal(),
|
|
378
389
|
minParticipation: 0,
|
|
379
|
-
scorecardTimeout: 0
|
|
390
|
+
scorecardTimeout: 0,
|
|
391
|
+
timelockDuration: 0
|
|
380
392
|
});
|
|
381
393
|
(_pid, _nft, _gov) = _launch(d);
|
|
382
394
|
vm.warp(d.start - d.mintPeriodDuration - d.refundPeriodDuration);
|
|
383
395
|
|
|
384
|
-
// Paid mints:
|
|
385
|
-
_users = new address[](
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
_delegateSelf(_users[1], 2);
|
|
393
|
-
vm.warp(_tsReader.timestamp() + 1);
|
|
396
|
+
// Paid mints: 1 user per tier
|
|
397
|
+
_users = new address[](4);
|
|
398
|
+
for (uint256 i; i < 4; i++) {
|
|
399
|
+
_users[i] = _addr(i);
|
|
400
|
+
_mint(_users[i], i + 1, 1 ether);
|
|
401
|
+
_delegateSelf(_users[i], i + 1);
|
|
402
|
+
vm.warp(_tsReader.timestamp() + 1);
|
|
403
|
+
}
|
|
394
404
|
|
|
395
405
|
// Move to scoring phase (reserves can only be minted here)
|
|
396
406
|
_toScoring();
|
|
397
407
|
|
|
398
408
|
// Mint reserved tokens (1 per tier since reserveFrequency=1)
|
|
399
|
-
JB721TiersMintReservesConfig[] memory reserveConfigs = new JB721TiersMintReservesConfig[](
|
|
409
|
+
JB721TiersMintReservesConfig[] memory reserveConfigs = new JB721TiersMintReservesConfig[](4);
|
|
400
410
|
reserveConfigs[0] = JB721TiersMintReservesConfig({tierId: 1, count: 1});
|
|
401
411
|
reserveConfigs[1] = JB721TiersMintReservesConfig({tierId: 2, count: 1});
|
|
412
|
+
reserveConfigs[2] = JB721TiersMintReservesConfig({tierId: 3, count: 1});
|
|
413
|
+
reserveConfigs[3] = JB721TiersMintReservesConfig({tierId: 4, count: 1});
|
|
402
414
|
_nft.mintReservesFor(reserveConfigs);
|
|
403
415
|
|
|
404
|
-
// Reserve beneficiary should hold
|
|
405
|
-
assertEq(_nft.balanceOf(_reserveAddr),
|
|
416
|
+
// Reserve beneficiary should hold 4 NFTs (mintReservesFor auto-delegates to self)
|
|
417
|
+
assertEq(_nft.balanceOf(_reserveAddr), 4, "reserve beneficiary holds 4 NFTs");
|
|
406
418
|
|
|
407
419
|
// Seed fee tokens into the hook (simulating protocol fee distribution)
|
|
408
420
|
uint256 defifaAmount = 1000 ether;
|
|
@@ -410,29 +422,28 @@ contract DefifaSecurityTest is JBTest, TestBaseWorkflow {
|
|
|
410
422
|
deal(address(IERC20(_defifaProjectTokenAccount)), address(_nft), defifaAmount);
|
|
411
423
|
deal(address(IERC20(_protocolFeeProjectTokenAccount)), address(_nft), nanaAmount);
|
|
412
424
|
|
|
413
|
-
// Scorecard: equal weight
|
|
425
|
+
// Scorecard: equal weight across all 4 tiers
|
|
414
426
|
uint256 tw = _nft.TOTAL_CASHOUT_WEIGHT();
|
|
415
|
-
DefifaTierCashOutWeight[] memory sc = _buildScorecard(
|
|
416
|
-
sc[0].cashOutWeight = tw /
|
|
417
|
-
sc[1].cashOutWeight = tw /
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
427
|
+
DefifaTierCashOutWeight[] memory sc = _buildScorecard(4);
|
|
428
|
+
sc[0].cashOutWeight = tw / 4;
|
|
429
|
+
sc[1].cashOutWeight = tw / 4;
|
|
430
|
+
sc[2].cashOutWeight = tw / 4;
|
|
431
|
+
sc[3].cashOutWeight = tw / 4;
|
|
432
|
+
|
|
433
|
+
// Only paid minters attest -- reserve beneficiary has zero attestation weight at the
|
|
434
|
+
// snapshot (attestationsBegin - 1) because they received NFTs via reserve minting after
|
|
435
|
+
// the snapshot timestamp.
|
|
425
436
|
uint256 pid = _gov.submitScorecardFor(_gameId, sc);
|
|
426
437
|
vm.warp(_tsReader.timestamp() + _gov.attestationStartTimeOf(_gameId) + 1);
|
|
427
|
-
for (uint256 i; i <
|
|
428
|
-
vm.prank(
|
|
438
|
+
for (uint256 i; i < _users.length; i++) {
|
|
439
|
+
vm.prank(_users[i]);
|
|
429
440
|
_gov.attestToScorecardFrom(_gameId, pid);
|
|
430
441
|
}
|
|
431
442
|
vm.warp(_tsReader.timestamp() + _gov.attestationGracePeriodOf(_gameId) + 1);
|
|
432
443
|
_gov.ratifyScorecardFrom(_gameId, sc);
|
|
433
444
|
vm.warp(_tsReader.timestamp() + 1);
|
|
434
445
|
|
|
435
|
-
// Cash out paid minters
|
|
446
|
+
// Cash out paid minters from tiers 1 and 2
|
|
436
447
|
_cashOut(_users[0], 1, 1);
|
|
437
448
|
_cashOut(_users[1], 2, 1);
|
|
438
449
|
|
|
@@ -442,8 +453,7 @@ contract DefifaSecurityTest is JBTest, TestBaseWorkflow {
|
|
|
442
453
|
assertGt(user0Defifa, 0, "paid minter got DEFIFA tokens");
|
|
443
454
|
assertGt(user0Nana, 0, "paid minter got NANA tokens");
|
|
444
455
|
|
|
445
|
-
// Cash out reserved minter
|
|
446
|
-
// Reserved tokens are the 2nd minted in each tier
|
|
456
|
+
// Cash out reserved minter's tokens from tiers 1 and 2 (token #2 in each tier)
|
|
447
457
|
bytes memory meta1 = _cashOutMeta(1, 2);
|
|
448
458
|
vm.prank(_reserveAddr);
|
|
449
459
|
JBMultiTerminal(address(jbMultiTerminal()))
|
|
@@ -470,20 +480,17 @@ contract DefifaSecurityTest is JBTest, TestBaseWorkflow {
|
|
|
470
480
|
metadata: meta2
|
|
471
481
|
});
|
|
472
482
|
|
|
473
|
-
// Reserved minter should have gotten fee tokens
|
|
483
|
+
// Reserved minter should have gotten fee tokens from tiers 1+2 cash-outs
|
|
474
484
|
uint256 reserveDefifa = IERC20(_defifaProjectTokenAccount).balanceOf(_reserveAddr);
|
|
475
485
|
uint256 reserveNana = IERC20(_protocolFeeProjectTokenAccount).balanceOf(_reserveAddr);
|
|
476
486
|
assertGt(reserveDefifa, 0, "reserved minter got DEFIFA tokens");
|
|
477
487
|
assertGt(reserveNana, 0, "reserved minter got NANA tokens");
|
|
478
488
|
|
|
479
|
-
//
|
|
480
|
-
// (
|
|
489
|
+
// Each tier has 2 tokens (1 paid + 1 reserve), all at 1 ether.
|
|
490
|
+
// Paid minter (1 token in 1 tier) vs reserved minter (2 tokens in 2 tiers).
|
|
491
|
+
// Reserved minter gets 2x fee tokens relative to paid minter.
|
|
481
492
|
assertApproxEqAbs(user0Defifa, reserveDefifa / 2, 1, "reserved gets 2x (2 tokens) vs paid (1 token)");
|
|
482
493
|
assertApproxEqAbs(user0Nana, reserveNana / 2, 1, "NANA distribution matches");
|
|
483
|
-
|
|
484
|
-
// All fee tokens distributed (none left in hook)
|
|
485
|
-
assertEq(IERC20(_defifaProjectTokenAccount).balanceOf(address(_nft)), 0, "no DEFIFA left");
|
|
486
|
-
assertEq(IERC20(_protocolFeeProjectTokenAccount).balanceOf(address(_nft)), 0, "no NANA left");
|
|
487
494
|
}
|
|
488
495
|
|
|
489
496
|
// =========================================================================
|
|
@@ -616,7 +623,8 @@ contract DefifaSecurityTest is JBTest, TestBaseWorkflow {
|
|
|
616
623
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
617
624
|
terminal: jbMultiTerminal(),
|
|
618
625
|
minParticipation: 0,
|
|
619
|
-
scorecardTimeout: 0
|
|
626
|
+
scorecardTimeout: 0,
|
|
627
|
+
timelockDuration: 0
|
|
620
628
|
});
|
|
621
629
|
}
|
|
622
630
|
|
|
@@ -672,7 +680,7 @@ contract DefifaSecurityTest is JBTest, TestBaseWorkflow {
|
|
|
672
680
|
vm.warp(block.timestamp + _gov.attestationStartTimeOf(_gameId) + 1);
|
|
673
681
|
for (uint256 i; i < _users.length; i++) {
|
|
674
682
|
vm.prank(_users[i]);
|
|
675
|
-
_gov.attestToScorecardFrom(_gameId, pid)
|
|
683
|
+
try _gov.attestToScorecardFrom(_gameId, pid) {} catch {}
|
|
676
684
|
}
|
|
677
685
|
vm.warp(block.timestamp + _gov.attestationGracePeriodOf(_gameId) + 1);
|
|
678
686
|
}
|
package/test/DefifaUSDC.t.sol
CHANGED
|
@@ -211,7 +211,8 @@ contract DefifaUSDCTest is JBTest, TestBaseWorkflow {
|
|
|
211
211
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
212
212
|
terminal: jbMultiTerminal(),
|
|
213
213
|
minParticipation: minParticipation,
|
|
214
|
-
scorecardTimeout: scorecardTimeout
|
|
214
|
+
scorecardTimeout: scorecardTimeout,
|
|
215
|
+
timelockDuration: 0
|
|
215
216
|
});
|
|
216
217
|
}
|
|
217
218
|
|
|
@@ -277,7 +278,7 @@ contract DefifaUSDCTest is JBTest, TestBaseWorkflow {
|
|
|
277
278
|
vm.warp((attestStart > current ? attestStart : current) + 1);
|
|
278
279
|
for (uint256 i; i < _users.length; i++) {
|
|
279
280
|
vm.prank(_users[i]);
|
|
280
|
-
_gov.attestToScorecardFrom(_gameId, pid)
|
|
281
|
+
try _gov.attestToScorecardFrom(_gameId, pid) {} catch {}
|
|
281
282
|
}
|
|
282
283
|
vm.warp(_tsReader.timestamp() + _gov.attestationGracePeriodOf(_gameId) + 1);
|
|
283
284
|
_gov.ratifyScorecardFrom(_gameId, sc);
|
package/test/Fork.t.sol
CHANGED
|
@@ -694,28 +694,36 @@ contract DefifaForkTest is JBTest, TestBaseWorkflow {
|
|
|
694
694
|
// =========================================================================
|
|
695
695
|
|
|
696
696
|
function test_fork_singleTierGame() external {
|
|
697
|
-
|
|
697
|
+
// BWA prevents a sole beneficiary (100% weight) from self-attesting.
|
|
698
|
+
// Use 2 tiers: tier 1 gets all weight, tier 2 provides neutral attestation.
|
|
699
|
+
DefifaLaunchProjectData memory d = _launchData(2, 1 ether);
|
|
698
700
|
(_pid, _nft, _gov) = _launch(d);
|
|
699
701
|
vm.warp(d.start - d.mintPeriodDuration - d.refundPeriodDuration);
|
|
700
702
|
|
|
701
|
-
_users = new address[](
|
|
703
|
+
_users = new address[](2);
|
|
702
704
|
_users[0] = _addr(0);
|
|
703
705
|
_mint(_users[0], 1, 1 ether);
|
|
704
706
|
_delegateSelf(_users[0], 1);
|
|
705
707
|
vm.warp(_tsReader.timestamp() + 1);
|
|
706
708
|
|
|
709
|
+
_users[1] = _addr(1);
|
|
710
|
+
_mint(_users[1], 2, 1 ether);
|
|
711
|
+
_delegateSelf(_users[1], 2);
|
|
712
|
+
vm.warp(_tsReader.timestamp() + 1);
|
|
713
|
+
|
|
707
714
|
_toScoring();
|
|
708
715
|
|
|
709
|
-
DefifaTierCashOutWeight[] memory sc = _buildScorecard(
|
|
716
|
+
DefifaTierCashOutWeight[] memory sc = _buildScorecard(2);
|
|
710
717
|
sc[0].cashOutWeight = _nft.TOTAL_CASHOUT_WEIGHT();
|
|
718
|
+
sc[1].cashOutWeight = 0;
|
|
711
719
|
|
|
712
720
|
_attestAndRatify(sc);
|
|
713
721
|
|
|
714
|
-
// Cash out the
|
|
722
|
+
// Cash out the winner (tier 1).
|
|
715
723
|
uint256 bb = _users[0].balance;
|
|
716
724
|
_cashOut(_users[0], 1, 1);
|
|
717
725
|
uint256 received = _users[0].balance - bb;
|
|
718
|
-
assertGt(received, 0, "
|
|
726
|
+
assertGt(received, 0, "winner receives ETH");
|
|
719
727
|
}
|
|
720
728
|
|
|
721
729
|
// =========================================================================
|
|
@@ -969,7 +977,8 @@ contract DefifaForkTest is JBTest, TestBaseWorkflow {
|
|
|
969
977
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
970
978
|
terminal: jbMultiTerminal(),
|
|
971
979
|
minParticipation: 0,
|
|
972
|
-
scorecardTimeout: 0
|
|
980
|
+
scorecardTimeout: 0,
|
|
981
|
+
timelockDuration: 0
|
|
973
982
|
});
|
|
974
983
|
(_pid, _nft, _gov) = _launch(d);
|
|
975
984
|
vm.warp(d.start - d.mintPeriodDuration - d.refundPeriodDuration);
|
|
@@ -1003,15 +1012,13 @@ contract DefifaForkTest is JBTest, TestBaseWorkflow {
|
|
|
1003
1012
|
sc[0].cashOutWeight = _nft.TOTAL_CASHOUT_WEIGHT() / 2;
|
|
1004
1013
|
sc[1].cashOutWeight = _nft.TOTAL_CASHOUT_WEIGHT() / 2;
|
|
1005
1014
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
allUsers[2] = reserveAddr;
|
|
1010
|
-
|
|
1015
|
+
// Only paid minters attest -- reserve beneficiary has zero attestation weight at the
|
|
1016
|
+
// snapshot (attestationsBegin - 1) because they received NFTs via reserve minting after
|
|
1017
|
+
// the snapshot timestamp.
|
|
1011
1018
|
uint256 pid = _gov.submitScorecardFor(_gameId, sc);
|
|
1012
1019
|
vm.warp(_tsReader.timestamp() + _gov.attestationStartTimeOf(_gameId) + 1);
|
|
1013
|
-
for (uint256 i; i <
|
|
1014
|
-
vm.prank(
|
|
1020
|
+
for (uint256 i; i < _users.length; i++) {
|
|
1021
|
+
vm.prank(_users[i]);
|
|
1015
1022
|
_gov.attestToScorecardFrom(_gameId, pid);
|
|
1016
1023
|
}
|
|
1017
1024
|
vm.warp(_tsReader.timestamp() + _gov.attestationGracePeriodOf(_gameId) + 1);
|
|
@@ -1354,15 +1361,10 @@ contract DefifaForkTest is JBTest, TestBaseWorkflow {
|
|
|
1354
1361
|
uint256 current = _tsReader.timestamp();
|
|
1355
1362
|
vm.warp((attestStart > current ? attestStart : current) + 1);
|
|
1356
1363
|
|
|
1357
|
-
// Non-holder attests — should
|
|
1364
|
+
// Non-holder attests — should revert because BWA weight is 0.
|
|
1358
1365
|
address stranger = _addr(999);
|
|
1359
1366
|
vm.prank(stranger);
|
|
1360
|
-
|
|
1361
|
-
assertEq(weight, 0, "non-holder has 0 attestation power");
|
|
1362
|
-
|
|
1363
|
-
// But they can't attest again.
|
|
1364
|
-
vm.prank(stranger);
|
|
1365
|
-
vm.expectRevert(DefifaGovernor.DefifaGovernor_AlreadyAttested.selector);
|
|
1367
|
+
vm.expectRevert(DefifaGovernor.DefifaGovernor_NotAllowed.selector);
|
|
1366
1368
|
_gov.attestToScorecardFrom(_gameId, pid);
|
|
1367
1369
|
}
|
|
1368
1370
|
|
|
@@ -1632,7 +1634,7 @@ contract DefifaForkTest is JBTest, TestBaseWorkflow {
|
|
|
1632
1634
|
uint256 expectedQuorum = (3 * _gov.MAX_ATTESTATION_POWER_TIER()) / 2;
|
|
1633
1635
|
assertEq(q, expectedQuorum, "quorum = floor(3 * 1e9 / 2)");
|
|
1634
1636
|
|
|
1635
|
-
//
|
|
1637
|
+
// All 3 tiers attesting should exceed quorum (BWA reduces each holder's power by their tier share).
|
|
1636
1638
|
DefifaTierCashOutWeight[] memory sc = _evenScorecard(3);
|
|
1637
1639
|
uint256 pid = _gov.submitScorecardFor(_gameId, sc);
|
|
1638
1640
|
|
|
@@ -1640,13 +1642,12 @@ contract DefifaForkTest is JBTest, TestBaseWorkflow {
|
|
|
1640
1642
|
uint256 current = _tsReader.timestamp();
|
|
1641
1643
|
vm.warp((attestStart > current ? attestStart : current) + 1);
|
|
1642
1644
|
|
|
1643
|
-
//
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1645
|
+
// All 3 users attest. BWA reduces each to ~2/3 power; 3 * 2/3 * MAX = 2*MAX > quorum.
|
|
1646
|
+
for (uint256 i; i < 3; i++) {
|
|
1647
|
+
vm.prank(_users[i]);
|
|
1648
|
+
_gov.attestToScorecardFrom(_gameId, pid);
|
|
1649
|
+
}
|
|
1648
1650
|
|
|
1649
|
-
// 2e9 > 1.5e9 → quorum met.
|
|
1650
1651
|
vm.warp(_tsReader.timestamp() + _gov.attestationGracePeriodOf(_gameId) + 1);
|
|
1651
1652
|
assertEq(uint256(_gov.stateOf(_gameId, pid)), uint256(DefifaScorecardState.SUCCEEDED));
|
|
1652
1653
|
}
|
|
@@ -1866,7 +1867,8 @@ contract DefifaForkTest is JBTest, TestBaseWorkflow {
|
|
|
1866
1867
|
// =========================================================================
|
|
1867
1868
|
|
|
1868
1869
|
function test_fork_fuzz_fundConservation(uint8 rawTiers, uint8 rawPlayers) external {
|
|
1869
|
-
|
|
1870
|
+
// N≥3 avoids BWA rounding shortfall with N=2 even split + multiple holders per tier.
|
|
1871
|
+
uint8 nTiers = uint8(bound(rawTiers, 3, 12));
|
|
1870
1872
|
uint8 nPpt = uint8(bound(rawPlayers, 1, 3));
|
|
1871
1873
|
|
|
1872
1874
|
_setupMultiN(nTiers, nPpt, 1 ether);
|
|
@@ -2068,7 +2070,8 @@ contract DefifaForkTest is JBTest, TestBaseWorkflow {
|
|
|
2068
2070
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
2069
2071
|
terminal: jbMultiTerminal(),
|
|
2070
2072
|
minParticipation: 0,
|
|
2071
|
-
scorecardTimeout: 0
|
|
2073
|
+
scorecardTimeout: 0,
|
|
2074
|
+
timelockDuration: 0
|
|
2072
2075
|
});
|
|
2073
2076
|
(_pid, _nft, _gov) = _launch(d);
|
|
2074
2077
|
|
|
@@ -2201,7 +2204,8 @@ contract DefifaForkTest is JBTest, TestBaseWorkflow {
|
|
|
2201
2204
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
2202
2205
|
terminal: jbMultiTerminal(),
|
|
2203
2206
|
minParticipation: minParticipation,
|
|
2204
|
-
scorecardTimeout: scorecardTimeout
|
|
2207
|
+
scorecardTimeout: scorecardTimeout,
|
|
2208
|
+
timelockDuration: 0
|
|
2205
2209
|
});
|
|
2206
2210
|
}
|
|
2207
2211
|
|
|
@@ -2233,7 +2237,8 @@ contract DefifaForkTest is JBTest, TestBaseWorkflow {
|
|
|
2233
2237
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
2234
2238
|
terminal: jbMultiTerminal(),
|
|
2235
2239
|
minParticipation: 0,
|
|
2236
|
-
scorecardTimeout: 0
|
|
2240
|
+
scorecardTimeout: 0,
|
|
2241
|
+
timelockDuration: 0
|
|
2237
2242
|
});
|
|
2238
2243
|
}
|
|
2239
2244
|
|
|
@@ -2319,7 +2324,7 @@ contract DefifaForkTest is JBTest, TestBaseWorkflow {
|
|
|
2319
2324
|
vm.warp((attestStart > current ? attestStart : current) + 1);
|
|
2320
2325
|
for (uint256 i; i < _users.length; i++) {
|
|
2321
2326
|
vm.prank(_users[i]);
|
|
2322
|
-
_gov.attestToScorecardFrom(_gameId, pid)
|
|
2327
|
+
try _gov.attestToScorecardFrom(_gameId, pid) {} catch {}
|
|
2323
2328
|
}
|
|
2324
2329
|
vm.warp(_tsReader.timestamp() + _gov.attestationGracePeriodOf(_gameId) + 1);
|
|
2325
2330
|
}
|
package/test/TestAuditGaps.sol
CHANGED
|
@@ -210,7 +210,8 @@ contract TestAuditGapsERC20Games is JBTest, TestBaseWorkflow {
|
|
|
210
210
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
211
211
|
terminal: jbMultiTerminal(),
|
|
212
212
|
minParticipation: minParticipation,
|
|
213
|
-
scorecardTimeout: scorecardTimeout
|
|
213
|
+
scorecardTimeout: scorecardTimeout,
|
|
214
|
+
timelockDuration: 0
|
|
214
215
|
});
|
|
215
216
|
}
|
|
216
217
|
|
|
@@ -276,7 +277,7 @@ contract TestAuditGapsERC20Games is JBTest, TestBaseWorkflow {
|
|
|
276
277
|
vm.warp((attestStart > current ? attestStart : current) + 1);
|
|
277
278
|
for (uint256 i; i < _users.length; i++) {
|
|
278
279
|
vm.prank(_users[i]);
|
|
279
|
-
_gov.attestToScorecardFrom(_gameId, pid)
|
|
280
|
+
try _gov.attestToScorecardFrom(_gameId, pid) {} catch {}
|
|
280
281
|
}
|
|
281
282
|
vm.warp(_tsReader.timestamp() + _gov.attestationGracePeriodOf(_gameId) + 1);
|
|
282
283
|
_gov.ratifyScorecardFrom(_gameId, sc);
|
|
@@ -656,7 +657,8 @@ contract TestAuditGapsMultiGameIsolation is JBTest, TestBaseWorkflow {
|
|
|
656
657
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
657
658
|
terminal: jbMultiTerminal(),
|
|
658
659
|
minParticipation: 0,
|
|
659
|
-
scorecardTimeout: 0
|
|
660
|
+
scorecardTimeout: 0,
|
|
661
|
+
timelockDuration: 0
|
|
660
662
|
});
|
|
661
663
|
}
|
|
662
664
|
|
|
@@ -782,7 +784,7 @@ contract TestAuditGapsMultiGameIsolation is JBTest, TestBaseWorkflow {
|
|
|
782
784
|
vm.warp((attestStart > current ? attestStart : current) + 1);
|
|
783
785
|
for (uint256 i; i < users.length; i++) {
|
|
784
786
|
vm.prank(users[i]);
|
|
785
|
-
governor.attestToScorecardFrom(gameId, pid)
|
|
787
|
+
try governor.attestToScorecardFrom(gameId, pid) {} catch {}
|
|
786
788
|
}
|
|
787
789
|
vm.warp(_tsReader.timestamp() + governor.attestationGracePeriodOf(gameId) + 1);
|
|
788
790
|
governor.ratifyScorecardFrom(gameId, sc);
|
|
@@ -195,7 +195,8 @@ contract TestQACashOutDoSDuringFulfillmentWindow is JBTest, TestBaseWorkflow {
|
|
|
195
195
|
|
|
196
196
|
uint256 _proposalId = _governor.submitScorecardFor(_gameId, scorecards);
|
|
197
197
|
vm.warp(_tsReader.timestamp() + _governor.attestationStartTimeOf(_gameId) + 1);
|
|
198
|
-
|
|
198
|
+
// Start from i=1: user 0 holds tier 1 which gets 100% weight, so BWA reduces their power to 0.
|
|
199
|
+
for (uint256 i = 1; i < _users.length; i++) {
|
|
199
200
|
vm.prank(_users[i]);
|
|
200
201
|
_governor.attestToScorecardFrom(_gameId, _proposalId);
|
|
201
202
|
}
|
|
@@ -283,7 +284,8 @@ contract TestQACashOutDoSDuringFulfillmentWindow is JBTest, TestBaseWorkflow {
|
|
|
283
284
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
284
285
|
terminal: jbMultiTerminal(),
|
|
285
286
|
minParticipation: 0,
|
|
286
|
-
scorecardTimeout: 0
|
|
287
|
+
scorecardTimeout: 0,
|
|
288
|
+
timelockDuration: 0
|
|
287
289
|
});
|
|
288
290
|
}
|
|
289
291
|
|
|
@@ -505,7 +507,8 @@ contract TestQAGameIdPredictionRace is JBTest, TestBaseWorkflow {
|
|
|
505
507
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
506
508
|
terminal: jbMultiTerminal(),
|
|
507
509
|
minParticipation: 0,
|
|
508
|
-
scorecardTimeout: 0
|
|
510
|
+
scorecardTimeout: 0,
|
|
511
|
+
timelockDuration: 0
|
|
509
512
|
});
|
|
510
513
|
}
|
|
511
514
|
}
|
|
@@ -30,7 +30,7 @@ import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
|
30
30
|
import {JBCurrencyIds} from "@bananapus/core-v6/src/libraries/JBCurrencyIds.sol";
|
|
31
31
|
import {IJBRulesetApprovalHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetApprovalHook.sol";
|
|
32
32
|
|
|
33
|
-
contract
|
|
33
|
+
contract AttestationDoubleCountTest is JBTest, TestBaseWorkflow {
|
|
34
34
|
using JBRulesetMetadataResolver for JBRuleset;
|
|
35
35
|
|
|
36
36
|
uint256 internal _protocolFeeProjectId;
|
|
@@ -193,7 +193,8 @@ contract CodexAttestationDoubleCount is JBTest, TestBaseWorkflow {
|
|
|
193
193
|
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
194
194
|
terminal: jbMultiTerminal(),
|
|
195
195
|
minParticipation: 0,
|
|
196
|
-
scorecardTimeout: 0
|
|
196
|
+
scorecardTimeout: 0,
|
|
197
|
+
timelockDuration: 0
|
|
197
198
|
});
|
|
198
199
|
|
|
199
200
|
_mintPhaseStart = d.start - d.mintPeriodDuration - d.refundPeriodDuration;
|