@ballkidz/defifa 0.0.25 → 0.0.26

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 (52) hide show
  1. package/AUDIT_INSTRUCTIONS.md +6 -2
  2. package/README.md +11 -2
  3. package/RISKS.md +3 -1
  4. package/STYLE_GUIDE.md +14 -11
  5. package/package.json +31 -14
  6. package/script/Deploy.s.sol +4 -1
  7. package/src/DefifaDeployer.sol +74 -46
  8. package/src/DefifaGovernor.sol +53 -11
  9. package/src/DefifaHook.sol +79 -25
  10. package/src/DefifaTokenUriResolver.sol +111 -19
  11. package/src/interfaces/IDefifaDeployer.sol +5 -0
  12. package/src/interfaces/IDefifaGovernor.sol +4 -0
  13. package/src/interfaces/IDefifaHook.sol +5 -0
  14. package/src/libraries/DefifaHookLib.sol +9 -10
  15. package/src/structs/DefifaLaunchProjectData.sol +0 -3
  16. package/CRYPTO_ECON.pdf +0 -0
  17. package/CRYPTO_ECON.tex +0 -997
  18. package/foundry.lock +0 -17
  19. package/references/operations.md +0 -32
  20. package/references/runtime.md +0 -43
  21. package/slither-ci.config.json +0 -10
  22. package/sphinx.lock +0 -521
  23. package/test/BWAFunctionComparison.t.sol +0 -1320
  24. package/test/DefifaAdversarialQuorum.t.sol +0 -617
  25. package/test/DefifaAuditLowGuards.t.sol +0 -308
  26. package/test/DefifaFeeAccounting.t.sol +0 -581
  27. package/test/DefifaGovernanceHardening.t.sol +0 -1315
  28. package/test/DefifaGovernor.t.sol +0 -1378
  29. package/test/DefifaHookRegressions.t.sol +0 -415
  30. package/test/DefifaMintCostInvariant.t.sol +0 -319
  31. package/test/DefifaNoContest.t.sol +0 -941
  32. package/test/DefifaSecurity.t.sol +0 -741
  33. package/test/DefifaUSDC.t.sol +0 -480
  34. package/test/Fork.t.sol +0 -2388
  35. package/test/TestAuditGaps.sol +0 -984
  36. package/test/TestQALastMile.t.sol +0 -514
  37. package/test/audit/AttestationDoubleCount.t.sol +0 -218
  38. package/test/audit/CodexNemesisCurrencyMismatchBypass.t.sol +0 -112
  39. package/test/audit/CodexNemesisNoContestReserveDrain.t.sol +0 -238
  40. package/test/audit/CodexNemesisOneTierZeroTimeoutLockVerified.t.sol +0 -218
  41. package/test/audit/CodexNemesisSingleTierTimeoutLock.t.sol +0 -237
  42. package/test/audit/CodexRegistryMismatch.t.sol +0 -191
  43. package/test/audit/CodexTierCapMismatch.t.sol +0 -171
  44. package/test/audit/CurrencyMismatchFix.t.sol +0 -265
  45. package/test/audit/FixPendingReserveDilution.t.sol +0 -366
  46. package/test/audit/H5TierCapValidation.t.sol +0 -184
  47. package/test/audit/PendingReserveDilution.t.sol +0 -298
  48. package/test/audit/PendingReserveQuorumGrief.t.sol +0 -355
  49. package/test/audit/PendingReserveSnapshotBypass.t.sol +0 -319
  50. package/test/regression/AttestationDelegateBeneficiary.t.sol +0 -271
  51. package/test/regression/FulfillmentBlocksRatification.t.sol +0 -279
  52. package/test/regression/GracePeriodBypass.t.sol +0 -302
@@ -1,171 +0,0 @@
1
- // SPDX-License-Identifier: UNLICENSED
2
- pragma solidity 0.8.28;
3
-
4
- import {DefifaDeployer} from "../../src/DefifaDeployer.sol";
5
- import {DefifaGovernor} from "../../src/DefifaGovernor.sol";
6
- import {DefifaHook} from "../../src/DefifaHook.sol";
7
- import {DefifaTokenUriResolver} from "../../src/DefifaTokenUriResolver.sol";
8
- import {DefifaGamePhase} from "../../src/enums/DefifaGamePhase.sol";
9
- import {DefifaLaunchProjectData} from "../../src/structs/DefifaLaunchProjectData.sol";
10
- import {DefifaTierCashOutWeight} from "../../src/structs/DefifaTierCashOutWeight.sol";
11
- import {DefifaTierParams} from "../../src/structs/DefifaTierParams.sol";
12
-
13
- import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
14
- import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
15
- import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
16
- import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
17
- import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
18
- import {JBCurrencyIds} from "@bananapus/core-v6/src/libraries/JBCurrencyIds.sol";
19
- import {JBFundAccessLimitGroup} from "@bananapus/core-v6/src/structs/JBFundAccessLimitGroup.sol";
20
- import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
21
- import {JBRulesetConfig} from "@bananapus/core-v6/src/structs/JBRulesetConfig.sol";
22
- import {JBRulesetMetadataResolver} from "@bananapus/core-v6/src/libraries/JBRulesetMetadataResolver.sol";
23
- import {JBRulesetMetadata} from "@bananapus/core-v6/src/structs/JBRulesetMetadata.sol";
24
- import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
25
- import {JBSplitGroup} from "@bananapus/core-v6/src/structs/JBSplitGroup.sol";
26
- import {JBTerminalConfig} from "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
27
- import {IJBRulesetApprovalHook} from "@bananapus/core-v6/src/interfaces/IJBRulesets.sol";
28
- import {JBTest} from "@bananapus/core-v6/test/helpers/JBTest.sol";
29
- import {TestBaseWorkflow} from "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
30
-
31
- import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
32
- import {ITypeface} from "lib/typeface/contracts/interfaces/ITypeface.sol";
33
-
34
- contract CodexTierCapMismatchTest is JBTest, TestBaseWorkflow {
35
- using JBRulesetMetadataResolver for JBRuleset;
36
-
37
- DefifaDeployer internal deployer;
38
- DefifaGovernor internal governor;
39
- DefifaHook internal hookCodeOrigin;
40
-
41
- function setUp() public virtual override {
42
- super.setUp();
43
-
44
- JBAccountingContext[] memory tokens = new JBAccountingContext[](1);
45
- tokens[0] = JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH});
46
-
47
- JBTerminalConfig[] memory terminalConfigs = new JBTerminalConfig[](1);
48
- terminalConfigs[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: tokens});
49
-
50
- JBRulesetConfig[] memory rulesetConfigs = new JBRulesetConfig[](1);
51
- rulesetConfigs[0] = JBRulesetConfig({
52
- mustStartAtOrAfter: 0,
53
- duration: 10 days,
54
- weight: 1e18,
55
- weightCutPercent: 0,
56
- approvalHook: IJBRulesetApprovalHook(address(0)),
57
- metadata: JBRulesetMetadata({
58
- reservedPercent: 0,
59
- cashOutTaxRate: 0,
60
- baseCurrency: JBCurrencyIds.ETH,
61
- pausePay: false,
62
- pauseCreditTransfers: false,
63
- allowOwnerMinting: false,
64
- allowSetCustomToken: false,
65
- allowTerminalMigration: false,
66
- allowSetTerminals: false,
67
- allowSetController: false,
68
- allowAddAccountingContext: false,
69
- allowAddPriceFeed: false,
70
- ownerMustSendPayouts: false,
71
- holdFees: false,
72
- useTotalSurplusForCashOuts: false,
73
- useDataHookForPay: true,
74
- useDataHookForCashOut: true,
75
- dataHook: address(0),
76
- metadata: 0
77
- }),
78
- splitGroups: new JBSplitGroup[](0),
79
- fundAccessLimitGroups: new JBFundAccessLimitGroup[](0)
80
- });
81
-
82
- address projectOwner = address(bytes20(keccak256("projectOwner")));
83
- uint256 protocolFeeProjectId =
84
- jbController().launchProjectFor(projectOwner, "", rulesetConfigs, terminalConfigs, "");
85
- vm.prank(projectOwner);
86
- address nanaToken =
87
- address(jbController().deployERC20For(protocolFeeProjectId, "Bananapus", "NANA", bytes32(0)));
88
-
89
- uint256 defifaProjectId = jbController().launchProjectFor(projectOwner, "", rulesetConfigs, terminalConfigs, "");
90
- vm.prank(projectOwner);
91
- address defifaToken = address(jbController().deployERC20For(defifaProjectId, "Defifa", "DEFIFA", bytes32(0)));
92
-
93
- hookCodeOrigin = new DefifaHook(jbDirectory(), IERC20(defifaToken), IERC20(nanaToken));
94
- governor = new DefifaGovernor(jbController(), address(this));
95
- deployer = new DefifaDeployer(
96
- address(hookCodeOrigin),
97
- new DefifaTokenUriResolver(ITypeface(address(0))),
98
- governor,
99
- jbController(),
100
- new JBAddressRegistry(),
101
- defifaProjectId,
102
- protocolFeeProjectId
103
- );
104
-
105
- hookCodeOrigin.transferOwnership(address(deployer));
106
- governor.transferOwnership(address(deployer));
107
- }
108
-
109
- function test_launchRevertsFor129Tiers() external {
110
- DefifaLaunchProjectData memory data = _launchData(129);
111
-
112
- // H-5 fix: the deployer now caps tiers at 128, so launching with 129 reverts.
113
- vm.expectRevert(DefifaDeployer.DefifaDeployer_InvalidGameConfiguration.selector);
114
- deployer.launchGameWith(data);
115
- }
116
-
117
- function _launchData(uint256 tierCount) internal returns (DefifaLaunchProjectData memory) {
118
- DefifaTierParams[] memory tiers = new DefifaTierParams[](tierCount);
119
- for (uint256 i; i < tierCount; i++) {
120
- tiers[i] = DefifaTierParams({
121
- name: "TEAM",
122
- reservedRate: 0,
123
- reservedTokenBeneficiary: address(0),
124
- encodedIPFSUri: bytes32(0),
125
- shouldUseReservedTokenBeneficiaryAsDefault: false
126
- });
127
- }
128
-
129
- return DefifaLaunchProjectData({
130
- name: "DEFIFA",
131
- projectUri: "",
132
- contractUri: "",
133
- baseUri: "",
134
- tiers: tiers,
135
- tierPrice: 1 ether,
136
- token: JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH}),
137
- mintPeriodDuration: 1 days,
138
- refundPeriodDuration: 1 days,
139
- start: uint48(block.timestamp + 3 days),
140
- splits: new JBSplit[](0),
141
- attestationStartTime: 0,
142
- attestationGracePeriod: 100_381,
143
- defaultAttestationDelegate: address(0),
144
- defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
145
- terminal: jbMultiTerminal(),
146
- store: new JB721TiersHookStore(),
147
- minParticipation: 0,
148
- scorecardTimeout: 7 days,
149
- timelockDuration: 0
150
- });
151
- }
152
-
153
- function _mintTier(uint256 gameId, uint16 tierId, uint256 amount) internal {
154
- address buyer = address(bytes20(keccak256(abi.encodePacked("buyer", tierId))));
155
- vm.deal(buyer, amount);
156
-
157
- uint16[] memory tierIds = new uint16[](1);
158
- tierIds[0] = tierId;
159
-
160
- bytes[] memory payloads = new bytes[](1);
161
- payloads[0] = abi.encode(address(0), tierIds);
162
-
163
- bytes4[] memory ids = new bytes4[](1);
164
- ids[0] = metadataHelper().getId("pay", address(hookCodeOrigin));
165
-
166
- bytes memory metadata = metadataHelper().createMetadata(ids, payloads);
167
-
168
- vm.prank(buyer);
169
- jbMultiTerminal().pay{value: amount}(gameId, JBConstants.NATIVE_TOKEN, amount, buyer, 0, "", metadata);
170
- }
171
- }
@@ -1,265 +0,0 @@
1
- // SPDX-License-Identifier: UNLICENSED
2
- pragma solidity 0.8.28;
3
-
4
- import {DefifaUSDCTest, DefifaMockUSDC} from "../DefifaUSDC.t.sol";
5
- import {DefifaDeployer} from "../../src/DefifaDeployer.sol";
6
- import {DefifaGovernor} from "../../src/DefifaGovernor.sol";
7
- import {DefifaHook} from "../../src/DefifaHook.sol";
8
- import {DefifaTokenUriResolver} from "../../src/DefifaTokenUriResolver.sol";
9
- import {DefifaLaunchProjectData} from "../../src/structs/DefifaLaunchProjectData.sol";
10
- import {DefifaTierCashOutWeight} from "../../src/structs/DefifaTierCashOutWeight.sol";
11
- import {DefifaTierParams} from "../../src/structs/DefifaTierParams.sol";
12
- import {DefifaDelegation} from "../../src/structs/DefifaDelegation.sol";
13
- import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
14
- import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
15
- import {IJBRulesetApprovalHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetApprovalHook.sol";
16
- import {JBRulesetConfig, JBTerminalConfig} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
17
- import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
18
- import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
19
- import {JBCurrencyIds} from "@bananapus/core-v6/src/libraries/JBCurrencyIds.sol";
20
- import {JBFundAccessLimitGroup} from "@bananapus/core-v6/src/structs/JBFundAccessLimitGroup.sol";
21
- import {JBMultiTerminal} from "@bananapus/core-v6/src/JBMultiTerminal.sol";
22
- import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
23
- import {JBPermissionsData} from "@bananapus/core-v6/src/structs/JBPermissionsData.sol";
24
- import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
25
- import {JBRulesetMetadata} from "@bananapus/core-v6/src/structs/JBRulesetMetadata.sol";
26
- import {JBRulesetMetadataResolver} from "@bananapus/core-v6/src/libraries/JBRulesetMetadataResolver.sol";
27
- import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
28
- import {JBSplitGroup} from "@bananapus/core-v6/src/structs/JBSplitGroup.sol";
29
- import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
30
- import {TestBaseWorkflow} from "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
31
- import {JBTest} from "@bananapus/core-v6/test/helpers/JBTest.sol";
32
- import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
33
- import {ITypeface} from "lib/typeface/contracts/interfaces/ITypeface.sol";
34
-
35
- /// @title CurrencyMismatchFixTest
36
- /// @notice Adversarial tests for the currency mismatch fix: verifies that fulfillCommitmentsOf correctly resolves
37
- /// payout limits for both ETH and ERC-20 games, and that launch-time validation rejects zero-currency ERC-20
38
- /// configurations. Inherits DefifaUSDCTest for USDC helpers and fee project setup.
39
- contract CurrencyMismatchFixTest is DefifaUSDCTest {
40
- // =========================================================================
41
- // HELPERS
42
- // =========================================================================
43
-
44
- function _launchDataNonCanonical(uint8 n, uint104 tierPrice) internal returns (DefifaLaunchProjectData memory) {
45
- DefifaTierParams[] memory tp = new DefifaTierParams[](n);
46
- for (uint256 i; i < n; i++) {
47
- tp[i] = DefifaTierParams({
48
- reservedRate: 1001,
49
- reservedTokenBeneficiary: address(0),
50
- encodedIPFSUri: bytes32(0),
51
- shouldUseReservedTokenBeneficiaryAsDefault: false,
52
- name: "DEFIFA"
53
- });
54
- }
55
-
56
- // Non-canonical currency (1) for a USDC token.
57
- return DefifaLaunchProjectData({
58
- name: "DEFIFA_NONCANONICAL",
59
- projectUri: "",
60
- contractUri: "",
61
- baseUri: "",
62
- token: JBAccountingContext({token: address(usdc), decimals: 6, currency: 1}),
63
- mintPeriodDuration: 1 days,
64
- start: uint48(block.timestamp + 3 days),
65
- refundPeriodDuration: 1 days,
66
- store: new JB721TiersHookStore(),
67
- splits: new JBSplit[](0),
68
- attestationStartTime: 0,
69
- attestationGracePeriod: 1 days,
70
- defaultAttestationDelegate: address(0),
71
- tierPrice: tierPrice,
72
- tiers: tp,
73
- defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
74
- terminal: jbMultiTerminal(),
75
- minParticipation: 0,
76
- scorecardTimeout: 0,
77
- timelockDuration: 0
78
- });
79
- }
80
-
81
- function _setupNonCanonicalGame(uint8 nTiers, uint104 tierPrice) internal {
82
- DefifaLaunchProjectData memory d = _launchDataNonCanonical(nTiers, tierPrice);
83
- (_pid, _nft, _gov) = _launch(d);
84
- vm.warp(d.start - d.mintPeriodDuration - d.refundPeriodDuration);
85
- _users = new address[](nTiers);
86
- for (uint256 i; i < nTiers; i++) {
87
- _users[i] = _addr(i);
88
- _mintUsdc(_users[i], i + 1, tierPrice);
89
- _delegateSelf(_users[i], i + 1);
90
- vm.warp(block.timestamp + 1);
91
- }
92
- }
93
-
94
- // =========================================================================
95
- // TEST 1: ERC-20 game with non-canonical currency correctly resolves payout limit
96
- // =========================================================================
97
-
98
- /// @notice An ERC-20 game launched with currency=1 (non-canonical) now correctly sends commitment payouts
99
- /// because fulfillCommitmentsOf uses metadata.baseCurrency instead of uint32(uint160(token)).
100
- function test_currencyMismatchFix_erc20NonCanonicalCurrencyFulfillsCorrectly() external {
101
- uint104 tierPrice = 100e6;
102
- _setupNonCanonicalGame(4, tierPrice);
103
-
104
- // Advance to scoring phase.
105
- _toScoring();
106
-
107
- uint256 potBefore = _balance();
108
- assertEq(potBefore, 400e6, "pot = 400 USDC before fulfillment");
109
-
110
- uint256 expectedFee = (potBefore * 75_000_000) / JBConstants.SPLITS_TOTAL_PERCENT;
111
-
112
- _attestAndRatify(_evenScorecard(4));
113
-
114
- // Verify: payout succeeded. fulfilledCommitmentsOf stores the actual fee amount, not sentinel (1).
115
- uint256 fulfilled = deployer.fulfilledCommitmentsOf(_pid);
116
- assertEq(fulfilled, expectedFee, "fulfilled = expected fee (payout succeeded)");
117
-
118
- // Verify: balance reduced by the fee.
119
- assertEq(_balance(), potBefore - expectedFee, "balance = pot - fee");
120
- }
121
-
122
- // =========================================================================
123
- // TEST 2: ETH game fulfillment still works correctly (no regression)
124
- // =========================================================================
125
-
126
- /// @notice ETH game (canonical currency) continues to work after the fix.
127
- /// Covered by DefifaFeeAccountingTest; this verifies no regression from the baseCurrency change.
128
- function test_currencyMismatchFix_ethGameFeeAccountingUnchanged() external {
129
- // The DefifaFeeAccountingTest suite tests ETH fulfillment comprehensively.
130
- // Here we verify the core assertion: fee percentage matches for USDC canonical game too.
131
- uint104 tierPrice = 100e6;
132
- _setupGameUsdc(4, tierPrice);
133
-
134
- _toScoring();
135
-
136
- uint256 potBefore = _balance();
137
- uint256 expectedFee = (potBefore * 75_000_000) / JBConstants.SPLITS_TOTAL_PERCENT;
138
-
139
- _attestAndRatify(_evenScorecard(4));
140
-
141
- uint256 fulfilled = deployer.fulfilledCommitmentsOf(_pid);
142
- assertEq(fulfilled, expectedFee, "canonical USDC fee unchanged by fix");
143
- assertEq(fulfilled + _balance(), potBefore, "fee + surplus = original pot");
144
- }
145
-
146
- // =========================================================================
147
- // TEST 3: Non-canonical ERC-20 winner cash-out is correct after fulfillment
148
- // =========================================================================
149
-
150
- /// @notice After the fix, the winner of a non-canonical-currency ERC-20 game receives the post-fee surplus
151
- /// (not the full pot). This confirms the fee was actually deducted.
152
- function test_currencyMismatchFix_winnerReceivesPostFeeSurplusNotFullPot() external {
153
- uint104 tierPrice = 100e6;
154
- _setupNonCanonicalGame(2, tierPrice);
155
-
156
- _toScoring();
157
-
158
- uint256 potBefore = _balance();
159
- uint256 expectedFee = (potBefore * 75_000_000) / JBConstants.SPLITS_TOTAL_PERCENT;
160
-
161
- // Tier 1 gets 100% of the cashout weight.
162
- DefifaTierCashOutWeight[] memory sc = new DefifaTierCashOutWeight[](2);
163
- sc[0] = DefifaTierCashOutWeight({id: 1, cashOutWeight: _nft.TOTAL_CASHOUT_WEIGHT()});
164
- sc[1] = DefifaTierCashOutWeight({id: 2, cashOutWeight: 0});
165
- _attestAndRatify(sc);
166
-
167
- // Fulfillment succeeded -- fee was deducted.
168
- assertEq(deployer.fulfilledCommitmentsOf(_pid), expectedFee, "fee was deducted");
169
-
170
- // Winner cashes out and receives the post-fee surplus.
171
- uint256 winnerBalBefore = usdc.balanceOf(_users[0]);
172
- _cashOutUsdc(_users[0], 1, 1);
173
- uint256 winnerReceived = usdc.balanceOf(_users[0]) - winnerBalBefore;
174
-
175
- // Before the fix, winner would have received the full pot (fee was skipped).
176
- // After the fix, winner receives pot minus fee.
177
- assertEq(winnerReceived, potBefore - expectedFee, "winner receives post-fee surplus, not the full pot");
178
- assertLt(winnerReceived, potBefore, "winner does NOT receive the full pot");
179
- }
180
-
181
- // =========================================================================
182
- // TEST 4: Launch rejects zero currency for non-native tokens
183
- // =========================================================================
184
-
185
- /// @notice Launching an ERC-20 game with currency=0 is rejected at launch time.
186
- function test_currencyMismatchFix_revertOnZeroCurrencyForErc20() external {
187
- DefifaTierParams[] memory tp = new DefifaTierParams[](2);
188
- for (uint256 i; i < 2; i++) {
189
- tp[i] = DefifaTierParams({
190
- reservedRate: 1001,
191
- reservedTokenBeneficiary: address(0),
192
- encodedIPFSUri: bytes32(0),
193
- shouldUseReservedTokenBeneficiaryAsDefault: false,
194
- name: "DEFIFA"
195
- });
196
- }
197
-
198
- DefifaLaunchProjectData memory d = DefifaLaunchProjectData({
199
- name: "DEFIFA_ZERO_CURRENCY",
200
- projectUri: "",
201
- contractUri: "",
202
- baseUri: "",
203
- token: JBAccountingContext({token: address(usdc), decimals: 6, currency: 0}),
204
- mintPeriodDuration: 1 days,
205
- start: uint48(block.timestamp + 3 days),
206
- refundPeriodDuration: 1 days,
207
- store: new JB721TiersHookStore(),
208
- splits: new JBSplit[](0),
209
- attestationStartTime: 0,
210
- attestationGracePeriod: 1 days,
211
- defaultAttestationDelegate: address(0),
212
- tierPrice: 100e6,
213
- tiers: tp,
214
- defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
215
- terminal: jbMultiTerminal(),
216
- minParticipation: 0,
217
- scorecardTimeout: 0,
218
- timelockDuration: 0
219
- });
220
-
221
- vm.expectRevert(abi.encodeWithSignature("DefifaDeployer_InvalidCurrency()"));
222
- deployer.launchGameWith(d);
223
- }
224
-
225
- /// @notice Native token (ETH) is exempt from the zero-currency check.
226
- function test_currencyMismatchFix_nativeTokenAllowsAnyCurrency() external {
227
- DefifaTierParams[] memory tp = new DefifaTierParams[](2);
228
- for (uint256 i; i < 2; i++) {
229
- tp[i] = DefifaTierParams({
230
- reservedRate: 1001,
231
- reservedTokenBeneficiary: address(0),
232
- encodedIPFSUri: bytes32(0),
233
- shouldUseReservedTokenBeneficiaryAsDefault: false,
234
- name: "DEFIFA"
235
- });
236
- }
237
-
238
- DefifaLaunchProjectData memory d = DefifaLaunchProjectData({
239
- name: "DEFIFA_ETH_LAUNCH",
240
- projectUri: "",
241
- contractUri: "",
242
- baseUri: "",
243
- token: JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH}),
244
- mintPeriodDuration: 1 days,
245
- start: uint48(block.timestamp + 3 days),
246
- refundPeriodDuration: 1 days,
247
- store: new JB721TiersHookStore(),
248
- splits: new JBSplit[](0),
249
- attestationStartTime: 0,
250
- attestationGracePeriod: 1 days,
251
- defaultAttestationDelegate: address(0),
252
- tierPrice: 1 ether,
253
- tiers: tp,
254
- defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
255
- terminal: jbMultiTerminal(),
256
- minParticipation: 0,
257
- scorecardTimeout: 0,
258
- timelockDuration: 0
259
- });
260
-
261
- // Should succeed without revert.
262
- uint256 gameId = deployer.launchGameWith(d);
263
- assertGt(gameId, 0, "ETH game launched successfully");
264
- }
265
- }