@ballkidz/defifa 0.0.24 → 0.0.25

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ballkidz/defifa",
3
- "version": "0.0.24",
3
+ "version": "0.0.25",
4
4
  "license": "MIT",
5
5
  "engines": {
6
6
  "node": ">=20.0.0"
@@ -13,13 +13,13 @@
13
13
  "url": "https://github.com/BallKidz/defifa-collection-deployer"
14
14
  },
15
15
  "dependencies": {
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",
16
+ "@bananapus/721-hook-v6": "^0.0.38",
17
+ "@bananapus/core-v6": "^0.0.36",
18
+ "@bananapus/permission-ids-v6": "^0.0.19",
19
+ "@croptop/core-v6": "^0.0.36",
20
20
  "@openzeppelin/contracts": "^5.6.1",
21
21
  "@prb/math": "^4.1.1",
22
- "@rev-net/core-v6": "^0.0.32",
22
+ "@rev-net/core-v6": "^0.0.35",
23
23
  "scripty.sol": "^2.1.1"
24
24
  },
25
25
  "devDependencies": {
@@ -0,0 +1,218 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ pragma solidity 0.8.28;
3
+
4
+ import {DefifaDeployer} from "../../src/DefifaDeployer.sol";
5
+ import {DefifaGamePhase} from "../../src/enums/DefifaGamePhase.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 {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
13
+ import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
14
+ import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
15
+ import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
16
+ import {JBCurrencyIds} from "@bananapus/core-v6/src/libraries/JBCurrencyIds.sol";
17
+ import {JBFundAccessLimitGroup} from "@bananapus/core-v6/src/structs/JBFundAccessLimitGroup.sol";
18
+ import {JBMultiTerminal} from "@bananapus/core-v6/src/JBMultiTerminal.sol";
19
+ import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
20
+ import {JBRulesetConfig} from "@bananapus/core-v6/src/structs/JBRulesetConfig.sol";
21
+ import {JBRulesetMetadata} from "@bananapus/core-v6/src/structs/JBRulesetMetadata.sol";
22
+ import {JBRulesetMetadataResolver} from "@bananapus/core-v6/src/libraries/JBRulesetMetadataResolver.sol";
23
+ import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
24
+ import {JBSplitGroup} from "@bananapus/core-v6/src/structs/JBSplitGroup.sol";
25
+ import {JBTerminalConfig} from "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
26
+ import {IJBRulesetApprovalHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetApprovalHook.sol";
27
+ import {JBTest} from "@bananapus/core-v6/test/helpers/JBTest.sol";
28
+ import {TestBaseWorkflow} from "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
29
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
30
+ import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
31
+ import {ITypeface} from "lib/typeface/contracts/interfaces/ITypeface.sol";
32
+
33
+ contract CodexNemesisOneTierZeroTimeoutLockVerifiedTest is JBTest, TestBaseWorkflow {
34
+ using JBRulesetMetadataResolver for JBRuleset;
35
+
36
+ uint256 internal constant GAME_ID = 3;
37
+
38
+ DefifaDeployer internal deployer;
39
+ DefifaGovernor internal governor;
40
+ DefifaHook internal hook;
41
+
42
+ uint256 internal projectId;
43
+ DefifaHook internal gameHook;
44
+
45
+ address internal projectOwner = address(bytes20(keccak256("projectOwner")));
46
+ address internal player = address(bytes20(keccak256("player")));
47
+
48
+ function setUp() public override {
49
+ super.setUp();
50
+
51
+ JBAccountingContext[] memory tokens = new JBAccountingContext[](1);
52
+ tokens[0] = JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH});
53
+
54
+ JBTerminalConfig[] memory terminalConfigs = new JBTerminalConfig[](1);
55
+ terminalConfigs[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: tokens});
56
+
57
+ JBRulesetConfig[] memory rulesetConfigs = new JBRulesetConfig[](1);
58
+ rulesetConfigs[0] = JBRulesetConfig({
59
+ mustStartAtOrAfter: 0,
60
+ duration: 10 days,
61
+ weight: 1e18,
62
+ weightCutPercent: 0,
63
+ approvalHook: IJBRulesetApprovalHook(address(0)),
64
+ metadata: JBRulesetMetadata({
65
+ reservedPercent: 0,
66
+ cashOutTaxRate: 0,
67
+ baseCurrency: JBCurrencyIds.ETH,
68
+ pausePay: false,
69
+ pauseCreditTransfers: false,
70
+ allowOwnerMinting: false,
71
+ allowSetCustomToken: false,
72
+ allowTerminalMigration: false,
73
+ allowSetTerminals: false,
74
+ allowSetController: false,
75
+ allowAddAccountingContext: false,
76
+ allowAddPriceFeed: false,
77
+ ownerMustSendPayouts: false,
78
+ holdFees: false,
79
+ useTotalSurplusForCashOuts: false,
80
+ useDataHookForPay: true,
81
+ useDataHookForCashOut: true,
82
+ dataHook: address(0),
83
+ metadata: 0
84
+ }),
85
+ splitGroups: new JBSplitGroup[](0),
86
+ fundAccessLimitGroups: new JBFundAccessLimitGroup[](0)
87
+ });
88
+
89
+ uint256 protocolFeeProjectId =
90
+ jbController().launchProjectFor(projectOwner, "", rulesetConfigs, terminalConfigs, "");
91
+ vm.prank(projectOwner);
92
+ address protocolFeeToken =
93
+ address(jbController().deployERC20For(protocolFeeProjectId, "Bananapus", "NANA", bytes32(0)));
94
+
95
+ uint256 defifaProjectId = jbController().launchProjectFor(projectOwner, "", rulesetConfigs, terminalConfigs, "");
96
+ vm.prank(projectOwner);
97
+ address defifaToken = address(jbController().deployERC20For(defifaProjectId, "Defifa", "DEFIFA", bytes32(0)));
98
+
99
+ hook = new DefifaHook(jbDirectory(), IERC20(defifaToken), IERC20(protocolFeeToken));
100
+ governor = new DefifaGovernor(jbController(), address(this));
101
+ deployer = new DefifaDeployer(
102
+ address(hook),
103
+ new DefifaTokenUriResolver(ITypeface(address(0))),
104
+ governor,
105
+ jbController(),
106
+ new JBAddressRegistry(),
107
+ defifaProjectId,
108
+ protocolFeeProjectId
109
+ );
110
+
111
+ hook.transferOwnership(address(deployer));
112
+ governor.transferOwnership(address(deployer));
113
+ }
114
+
115
+ function test_oneTier_zeroTimeout_permanentlyLocksGameInScoring() external {
116
+ projectId = deployer.launchGameWith(_launchData());
117
+
118
+ JBRuleset memory ruleset = jbRulesets().currentOf(projectId);
119
+ if (ruleset.dataHook() == address(0)) {
120
+ (ruleset,) = jbRulesets().latestQueuedOf(projectId);
121
+ }
122
+ gameHook = DefifaHook(ruleset.dataHook());
123
+
124
+ vm.warp(block.timestamp + 1 days);
125
+ _mintSingleTier(player, 1 ether);
126
+
127
+ vm.warp(block.timestamp + 3 days + 1);
128
+ assertEq(uint256(deployer.currentGamePhaseOf(projectId)), uint256(DefifaGamePhase.SCORING));
129
+
130
+ DefifaTierCashOutWeight[] memory scorecard = new DefifaTierCashOutWeight[](1);
131
+ scorecard[0] = DefifaTierCashOutWeight({id: 1, cashOutWeight: gameHook.TOTAL_CASHOUT_WEIGHT()});
132
+ uint256 scorecardId = governor.submitScorecardFor(GAME_ID, scorecard);
133
+
134
+ vm.warp(block.timestamp + 1);
135
+ vm.prank(player);
136
+ vm.expectRevert(DefifaGovernor.DefifaGovernor_NotAllowed.selector);
137
+ governor.attestToScorecardFrom(GAME_ID, scorecardId);
138
+
139
+ vm.warp(block.timestamp + 365 days);
140
+ assertEq(uint256(deployer.currentGamePhaseOf(projectId)), uint256(DefifaGamePhase.SCORING));
141
+
142
+ vm.expectRevert(DefifaDeployer.DefifaDeployer_NotNoContest.selector);
143
+ deployer.triggerNoContestFor(projectId);
144
+
145
+ bytes memory cashOutMetadata = _cashOutMetadata();
146
+ vm.prank(player);
147
+ vm.expectRevert(DefifaHook.DefifaHook_NothingToClaim.selector);
148
+ JBMultiTerminal(payable(address(jbMultiTerminal())))
149
+ .cashOutTokensOf({
150
+ holder: player,
151
+ projectId: projectId,
152
+ cashOutCount: 0,
153
+ tokenToReclaim: JBConstants.NATIVE_TOKEN,
154
+ minTokensReclaimed: 0,
155
+ beneficiary: payable(player),
156
+ metadata: cashOutMetadata
157
+ });
158
+ }
159
+
160
+ function _launchData() internal returns (DefifaLaunchProjectData memory) {
161
+ DefifaTierParams[] memory tiers = new DefifaTierParams[](1);
162
+ tiers[0] = DefifaTierParams({
163
+ reservedRate: 1001,
164
+ reservedTokenBeneficiary: address(0),
165
+ encodedIPFSUri: bytes32(0),
166
+ shouldUseReservedTokenBeneficiaryAsDefault: false,
167
+ name: "ONLY_TEAM"
168
+ });
169
+
170
+ return DefifaLaunchProjectData({
171
+ name: "ONE_TIER_LOCK",
172
+ projectUri: "",
173
+ contractUri: "",
174
+ baseUri: "",
175
+ tiers: tiers,
176
+ tierPrice: 1 ether,
177
+ token: JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH}),
178
+ mintPeriodDuration: 1 days,
179
+ refundPeriodDuration: 1 days,
180
+ start: uint48(block.timestamp + 3 days),
181
+ splits: new JBSplit[](0),
182
+ attestationStartTime: 0,
183
+ attestationGracePeriod: 100_381,
184
+ defaultAttestationDelegate: address(0),
185
+ defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
186
+ terminal: jbMultiTerminal(),
187
+ store: new JB721TiersHookStore(),
188
+ minParticipation: 0,
189
+ scorecardTimeout: 0,
190
+ timelockDuration: 0
191
+ });
192
+ }
193
+
194
+ function _mintSingleTier(address user, uint256 amount) internal {
195
+ vm.deal(user, amount);
196
+ uint16[] memory mintIds = new uint16[](1);
197
+ mintIds[0] = 1;
198
+ bytes[] memory data = new bytes[](1);
199
+ data[0] = abi.encode(user, mintIds);
200
+ bytes4[] memory ids = new bytes4[](1);
201
+ ids[0] = metadataHelper().getId("pay", address(hook));
202
+
203
+ vm.prank(user);
204
+ jbMultiTerminal().pay{value: amount}(
205
+ projectId, JBConstants.NATIVE_TOKEN, amount, user, 0, "", metadataHelper().createMetadata(ids, data)
206
+ );
207
+ }
208
+
209
+ function _cashOutMetadata() internal view returns (bytes memory) {
210
+ uint256[] memory tokenIds = new uint256[](1);
211
+ tokenIds[0] = 1_000_000_001;
212
+ bytes[] memory data = new bytes[](1);
213
+ data[0] = abi.encode(tokenIds);
214
+ bytes4[] memory ids = new bytes4[](1);
215
+ ids[0] = metadataHelper().getId("cashOut", address(hook));
216
+ return metadataHelper().createMetadata(ids, data);
217
+ }
218
+ }
@@ -0,0 +1,237 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ pragma solidity 0.8.28;
3
+
4
+ import {TestBaseWorkflow} from "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
5
+ import {JBTest} from "@bananapus/core-v6/test/helpers/JBTest.sol";
6
+
7
+ import {DefifaDeployer} from "../../src/DefifaDeployer.sol";
8
+ import {DefifaGovernor} from "../../src/DefifaGovernor.sol";
9
+ import {DefifaHook} from "../../src/DefifaHook.sol";
10
+ import {DefifaTokenUriResolver} from "../../src/DefifaTokenUriResolver.sol";
11
+ import {DefifaGamePhase} from "../../src/enums/DefifaGamePhase.sol";
12
+ import {DefifaTierCashOutWeight} from "../../src/structs/DefifaTierCashOutWeight.sol";
13
+ import {DefifaLaunchProjectData} from "../../src/structs/DefifaLaunchProjectData.sol";
14
+ import {DefifaTierParams} from "../../src/structs/DefifaTierParams.sol";
15
+ import {DefifaDelegation} from "../../src/structs/DefifaDelegation.sol";
16
+ import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
17
+ import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
18
+ import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
19
+ import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
20
+ import {JBCurrencyIds} from "@bananapus/core-v6/src/libraries/JBCurrencyIds.sol";
21
+ import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
22
+ import {JBTerminalConfig} from "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
23
+ import {JBRulesetConfig} from "@bananapus/core-v6/src/structs/JBRulesetConfig.sol";
24
+ import {JBRulesetMetadata} from "@bananapus/core-v6/src/structs/JBRulesetMetadata.sol";
25
+ import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
26
+ import {JBRulesetMetadataResolver} from "@bananapus/core-v6/src/libraries/JBRulesetMetadataResolver.sol";
27
+ import {JBSplitGroup} from "@bananapus/core-v6/src/structs/JBSplitGroup.sol";
28
+ import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
29
+ import {JBFundAccessLimitGroup} from "@bananapus/core-v6/src/structs/JBFundAccessLimitGroup.sol";
30
+ import {IJBRulesetApprovalHook} from "@bananapus/core-v6/src/interfaces/IJBRulesets.sol";
31
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
32
+ import {ITypeface} from "lib/typeface/contracts/interfaces/ITypeface.sol";
33
+
34
+ contract CodexNemesisSingleTierTimeoutLockTest is JBTest, TestBaseWorkflow {
35
+ using JBRulesetMetadataResolver for JBRuleset;
36
+
37
+ uint256 internal _protocolFeeProjectId;
38
+ uint256 internal _defifaProjectId;
39
+ uint256 internal _gameId = 3;
40
+
41
+ DefifaDeployer internal _deployer;
42
+ DefifaHook internal _hookImpl;
43
+ DefifaGovernor internal _governorImpl;
44
+
45
+ address internal _projectOwner = address(bytes20(keccak256("projectOwner")));
46
+ address internal _player = address(bytes20(keccak256("player")));
47
+
48
+ function setUp() public virtual override {
49
+ super.setUp();
50
+
51
+ JBAccountingContext[] memory tokens = new JBAccountingContext[](1);
52
+ tokens[0] = JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH});
53
+
54
+ JBTerminalConfig[] memory terminalConfigs = new JBTerminalConfig[](1);
55
+ terminalConfigs[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: tokens});
56
+
57
+ JBRulesetConfig[] memory rulesetConfigs = new JBRulesetConfig[](1);
58
+ rulesetConfigs[0] = JBRulesetConfig({
59
+ mustStartAtOrAfter: 0,
60
+ duration: 10 days,
61
+ weight: 1e18,
62
+ weightCutPercent: 0,
63
+ approvalHook: IJBRulesetApprovalHook(address(0)),
64
+ metadata: JBRulesetMetadata({
65
+ reservedPercent: 0,
66
+ cashOutTaxRate: 0,
67
+ baseCurrency: JBCurrencyIds.ETH,
68
+ pausePay: false,
69
+ pauseCreditTransfers: false,
70
+ allowOwnerMinting: false,
71
+ allowSetCustomToken: false,
72
+ allowTerminalMigration: false,
73
+ allowSetTerminals: false,
74
+ allowSetController: false,
75
+ allowAddAccountingContext: false,
76
+ allowAddPriceFeed: false,
77
+ ownerMustSendPayouts: false,
78
+ holdFees: false,
79
+ useTotalSurplusForCashOuts: false,
80
+ useDataHookForPay: true,
81
+ useDataHookForCashOut: true,
82
+ dataHook: address(0),
83
+ metadata: 0
84
+ }),
85
+ splitGroups: new JBSplitGroup[](0),
86
+ fundAccessLimitGroups: new JBFundAccessLimitGroup[](0)
87
+ });
88
+
89
+ _protocolFeeProjectId =
90
+ jbController().launchProjectFor(address(_projectOwner), "", rulesetConfigs, terminalConfigs, "");
91
+ vm.prank(_projectOwner);
92
+ address nanaToken =
93
+ address(jbController().deployERC20For(_protocolFeeProjectId, "Bananapus", "NANA", bytes32(0)));
94
+
95
+ _defifaProjectId =
96
+ jbController().launchProjectFor(address(_projectOwner), "", rulesetConfigs, terminalConfigs, "");
97
+ vm.prank(_projectOwner);
98
+ address defifaToken = address(jbController().deployERC20For(_defifaProjectId, "Defifa", "DEFIFA", bytes32(0)));
99
+
100
+ _hookImpl = new DefifaHook(jbDirectory(), IERC20(defifaToken), IERC20(nanaToken));
101
+ _governorImpl = new DefifaGovernor(jbController(), address(this));
102
+ _deployer = new DefifaDeployer(
103
+ address(_hookImpl),
104
+ new DefifaTokenUriResolver(ITypeface(address(0))),
105
+ _governorImpl,
106
+ jbController(),
107
+ new JBAddressRegistry(),
108
+ _defifaProjectId,
109
+ _protocolFeeProjectId
110
+ );
111
+
112
+ _hookImpl.transferOwnership(address(_deployer));
113
+ _governorImpl.transferOwnership(address(_deployer));
114
+ }
115
+
116
+ function test_singleTierGameWithZeroTimeoutLocksFunds() external {
117
+ DefifaLaunchProjectData memory data = _launchData();
118
+ uint256 projectId = _deployer.launchGameWith(data);
119
+ JBRuleset memory ruleset = jbRulesets().currentOf(projectId);
120
+ if (ruleset.dataHook() == address(0)) {
121
+ (ruleset,) = jbRulesets().latestQueuedOf(projectId);
122
+ }
123
+ DefifaHook hook = DefifaHook(ruleset.dataHook());
124
+
125
+ vm.warp(data.start - data.mintPeriodDuration - data.refundPeriodDuration);
126
+ vm.deal(_player, 1 ether);
127
+
128
+ uint16[] memory tierIds = new uint16[](1);
129
+ tierIds[0] = 1;
130
+ vm.prank(_player);
131
+ jbMultiTerminal().pay{value: 1 ether}(
132
+ projectId,
133
+ JBConstants.NATIVE_TOKEN,
134
+ 1 ether,
135
+ _player,
136
+ 0,
137
+ "",
138
+ _buildPayMetadata(abi.encode(_player, tierIds))
139
+ );
140
+
141
+ DefifaDelegation[] memory delegations = new DefifaDelegation[](1);
142
+ delegations[0] = DefifaDelegation({delegatee: _player, tierId: 1});
143
+ vm.prank(_player);
144
+ hook.setTierDelegatesTo(delegations);
145
+
146
+ vm.warp(data.start + 1);
147
+ assertEq(uint256(_deployer.currentGamePhaseOf(projectId)), uint256(DefifaGamePhase.SCORING));
148
+
149
+ DefifaTierCashOutWeight[] memory scorecard = new DefifaTierCashOutWeight[](1);
150
+ scorecard[0] = DefifaTierCashOutWeight({id: 1, cashOutWeight: hook.TOTAL_CASHOUT_WEIGHT()});
151
+ uint256 scorecardId = _governorImpl.submitScorecardFor(_gameId, scorecard);
152
+
153
+ assertEq(_governorImpl.quorum(_gameId), 500_000_000, "single tier quorum remains positive");
154
+ assertEq(
155
+ _governorImpl.getBWAAttestationWeight(_gameId, scorecardId, _player, uint48(block.timestamp)),
156
+ 0,
157
+ "sole beneficiary has zero BWA power"
158
+ );
159
+
160
+ vm.prank(_player);
161
+ vm.expectRevert(DefifaGovernor.DefifaGovernor_NotAllowed.selector);
162
+ _governorImpl.attestToScorecardFrom(_gameId, scorecardId);
163
+
164
+ vm.warp(block.timestamp + 365 days);
165
+ assertEq(
166
+ uint256(_deployer.currentGamePhaseOf(projectId)),
167
+ uint256(DefifaGamePhase.SCORING),
168
+ "timeout disabled keeps the game in scoring forever"
169
+ );
170
+
171
+ vm.expectRevert(DefifaDeployer.DefifaDeployer_NotNoContest.selector);
172
+ _deployer.triggerNoContestFor(projectId);
173
+
174
+ uint256[] memory tokenIds = new uint256[](1);
175
+ tokenIds[0] = _generateTokenId(1, 1);
176
+ bytes memory cashOutMetadata = _buildCashOutMetadata(tokenIds);
177
+
178
+ vm.prank(_player);
179
+ vm.expectRevert(DefifaHook.DefifaHook_NothingToClaim.selector);
180
+ jbMultiTerminal()
181
+ .cashOutTokensOf(_player, projectId, 0, JBConstants.NATIVE_TOKEN, 0, payable(_player), cashOutMetadata);
182
+ }
183
+
184
+ function _launchData() internal returns (DefifaLaunchProjectData memory) {
185
+ DefifaTierParams[] memory tierParams = new DefifaTierParams[](1);
186
+ tierParams[0] = DefifaTierParams({
187
+ reservedRate: 1001,
188
+ reservedTokenBeneficiary: address(0),
189
+ encodedIPFSUri: bytes32(0),
190
+ shouldUseReservedTokenBeneficiaryAsDefault: false,
191
+ name: "SOLE"
192
+ });
193
+
194
+ return DefifaLaunchProjectData({
195
+ name: "DEFIFA",
196
+ projectUri: "",
197
+ contractUri: "",
198
+ baseUri: "",
199
+ tiers: tierParams,
200
+ tierPrice: 1 ether,
201
+ token: JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH}),
202
+ mintPeriodDuration: 1 days,
203
+ refundPeriodDuration: 1 days,
204
+ start: uint48(block.timestamp + 3 days),
205
+ splits: new JBSplit[](0),
206
+ attestationStartTime: 0,
207
+ attestationGracePeriod: 100_381,
208
+ defaultAttestationDelegate: address(0),
209
+ defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
210
+ terminal: jbMultiTerminal(),
211
+ store: new JB721TiersHookStore(),
212
+ minParticipation: 0,
213
+ scorecardTimeout: 0,
214
+ timelockDuration: 0
215
+ });
216
+ }
217
+
218
+ function _buildPayMetadata(bytes memory decodedData) internal view returns (bytes memory) {
219
+ bytes4[] memory ids = new bytes4[](1);
220
+ ids[0] = metadataHelper().getId("pay", address(_hookImpl));
221
+ bytes[] memory datas = new bytes[](1);
222
+ datas[0] = decodedData;
223
+ return metadataHelper().createMetadata(ids, datas);
224
+ }
225
+
226
+ function _buildCashOutMetadata(uint256[] memory tokenIds) internal view returns (bytes memory) {
227
+ bytes4[] memory ids = new bytes4[](1);
228
+ ids[0] = metadataHelper().getId("cashOut", address(_hookImpl));
229
+ bytes[] memory datas = new bytes[](1);
230
+ datas[0] = abi.encode(tokenIds);
231
+ return metadataHelper().createMetadata(ids, datas);
232
+ }
233
+
234
+ function _generateTokenId(uint256 tierId, uint256 tokenNumber) internal pure returns (uint256) {
235
+ return (tierId * 1_000_000_000) + tokenNumber;
236
+ }
237
+ }