@ballkidz/defifa 0.0.25 → 0.0.27
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 +6 -2
- package/README.md +11 -2
- package/RISKS.md +3 -1
- package/STYLE_GUIDE.md +14 -11
- package/package.json +31 -14
- package/script/Deploy.s.sol +4 -1
- package/src/DefifaDeployer.sol +79 -47
- package/src/DefifaGovernor.sol +57 -12
- package/src/DefifaHook.sol +83 -26
- package/src/DefifaProjectOwner.sol +4 -3
- package/src/DefifaTokenUriResolver.sol +113 -20
- package/src/enums/DefifaGamePhase.sol +6 -0
- package/src/enums/DefifaScorecardState.sol +4 -0
- package/src/interfaces/IDefifaDeployer.sol +5 -0
- package/src/interfaces/IDefifaGamePhaseReporter.sol +4 -0
- package/src/interfaces/IDefifaGamePotReporter.sol +10 -0
- package/src/interfaces/IDefifaGovernor.sol +4 -0
- package/src/interfaces/IDefifaHook.sol +5 -0
- package/src/interfaces/IDefifaTokenUriResolver.sol +3 -0
- package/src/libraries/DefifaFontImporter.sol +1 -1
- package/src/libraries/DefifaHookLib.sol +9 -10
- package/src/structs/DefifaAttestations.sol +3 -2
- package/src/structs/DefifaDelegation.sol +1 -0
- package/src/structs/DefifaLaunchProjectData.sol +2 -3
- package/src/structs/DefifaOpsData.sol +1 -0
- package/src/structs/DefifaScorecard.sol +2 -0
- package/src/structs/DefifaTierCashOutWeight.sol +3 -1
- package/src/structs/DefifaTierParams.sol +1 -0
- package/CRYPTO_ECON.pdf +0 -0
- package/CRYPTO_ECON.tex +0 -997
- package/foundry.lock +0 -17
- package/references/operations.md +0 -32
- package/references/runtime.md +0 -43
- package/slither-ci.config.json +0 -10
- package/sphinx.lock +0 -521
- package/test/BWAFunctionComparison.t.sol +0 -1320
- package/test/DefifaAdversarialQuorum.t.sol +0 -617
- package/test/DefifaAuditLowGuards.t.sol +0 -308
- package/test/DefifaFeeAccounting.t.sol +0 -581
- package/test/DefifaGovernanceHardening.t.sol +0 -1315
- package/test/DefifaGovernor.t.sol +0 -1378
- package/test/DefifaHookRegressions.t.sol +0 -415
- package/test/DefifaMintCostInvariant.t.sol +0 -319
- package/test/DefifaNoContest.t.sol +0 -941
- package/test/DefifaSecurity.t.sol +0 -741
- package/test/DefifaUSDC.t.sol +0 -480
- package/test/Fork.t.sol +0 -2388
- package/test/TestAuditGaps.sol +0 -984
- package/test/TestQALastMile.t.sol +0 -514
- package/test/audit/AttestationDoubleCount.t.sol +0 -218
- package/test/audit/CodexNemesisCurrencyMismatchBypass.t.sol +0 -112
- package/test/audit/CodexNemesisNoContestReserveDrain.t.sol +0 -238
- package/test/audit/CodexNemesisOneTierZeroTimeoutLockVerified.t.sol +0 -218
- package/test/audit/CodexNemesisSingleTierTimeoutLock.t.sol +0 -237
- package/test/audit/CodexRegistryMismatch.t.sol +0 -191
- package/test/audit/CodexTierCapMismatch.t.sol +0 -171
- package/test/audit/CurrencyMismatchFix.t.sol +0 -265
- package/test/audit/FixPendingReserveDilution.t.sol +0 -366
- package/test/audit/H5TierCapValidation.t.sol +0 -184
- package/test/audit/PendingReserveDilution.t.sol +0 -298
- package/test/audit/PendingReserveQuorumGrief.t.sol +0 -355
- package/test/audit/PendingReserveSnapshotBypass.t.sol +0 -319
- package/test/regression/AttestationDelegateBeneficiary.t.sol +0 -271
- package/test/regression/FulfillmentBlocksRatification.t.sol +0 -279
- package/test/regression/GracePeriodBypass.t.sol +0 -302
|
@@ -1,1378 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: UNLICENSED
|
|
2
|
-
pragma solidity 0.8.28;
|
|
3
|
-
|
|
4
|
-
import {TestBaseWorkflow} from "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
|
|
5
|
-
|
|
6
|
-
import {DefifaGovernor} from "../src/DefifaGovernor.sol";
|
|
7
|
-
import {DefifaDeployer} from "../src/DefifaDeployer.sol";
|
|
8
|
-
import {DefifaHook} from "../src/DefifaHook.sol";
|
|
9
|
-
import {DefifaTokenUriResolver} from "../src/DefifaTokenUriResolver.sol";
|
|
10
|
-
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
11
|
-
|
|
12
|
-
import {JBTest} from "@bananapus/core-v6/test/helpers/JBTest.sol";
|
|
13
|
-
import {JBRulesetMetadataResolver} from "@bananapus/core-v6/src/libraries/JBRulesetMetadataResolver.sol";
|
|
14
|
-
import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
|
|
15
|
-
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
16
|
-
|
|
17
|
-
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
18
|
-
import {ITypeface} from "lib/typeface/contracts/interfaces/ITypeface.sol";
|
|
19
|
-
import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
|
|
20
|
-
import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
|
|
21
|
-
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
|
|
22
|
-
import {DefifaDelegation} from "../src/structs/DefifaDelegation.sol";
|
|
23
|
-
import {DefifaLaunchProjectData} from "../src/structs/DefifaLaunchProjectData.sol";
|
|
24
|
-
import {DefifaTierParams} from "../src/structs/DefifaTierParams.sol";
|
|
25
|
-
import {DefifaTierCashOutWeight} from "../src/structs/DefifaTierCashOutWeight.sol";
|
|
26
|
-
import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
27
|
-
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
28
|
-
import {JBCurrencyIds} from "@bananapus/core-v6/src/libraries/JBCurrencyIds.sol";
|
|
29
|
-
import {JBFundAccessLimitGroup} from "@bananapus/core-v6/src/structs/JBFundAccessLimitGroup.sol";
|
|
30
|
-
import {JBMultiTerminal} from "@bananapus/core-v6/src/JBMultiTerminal.sol";
|
|
31
|
-
import {JBRulesetConfig, JBTerminalConfig} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
32
|
-
import {JBRulesetMetadata} from "@bananapus/core-v6/src/structs/JBRulesetMetadata.sol";
|
|
33
|
-
import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
34
|
-
import {JBSplitGroup} from "@bananapus/core-v6/src/structs/JBSplitGroup.sol";
|
|
35
|
-
import {IJBRulesetApprovalHook} from "@bananapus/core-v6/src/interfaces/IJBRulesets.sol";
|
|
36
|
-
import {JBTerminalStore} from "@bananapus/core-v6/src/JBTerminalStore.sol";
|
|
37
|
-
|
|
38
|
-
/// @dev Helper to read block.timestamp via an external call, bypassing the via-ir optimizer's timestamp caching.
|
|
39
|
-
contract TimestampReader {
|
|
40
|
-
function timestamp() external view returns (uint256) {
|
|
41
|
-
return block.timestamp;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
contract DefifaGovernorTest is JBTest, TestBaseWorkflow {
|
|
46
|
-
using JBRulesetMetadataResolver for JBRuleset;
|
|
47
|
-
|
|
48
|
-
TimestampReader private _tsReader = new TimestampReader();
|
|
49
|
-
|
|
50
|
-
address _protocolFeeProjectTokenAccount;
|
|
51
|
-
address _defifaProjectTokenAccount;
|
|
52
|
-
|
|
53
|
-
uint256 _protocolFeeProjectId;
|
|
54
|
-
uint256 _defifaProjectId;
|
|
55
|
-
address _owner = 0x1000000000000000000000000000000000000000;
|
|
56
|
-
uint256 _gameId = 3;
|
|
57
|
-
|
|
58
|
-
DefifaDeployer deployer;
|
|
59
|
-
DefifaHook hook;
|
|
60
|
-
DefifaGovernor governor;
|
|
61
|
-
|
|
62
|
-
address projectOwner = address(bytes20(keccak256("projectOwner")));
|
|
63
|
-
|
|
64
|
-
function setUp() public virtual override {
|
|
65
|
-
super.setUp();
|
|
66
|
-
|
|
67
|
-
// Terminal configurations.
|
|
68
|
-
JBAccountingContext[] memory _tokens = new JBAccountingContext[](1);
|
|
69
|
-
_tokens[0] = JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH});
|
|
70
|
-
|
|
71
|
-
JBTerminalConfig[] memory terminalConfigs = new JBTerminalConfig[](1);
|
|
72
|
-
terminalConfigs[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: _tokens});
|
|
73
|
-
|
|
74
|
-
JBRulesetConfig[] memory rulesetConfigs = new JBRulesetConfig[](1);
|
|
75
|
-
rulesetConfigs[0] = JBRulesetConfig({
|
|
76
|
-
mustStartAtOrAfter: 0,
|
|
77
|
-
duration: 10 days,
|
|
78
|
-
weight: 1e18,
|
|
79
|
-
weightCutPercent: 0,
|
|
80
|
-
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
81
|
-
metadata: JBRulesetMetadata({
|
|
82
|
-
reservedPercent: 0,
|
|
83
|
-
cashOutTaxRate: 0,
|
|
84
|
-
baseCurrency: JBCurrencyIds.ETH,
|
|
85
|
-
pausePay: false,
|
|
86
|
-
pauseCreditTransfers: false,
|
|
87
|
-
allowOwnerMinting: false,
|
|
88
|
-
allowSetCustomToken: false,
|
|
89
|
-
allowTerminalMigration: false,
|
|
90
|
-
allowSetTerminals: false,
|
|
91
|
-
allowSetController: false,
|
|
92
|
-
allowAddAccountingContext: false,
|
|
93
|
-
allowAddPriceFeed: false,
|
|
94
|
-
ownerMustSendPayouts: false,
|
|
95
|
-
holdFees: false,
|
|
96
|
-
useTotalSurplusForCashOuts: false,
|
|
97
|
-
useDataHookForPay: true,
|
|
98
|
-
useDataHookForCashOut: true,
|
|
99
|
-
dataHook: address(0),
|
|
100
|
-
metadata: 0
|
|
101
|
-
}),
|
|
102
|
-
splitGroups: new JBSplitGroup[](0),
|
|
103
|
-
fundAccessLimitGroups: new JBFundAccessLimitGroup[](0)
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// Launch the NANA fee project.
|
|
107
|
-
_protocolFeeProjectId =
|
|
108
|
-
jbController().launchProjectFor(address(projectOwner), "", rulesetConfigs, terminalConfigs, "");
|
|
109
|
-
vm.prank(projectOwner);
|
|
110
|
-
_protocolFeeProjectTokenAccount =
|
|
111
|
-
address(jbController().deployERC20For(_protocolFeeProjectId, "Bananapus", "NANA", bytes32(0)));
|
|
112
|
-
|
|
113
|
-
// Launch the Defifa fee project.
|
|
114
|
-
_defifaProjectId =
|
|
115
|
-
jbController().launchProjectFor(address(projectOwner), "", rulesetConfigs, terminalConfigs, "");
|
|
116
|
-
vm.prank(projectOwner);
|
|
117
|
-
_defifaProjectTokenAccount =
|
|
118
|
-
address(jbController().deployERC20For(_defifaProjectId, "Defifa", "DEFIFA", bytes32(0)));
|
|
119
|
-
|
|
120
|
-
hook = new DefifaHook(
|
|
121
|
-
jbDirectory(), IERC20(address(_defifaProjectTokenAccount)), IERC20(_protocolFeeProjectTokenAccount)
|
|
122
|
-
);
|
|
123
|
-
governor = new DefifaGovernor(jbController(), address(this));
|
|
124
|
-
JBAddressRegistry _registry = new JBAddressRegistry();
|
|
125
|
-
DefifaTokenUriResolver _tokenUriResolver = new DefifaTokenUriResolver(ITypeface(address(0)));
|
|
126
|
-
deployer = new DefifaDeployer(
|
|
127
|
-
address(hook),
|
|
128
|
-
_tokenUriResolver,
|
|
129
|
-
governor,
|
|
130
|
-
jbController(),
|
|
131
|
-
_registry,
|
|
132
|
-
_defifaProjectId,
|
|
133
|
-
_protocolFeeProjectId
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
// Transfer ownership of the hook to the deployer.
|
|
137
|
-
hook.transferOwnership(address(deployer));
|
|
138
|
-
// Transfer ownership of the governor to the deployer.
|
|
139
|
-
governor.transferOwnership(address(deployer));
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function testReceiveVotingPower(uint8 nTiers, uint8 tier) public {
|
|
143
|
-
vm.assume(nTiers < 100);
|
|
144
|
-
vm.assume(nTiers >= tier);
|
|
145
|
-
vm.assume(tier != 0);
|
|
146
|
-
address _user = address(bytes20(keccak256("user")));
|
|
147
|
-
DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData(nTiers);
|
|
148
|
-
(uint256 _projectId, DefifaHook _nft, DefifaGovernor _governor) = createDefifaProject(defifaData);
|
|
149
|
-
|
|
150
|
-
// Phase 1: Mint
|
|
151
|
-
vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
152
|
-
//deployer.queueNextPhaseOf(_projectId);
|
|
153
|
-
// User should have no voting power (yet)
|
|
154
|
-
assertEq(_governor.getAttestationWeight(_gameId, _user, uint48(block.timestamp)), 0);
|
|
155
|
-
// fund user
|
|
156
|
-
vm.deal(_user, 1 ether);
|
|
157
|
-
// Build metadata to buy specific NFT
|
|
158
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
159
|
-
vm.assume(tier != 0);
|
|
160
|
-
rawMetadata[0] = uint16(tier); // reward tier
|
|
161
|
-
|
|
162
|
-
// Pay to the project and mint an NFT
|
|
163
|
-
vm.prank(_user);
|
|
164
|
-
jbMultiTerminal().pay{value: 1 ether}(
|
|
165
|
-
_projectId,
|
|
166
|
-
JBConstants.NATIVE_TOKEN,
|
|
167
|
-
1 ether,
|
|
168
|
-
_user,
|
|
169
|
-
0,
|
|
170
|
-
"",
|
|
171
|
-
_buildPayMetadata(abi.encode(_user, rawMetadata))
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
// The user should now have a balance
|
|
175
|
-
assertEq(_nft.balanceOf(_user), 1);
|
|
176
|
-
|
|
177
|
-
// Forward 1 block, user should receive all the voting power of the tier, as its the only NFT
|
|
178
|
-
vm.warp(block.timestamp + 1);
|
|
179
|
-
|
|
180
|
-
assertEq(_nft.store().tierOf(address(_nft), tier, false).votingUnits, 1 ether);
|
|
181
|
-
assertEq(
|
|
182
|
-
_governor.MAX_ATTESTATION_POWER_TIER(),
|
|
183
|
-
_governor.getAttestationWeight(_gameId, _user, uint48(block.timestamp))
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// cashOuts can happen after mint phase
|
|
188
|
-
// function testRefund_fails_afterMintPhase() external {
|
|
189
|
-
// uint8 nTiers = 10;
|
|
190
|
-
// address[] memory _users = new address[](nTiers);
|
|
191
|
-
// DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData(nTiers);
|
|
192
|
-
// (uint256 _projectId, , ) = createDefifaProject(defifaData);
|
|
193
|
-
// // Phase 1: Mint
|
|
194
|
-
// vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
195
|
-
// //deployer.queueNextPhaseOf(_projectId);
|
|
196
|
-
// for (uint256 i = 0; i < nTiers; i++) {
|
|
197
|
-
// // Generate a new address for each tier
|
|
198
|
-
// _users[i] = address(bytes20(keccak256(abi.encode('user', Strings.toString(i)))));
|
|
199
|
-
// // fund user
|
|
200
|
-
// vm.deal(_users[i], 1 ether);
|
|
201
|
-
// // Build metadata to buy specific NFT
|
|
202
|
-
// uint16[] memory rawMetadata = new uint16[](1);
|
|
203
|
-
// rawMetadata[0] = uint16(i + 1); // reward tier, 1 indexed
|
|
204
|
-
// bytes memory metadata = abi.encode(
|
|
205
|
-
// bytes32(0),
|
|
206
|
-
// bytes32(0),
|
|
207
|
-
// type(IDefifaHook).interfaceId,
|
|
208
|
-
// _users[i],
|
|
209
|
-
// rawMetadata
|
|
210
|
-
// );
|
|
211
|
-
// // Pay to the project and mint an NFT
|
|
212
|
-
// vm.prank(_users[i]);
|
|
213
|
-
// jbMultiTerminal().pay{value: 1 ether}(
|
|
214
|
-
// _projectId,
|
|
215
|
-
// 1 ether,
|
|
216
|
-
// address(0),
|
|
217
|
-
// _users[i],
|
|
218
|
-
// 0,
|
|
219
|
-
// true,
|
|
220
|
-
// '',
|
|
221
|
-
// metadata
|
|
222
|
-
// );
|
|
223
|
-
// }
|
|
224
|
-
// // Phase 2: Redeem
|
|
225
|
-
// vm.warp(block.timestamp + defifaData.mintPeriodDuration);
|
|
226
|
-
// //deployer.queueNextPhaseOf(_projectId);
|
|
227
|
-
// // Phase 3: Start
|
|
228
|
-
// vm.warp(defifaData.start + 1);
|
|
229
|
-
// //deployer.queueNextPhaseOf(_projectId);
|
|
230
|
-
// // Make sure this is actually Phase 3
|
|
231
|
-
// assertEq(jbRulesets().currentOf(_projectId).number, 3);
|
|
232
|
-
// for (uint256 i = 0; i < _users.length; i++) {
|
|
233
|
-
// address _user = _users[i];
|
|
234
|
-
// // Craft the metadata: redeem the tokenId
|
|
235
|
-
// bytes memory cashOutMetadata;
|
|
236
|
-
// {
|
|
237
|
-
// uint256[] memory cashOutId = new uint256[](1);
|
|
238
|
-
// cashOutId[0] = _generateTokenId(i + 1, 1);
|
|
239
|
-
// cashOutMetadata = _buildCashOutMetadata(abi.encode(cashOutId);
|
|
240
|
-
// }
|
|
241
|
-
// vm.expectRevert(abi.encodeWithSignature('FUNDING_CYCLE_REDEEM_PAUSED()'));
|
|
242
|
-
// vm.prank(_user);
|
|
243
|
-
// JBMultiTerminal(address(jbMultiTerminal())).redeemTokensOf({
|
|
244
|
-
// _holder: _user,
|
|
245
|
-
// _projectId: _projectId,
|
|
246
|
-
// _tokenCount: 0,
|
|
247
|
-
// _token: address(0),
|
|
248
|
-
// _minReturnedTokens: 0,
|
|
249
|
-
// _beneficiary: payable(_user),
|
|
250
|
-
// _memo: 'Refund plz',
|
|
251
|
-
// _metadata: cashOutMetadata
|
|
252
|
-
// });
|
|
253
|
-
// }
|
|
254
|
-
// // // Phase 4: End
|
|
255
|
-
// // vm.warp(deployer.endOf(_projectId));
|
|
256
|
-
// // Forward the amount of blocks needed to reach the end (and round up)
|
|
257
|
-
// // vm.roll(deployer.endOf(_projectId) - block.timestamp / 12 + 1);
|
|
258
|
-
// vm.warp(block.timestamp + 1 weeks);
|
|
259
|
-
// assertEq(jbRulesets().currentOf(_projectId).number, 4);
|
|
260
|
-
// for (uint256 i = 0; i < _users.length; i++) {
|
|
261
|
-
// address _user = _users[i];
|
|
262
|
-
// // Craft the metadata: redeem the tokenId
|
|
263
|
-
// bytes memory cashOutMetadata;
|
|
264
|
-
// {
|
|
265
|
-
// uint256[] memory cashOutId = new uint256[](1);
|
|
266
|
-
// cashOutId[0] = _generateTokenId(i + 1, 1);
|
|
267
|
-
// cashOutMetadata = _buildCashOutMetadata(abi.encode(cashOutId);
|
|
268
|
-
// }
|
|
269
|
-
// // Here the refunds are not allowed but cashOuts are,
|
|
270
|
-
// // so it should instead revert with an error showing that there is no cashOut set for our tier
|
|
271
|
-
// vm.expectRevert(abi.encodeWithSignature('NOTHING_TO_CLAIM()'));
|
|
272
|
-
// vm.prank(_user);
|
|
273
|
-
// JBMultiTerminal(address(jbMultiTerminal())).redeemTokensOf({
|
|
274
|
-
// _holder: _user,
|
|
275
|
-
// _projectId: _projectId,
|
|
276
|
-
// _tokenCount: 0,
|
|
277
|
-
// _token: address(0),
|
|
278
|
-
// _minReturnedTokens: 0,
|
|
279
|
-
// _beneficiary: payable(_user),
|
|
280
|
-
// _memo: 'Refund plz',
|
|
281
|
-
// _metadata: cashOutMetadata
|
|
282
|
-
// });
|
|
283
|
-
// }
|
|
284
|
-
// }
|
|
285
|
-
|
|
286
|
-
function testMint_fails_afterMintPhase() external {
|
|
287
|
-
uint8 nTiers = 10;
|
|
288
|
-
address[] memory _users = new address[](nTiers);
|
|
289
|
-
DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData(nTiers);
|
|
290
|
-
(uint256 _projectId,,) = createDefifaProject(defifaData);
|
|
291
|
-
// Phase 1: minting
|
|
292
|
-
vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
293
|
-
//deployer.queueNextPhaseOf(_projectId);
|
|
294
|
-
// Phase 2: Redeem
|
|
295
|
-
vm.warp(block.timestamp + defifaData.mintPeriodDuration);
|
|
296
|
-
//deployer.queueNextPhaseOf(_projectId);
|
|
297
|
-
// Make sure this is actually Phase 2
|
|
298
|
-
assertEq(jbRulesets().currentOf(_projectId).cycleNumber, 2);
|
|
299
|
-
|
|
300
|
-
for (uint256 i = 0; i < nTiers; i++) {
|
|
301
|
-
// Generate a new address for each tier
|
|
302
|
-
_users[i] = address(bytes20(keccak256(abi.encode("user", Strings.toString(i)))));
|
|
303
|
-
// fund user
|
|
304
|
-
vm.deal(_users[i], 1 ether);
|
|
305
|
-
// Build metadata to buy specific NFT
|
|
306
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
307
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
308
|
-
rawMetadata[0] = uint16(i + 1); // reward tier, 1 indexed
|
|
309
|
-
bytes memory metadata = _buildPayMetadata(abi.encode(_users[i], rawMetadata));
|
|
310
|
-
// Pay to the project and mint an NFT
|
|
311
|
-
vm.expectRevert(JBTerminalStore.JBTerminalStore_RulesetPaymentPaused.selector);
|
|
312
|
-
vm.prank(_users[i]);
|
|
313
|
-
jbMultiTerminal().pay{value: 1 ether}(
|
|
314
|
-
_projectId, JBConstants.NATIVE_TOKEN, 1 ether, _users[i], 0, "", metadata
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
for (uint256 i = 0; i < nTiers; i++) {
|
|
318
|
-
// Generate a new address for each tier
|
|
319
|
-
_users[i] = address(bytes20(keccak256(abi.encode("user", Strings.toString(i)))));
|
|
320
|
-
// fund user
|
|
321
|
-
vm.deal(_users[i], 1 ether);
|
|
322
|
-
// Build metadata to buy specific NFT
|
|
323
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
324
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
325
|
-
rawMetadata[0] = uint16(i + 1); // reward tier, 1 indexed
|
|
326
|
-
bytes memory metadata = _buildPayMetadata(abi.encode(_users[i], rawMetadata));
|
|
327
|
-
// Pay to the project and mint an NFT
|
|
328
|
-
vm.expectRevert(JBTerminalStore.JBTerminalStore_RulesetPaymentPaused.selector);
|
|
329
|
-
vm.prank(_users[i]);
|
|
330
|
-
jbMultiTerminal().pay{value: 1 ether}(
|
|
331
|
-
_projectId, JBConstants.NATIVE_TOKEN, 1 ether, _users[i], 0, "", metadata
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Transfers are no longer disabled
|
|
337
|
-
// function testTransfer_fails_afterTradeDeadline() external {
|
|
338
|
-
// uint8 nTiers = 10;
|
|
339
|
-
// address[] memory _users = new address[](nTiers);
|
|
340
|
-
// DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData();
|
|
341
|
-
// (uint256 _projectId, DefifaHook _nft, ) = createDefifaProject(
|
|
342
|
-
// uint256(nTiers),
|
|
343
|
-
// getBasicDefifaLaunchData()
|
|
344
|
-
// );
|
|
345
|
-
// // Phase 1: minting
|
|
346
|
-
// vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
347
|
-
// for (uint256 i = 0; i < nTiers; i++) {
|
|
348
|
-
// // Generate a new address for each tier
|
|
349
|
-
// _users[i] = address(bytes20(keccak256(abi.encode('user', Strings.toString(i)))));
|
|
350
|
-
// // fund user
|
|
351
|
-
// vm.deal(_users[i], 1 ether);
|
|
352
|
-
// // Build metadata to buy specific NFT
|
|
353
|
-
// uint16[] memory rawMetadata = new uint16[](1);
|
|
354
|
-
// rawMetadata[0] = uint16(i + 1); // reward tier, 1 indexed
|
|
355
|
-
// bytes memory metadata = abi.encode(
|
|
356
|
-
// bytes32(0),
|
|
357
|
-
// bytes32(0),
|
|
358
|
-
// type(IDefifaHook).interfaceId,
|
|
359
|
-
// false,
|
|
360
|
-
// false,
|
|
361
|
-
// false,
|
|
362
|
-
// rawMetadata
|
|
363
|
-
// );
|
|
364
|
-
// // Pay to the project and mint an NFT
|
|
365
|
-
// vm.prank(_users[i]);
|
|
366
|
-
// jbMultiTerminal().pay{value: 1 ether}(
|
|
367
|
-
// _projectId,
|
|
368
|
-
// 1 ether,
|
|
369
|
-
// address(0),
|
|
370
|
-
// _users[i],
|
|
371
|
-
// 0,
|
|
372
|
-
// true,
|
|
373
|
-
// '',
|
|
374
|
-
// metadata
|
|
375
|
-
// );
|
|
376
|
-
// }
|
|
377
|
-
// // Phase 2: Redeem
|
|
378
|
-
// vm.warp(block.timestamp + defifaData.mintPeriodDuration);
|
|
379
|
-
// //deployer.queueNextPhaseOf(_projectId);
|
|
380
|
-
// // Make sure this is actually Phase 2
|
|
381
|
-
// assertEq(jbRulesets().currentOf(_projectId).number, 2);
|
|
382
|
-
// // Phase 3: Start
|
|
383
|
-
// vm.warp(defifaData.start + 1);
|
|
384
|
-
// //deployer.queueNextPhaseOf(_projectId);
|
|
385
|
-
// // Make sure this is actually Phase 3
|
|
386
|
-
// assertEq(jbRulesets().currentOf(_projectId).number, 3);
|
|
387
|
-
// uint256 _tokenIdToTransfer = _generateTokenId(1, 1);
|
|
388
|
-
// vm.prank(_users[0]);
|
|
389
|
-
// // trasnfers not possible in phase 3
|
|
390
|
-
// vm.expectRevert(abi.encodeWithSignature('TRANSFERS_PAUSED()'));
|
|
391
|
-
// _nft.transferFrom(_users[0], _users[1], _tokenIdToTransfer);
|
|
392
|
-
// }
|
|
393
|
-
function testSetCashOutRates_fails_unmetQuorum() external {
|
|
394
|
-
uint8 nTiers = 10;
|
|
395
|
-
address[] memory _users = new address[](nTiers);
|
|
396
|
-
DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData(nTiers);
|
|
397
|
-
(uint256 _projectId, DefifaHook _nft, DefifaGovernor _governor) = createDefifaProject(defifaData);
|
|
398
|
-
// Phase 1: minting
|
|
399
|
-
vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
400
|
-
//deployer.queueNextPhaseOf(_projectId);
|
|
401
|
-
for (uint256 i = 0; i < nTiers; i++) {
|
|
402
|
-
// Generate a new address for each tier
|
|
403
|
-
_users[i] = address(bytes20(keccak256(abi.encode("user", Strings.toString(i)))));
|
|
404
|
-
// fund user
|
|
405
|
-
vm.deal(_users[i], 1 ether);
|
|
406
|
-
// Build metadata to buy specific NFT
|
|
407
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
408
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
409
|
-
rawMetadata[0] = uint16(i + 1); // reward tier, 1 indexed
|
|
410
|
-
bytes memory metadata = _buildPayMetadata(abi.encode(_users[i], rawMetadata));
|
|
411
|
-
// Pay to the project and mint an NFT
|
|
412
|
-
vm.prank(_users[i]);
|
|
413
|
-
jbMultiTerminal().pay{value: 1 ether}(
|
|
414
|
-
_projectId, JBConstants.NATIVE_TOKEN, 1 ether, _users[i], 0, "", metadata
|
|
415
|
-
);
|
|
416
|
-
// Set the delegate as the user themselves
|
|
417
|
-
DefifaDelegation[] memory tiered721SetDelegatesData = new DefifaDelegation[](1);
|
|
418
|
-
tiered721SetDelegatesData[0] = DefifaDelegation({delegatee: _users[i], tierId: uint256(i + 1)});
|
|
419
|
-
vm.prank(_users[i]);
|
|
420
|
-
_nft.setTierDelegatesTo(tiered721SetDelegatesData);
|
|
421
|
-
// Forward 1 block, user should receive all the voting power of the tier, as its the only NFT
|
|
422
|
-
vm.warp(_tsReader.timestamp() + 1);
|
|
423
|
-
assertEq(
|
|
424
|
-
_governor.MAX_ATTESTATION_POWER_TIER(),
|
|
425
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
426
|
-
_governor.getAttestationWeight(_gameId, _users[i], uint48(_tsReader.timestamp()))
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
// Warp to scoring phase (past start time)
|
|
430
|
-
vm.warp(defifaData.start + 1);
|
|
431
|
-
// Generate the scorecards
|
|
432
|
-
DefifaTierCashOutWeight[] memory scorecards = new DefifaTierCashOutWeight[](nTiers);
|
|
433
|
-
// We can't have a neutral outcome, so we only give shares to tiers that are an even number (in our array)
|
|
434
|
-
for (uint256 i = 0; i < scorecards.length; i++) {
|
|
435
|
-
scorecards[i].id = i + 1;
|
|
436
|
-
scorecards[i].cashOutWeight = i % 2 == 0 ? 1e18 / (scorecards.length / 2) : 0;
|
|
437
|
-
}
|
|
438
|
-
// Forward time so proposals can be created
|
|
439
|
-
uint256 _proposalId = _governor.submitScorecardFor(_gameId, scorecards);
|
|
440
|
-
// Forward time so voting becomes active
|
|
441
|
-
vm.warp(_tsReader.timestamp() + _governor.attestationStartTimeOf(_gameId) + 1);
|
|
442
|
-
// We have only 40% vote on the proposal, making it still be below quorum.
|
|
443
|
-
for (uint256 i = 0; i < _users.length * 4 / 10; i++) {
|
|
444
|
-
vm.prank(_users[i]);
|
|
445
|
-
_governor.attestToScorecardFrom(_gameId, _proposalId);
|
|
446
|
-
}
|
|
447
|
-
// Forward the amount of blocks needed to reach the end (and round up)
|
|
448
|
-
vm.warp(_tsReader.timestamp() + _governor.attestationGracePeriodOf(_gameId) + 1);
|
|
449
|
-
// Execute the proposal
|
|
450
|
-
vm.expectRevert(DefifaGovernor.DefifaGovernor_NotAllowed.selector);
|
|
451
|
-
_governor.ratifyScorecardFrom(_gameId, scorecards);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function testSetCashOutRatesAndRedeem_multipleTiers(uint8 nTiers, uint8[] calldata distribution) public {
|
|
455
|
-
nTiers = uint8(bound(uint256(nTiers), 11, 99));
|
|
456
|
-
vm.assume(distribution.length > 0 && distribution.length < nTiers);
|
|
457
|
-
|
|
458
|
-
uint256 _sumDistribution;
|
|
459
|
-
for (uint256 i = 0; i < distribution.length; i++) {
|
|
460
|
-
_sumDistribution += distribution[i];
|
|
461
|
-
}
|
|
462
|
-
vm.assume(_sumDistribution > 0);
|
|
463
|
-
address[] memory _users = new address[](nTiers);
|
|
464
|
-
DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData(nTiers);
|
|
465
|
-
(uint256 _projectId, DefifaHook _nft, DefifaGovernor _governor) = createDefifaProject(defifaData);
|
|
466
|
-
|
|
467
|
-
// Phase 1: minting
|
|
468
|
-
vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
469
|
-
for (uint256 i = 0; i < nTiers; i++) {
|
|
470
|
-
// Generate a new address for each tier
|
|
471
|
-
_users[i] = address(bytes20(keccak256(abi.encode("user", Strings.toString(i)))));
|
|
472
|
-
// fund user
|
|
473
|
-
vm.deal(_users[i], 1 ether);
|
|
474
|
-
|
|
475
|
-
// Build metadata to buy specific NFT
|
|
476
|
-
bytes memory metadata;
|
|
477
|
-
{
|
|
478
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
479
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
480
|
-
rawMetadata[0] = uint16(i + 1); // reward tier, 1 indexed
|
|
481
|
-
metadata = _buildPayMetadata(abi.encode(_users[i], rawMetadata));
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// Pay to the project and mint an NFT
|
|
485
|
-
vm.prank(_users[i]);
|
|
486
|
-
jbMultiTerminal().pay{value: 1 ether}(
|
|
487
|
-
_projectId, JBConstants.NATIVE_TOKEN, 1 ether, _users[i], 0, "", metadata
|
|
488
|
-
);
|
|
489
|
-
// Forward 1 block, user should receive all the voting power of the tier, as its the only NFT
|
|
490
|
-
vm.warp(block.timestamp + 1);
|
|
491
|
-
assertEq(
|
|
492
|
-
_governor.MAX_ATTESTATION_POWER_TIER(),
|
|
493
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
494
|
-
_governor.getAttestationWeight(_gameId, _users[i], uint48(block.timestamp))
|
|
495
|
-
);
|
|
496
|
-
// Have a user mint and refund the tier
|
|
497
|
-
mintAndRefund(_nft, _projectId, i + 1);
|
|
498
|
-
}
|
|
499
|
-
// Have a user mint and refund the tier
|
|
500
|
-
mintAndRefund(_nft, _projectId, 1);
|
|
501
|
-
|
|
502
|
-
// Warp to scoring phase (past start time)
|
|
503
|
-
vm.warp(defifaData.start + 1);
|
|
504
|
-
// Generate the scorecards — must sum to exactly TOTAL_CASHOUT_WEIGHT
|
|
505
|
-
DefifaTierCashOutWeight[] memory scorecards = new DefifaTierCashOutWeight[](nTiers);
|
|
506
|
-
uint256 assignedCashOutWeight;
|
|
507
|
-
// We can't have a neutral outcome, so we only give shares to tiers that are an even number (in our array)
|
|
508
|
-
for (uint256 i = 0; i < scorecards.length; i++) {
|
|
509
|
-
scorecards[i].id = i + 1;
|
|
510
|
-
if (distribution.length <= i) continue;
|
|
511
|
-
scorecards[i].cashOutWeight = (uint256(distribution[i]) * _nft.TOTAL_CASHOUT_WEIGHT()) / _sumDistribution;
|
|
512
|
-
assignedCashOutWeight += scorecards[i].cashOutWeight;
|
|
513
|
-
}
|
|
514
|
-
// Absorb rounding remainder into first tier with weight
|
|
515
|
-
if (assignedCashOutWeight < _nft.TOTAL_CASHOUT_WEIGHT()) {
|
|
516
|
-
uint256 remainder = _nft.TOTAL_CASHOUT_WEIGHT() - assignedCashOutWeight;
|
|
517
|
-
for (uint256 i = 0; i < scorecards.length; i++) {
|
|
518
|
-
if (scorecards[i].cashOutWeight > 0) {
|
|
519
|
-
scorecards[i].cashOutWeight += remainder;
|
|
520
|
-
break;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
// Forward time so proposals can be created
|
|
525
|
-
uint256 _proposalId = _governor.submitScorecardFor(_gameId, scorecards);
|
|
526
|
-
// Forward time so voting becomes active
|
|
527
|
-
vm.warp(block.timestamp + _governor.attestationStartTimeOf(_gameId) + 1);
|
|
528
|
-
// No voting delay after the initial voting delay has passed in
|
|
529
|
-
//assertEq(_governor.attestationStartTimeOf(_gameId), 0);
|
|
530
|
-
// All the users vote
|
|
531
|
-
// 0 = Against
|
|
532
|
-
// 1 = For
|
|
533
|
-
// 2 = Abstain
|
|
534
|
-
// BWA may reduce beneficiaries' power to zero; skip those gracefully.
|
|
535
|
-
for (uint256 i = 0; i < _users.length; i++) {
|
|
536
|
-
vm.prank(_users[i]);
|
|
537
|
-
try _governor.attestToScorecardFrom(_gameId, _proposalId) {} catch {}
|
|
538
|
-
}
|
|
539
|
-
// each block is of 12 secs
|
|
540
|
-
vm.warp(block.timestamp + _governor.attestationGracePeriodOf(_gameId));
|
|
541
|
-
|
|
542
|
-
_governor.ratifyScorecardFrom(_gameId, scorecards);
|
|
543
|
-
// Move forward 1 block to start the new ruleset.
|
|
544
|
-
vm.roll(block.number + 1);
|
|
545
|
-
|
|
546
|
-
_verifyCashOutsAndRedeem(
|
|
547
|
-
_projectId, _nft, scorecards, _users, _sumDistribution, distribution, assignedCashOutWeight
|
|
548
|
-
);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
function _verifyCashOutsAndRedeem(
|
|
552
|
-
uint256 _projectId,
|
|
553
|
-
DefifaHook _nft,
|
|
554
|
-
DefifaTierCashOutWeight[] memory scorecards,
|
|
555
|
-
address[] memory _users,
|
|
556
|
-
uint256 _sumDistribution,
|
|
557
|
-
uint8[] calldata distribution,
|
|
558
|
-
uint256 assignedCashOutWeight
|
|
559
|
-
)
|
|
560
|
-
internal
|
|
561
|
-
{
|
|
562
|
-
uint256 _pot = jbMultiTerminal().currentSurplusOf(_projectId, new address[](0), 18, JBCurrencyIds.ETH);
|
|
563
|
-
// Assert that the deployer did *NOT* receive any fee tokens.
|
|
564
|
-
assertEq(IERC20(_protocolFeeProjectTokenAccount).balanceOf(address(deployer)), 0);
|
|
565
|
-
assertEq(IERC20(_defifaProjectTokenAccount).balanceOf(address(deployer)), 0);
|
|
566
|
-
|
|
567
|
-
// Verify that the cashOutWeights actually changed
|
|
568
|
-
for (uint256 i = 0; i < scorecards.length; i++) {
|
|
569
|
-
_verifySingleCashOut(_projectId, _nft, scorecards[i], _users[i], _pot, _sumDistribution, distribution, i);
|
|
570
|
-
}
|
|
571
|
-
// All NFTs should have been redeemed, only some dust should be left
|
|
572
|
-
uint256 remainingSurplus =
|
|
573
|
-
jbMultiTerminal().currentSurplusOf(_projectId, new address[](0), 18, JBCurrencyIds.ETH);
|
|
574
|
-
uint256 _expected = _pot * (_nft.TOTAL_CASHOUT_WEIGHT() - assignedCashOutWeight) / _nft.TOTAL_CASHOUT_WEIGHT();
|
|
575
|
-
assertApproxEqAbs(remainingSurplus, _expected, 10 ** 14);
|
|
576
|
-
|
|
577
|
-
// There should be no fee tokens left in the hook.
|
|
578
|
-
assertEq(IERC20(_protocolFeeProjectTokenAccount).balanceOf(address(_nft)), 0);
|
|
579
|
-
assertEq(IERC20(_defifaProjectTokenAccount).balanceOf(address(_nft)), 0);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
function _verifySingleCashOut(
|
|
583
|
-
uint256 _projectId,
|
|
584
|
-
DefifaHook _nft,
|
|
585
|
-
DefifaTierCashOutWeight memory scorecard,
|
|
586
|
-
address _user,
|
|
587
|
-
uint256 _pot,
|
|
588
|
-
uint256 _sumDistribution,
|
|
589
|
-
uint8[] calldata distribution,
|
|
590
|
-
uint256 i
|
|
591
|
-
)
|
|
592
|
-
internal
|
|
593
|
-
{
|
|
594
|
-
assertEq(_nft.tierCashOutWeights()[i], scorecard.cashOutWeight);
|
|
595
|
-
|
|
596
|
-
bytes memory cashOutMetadata;
|
|
597
|
-
uint256 _receiveDefifa;
|
|
598
|
-
uint256 _receiveNana;
|
|
599
|
-
{
|
|
600
|
-
uint256[] memory cashOutId = new uint256[](1);
|
|
601
|
-
cashOutId[0] = _generateTokenId(i + 1, 1);
|
|
602
|
-
cashOutMetadata = _buildCashOutMetadata(abi.encode(cashOutId));
|
|
603
|
-
(_receiveDefifa, _receiveNana) = _nft.tokensClaimableFor(cashOutId);
|
|
604
|
-
}
|
|
605
|
-
uint256 _nanaBalance = IERC20(_protocolFeeProjectTokenAccount).balanceOf(_user);
|
|
606
|
-
uint256 _defifaBalance = IERC20(_defifaProjectTokenAccount).balanceOf(_user);
|
|
607
|
-
|
|
608
|
-
vm.prank(_user);
|
|
609
|
-
JBMultiTerminal(address(jbMultiTerminal()))
|
|
610
|
-
.cashOutTokensOf({
|
|
611
|
-
holder: _user,
|
|
612
|
-
projectId: _projectId,
|
|
613
|
-
cashOutCount: 0,
|
|
614
|
-
tokenToReclaim: JBConstants.NATIVE_TOKEN,
|
|
615
|
-
minTokensReclaimed: 0,
|
|
616
|
-
beneficiary: payable(_user),
|
|
617
|
-
metadata: cashOutMetadata
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
assertEq(IERC20(_protocolFeeProjectTokenAccount).balanceOf(_user), _nanaBalance + _receiveNana);
|
|
621
|
-
assertEq(IERC20(_defifaProjectTokenAccount).balanceOf(_user), _defifaBalance + _receiveDefifa);
|
|
622
|
-
|
|
623
|
-
if (scorecard.cashOutWeight == 0) return;
|
|
624
|
-
|
|
625
|
-
uint256 _expectedTierCashOut = _pot;
|
|
626
|
-
if (distribution.length > i) {
|
|
627
|
-
_expectedTierCashOut = (_expectedTierCashOut * distribution[i]) / _sumDistribution;
|
|
628
|
-
}
|
|
629
|
-
assertApproxEqRel(_expectedTierCashOut, _user.balance, 0.001 ether);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
function testVotingPowerDecreasesAfterRefund() public {
|
|
633
|
-
uint256 nOfOtherTiers = 31;
|
|
634
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
635
|
-
DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData(uint8(nOfOtherTiers + 1));
|
|
636
|
-
(uint256 _projectId, DefifaHook _hook, DefifaGovernor _governor) = createDefifaProject(defifaData);
|
|
637
|
-
|
|
638
|
-
// Phase 1: minting
|
|
639
|
-
vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
640
|
-
//deployer.queueNextPhaseOf(_projectId);
|
|
641
|
-
|
|
642
|
-
JB721Tier memory _tier = _hook.store().tierOf(address(_hook), 1, false);
|
|
643
|
-
uint256 _cost = _tier.price;
|
|
644
|
-
|
|
645
|
-
address _refundUser = address(bytes20(keccak256("refund_user")));
|
|
646
|
-
// The user should have no balance
|
|
647
|
-
assertEq(_hook.balanceOf(_refundUser), 0);
|
|
648
|
-
// Build metadata to buy specific NFT
|
|
649
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
650
|
-
rawMetadata[0] = uint16(1); // reward tier, 1 indexed
|
|
651
|
-
bytes memory metadata = _buildPayMetadata(abi.encode(_refundUser, rawMetadata));
|
|
652
|
-
// Pay to the project and mint an NFT
|
|
653
|
-
vm.deal(_refundUser, _cost);
|
|
654
|
-
|
|
655
|
-
vm.prank(_refundUser);
|
|
656
|
-
jbMultiTerminal().pay{value: _cost}(_projectId, JBConstants.NATIVE_TOKEN, _cost, _refundUser, 0, "", metadata);
|
|
657
|
-
|
|
658
|
-
vm.warp(block.timestamp + 1);
|
|
659
|
-
|
|
660
|
-
assertEq(
|
|
661
|
-
_governor.MAX_ATTESTATION_POWER_TIER(),
|
|
662
|
-
_governor.getAttestationWeight(_gameId, _refundUser, uint48(block.timestamp))
|
|
663
|
-
);
|
|
664
|
-
|
|
665
|
-
// User should no longer have any funds
|
|
666
|
-
assertEq(_refundUser.balance, 0);
|
|
667
|
-
// The user should have have a token
|
|
668
|
-
assertEq(_hook.balanceOf(_refundUser), 1);
|
|
669
|
-
|
|
670
|
-
uint256 _numberBurned = _hook.store().numberOfBurnedFor(address(_hook), 1);
|
|
671
|
-
// Craft the metadata: redeem the tokenId
|
|
672
|
-
bytes memory cashOutMetadata;
|
|
673
|
-
{
|
|
674
|
-
uint256[] memory cashOutId = new uint256[](1);
|
|
675
|
-
cashOutId[0] = _generateTokenId(1, _tier.initialSupply - _tier.remainingSupply + 1 + _numberBurned);
|
|
676
|
-
cashOutMetadata = _buildCashOutMetadata(abi.encode(cashOutId));
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
vm.prank(_refundUser);
|
|
680
|
-
JBMultiTerminal(address(jbMultiTerminal()))
|
|
681
|
-
.cashOutTokensOf({
|
|
682
|
-
holder: _refundUser,
|
|
683
|
-
projectId: _projectId,
|
|
684
|
-
cashOutCount: 0,
|
|
685
|
-
tokenToReclaim: JBConstants.NATIVE_TOKEN,
|
|
686
|
-
minTokensReclaimed: 0,
|
|
687
|
-
beneficiary: payable(_refundUser),
|
|
688
|
-
metadata: cashOutMetadata
|
|
689
|
-
});
|
|
690
|
-
vm.warp(block.timestamp + 1);
|
|
691
|
-
|
|
692
|
-
assertEq(_refundUser.balance, _cost);
|
|
693
|
-
assertEq(_hook.balanceOf(_refundUser), 0);
|
|
694
|
-
|
|
695
|
-
assertEq(0, _governor.getAttestationWeight(_gameId, _refundUser, uint48(block.timestamp)));
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
function testRevertsIfDelegationisDoneAfterMintPhase(
|
|
699
|
-
uint8 nUsersWithWinningTier,
|
|
700
|
-
uint8 winningTierExtraWeight,
|
|
701
|
-
uint8 baseCashOutWeight
|
|
702
|
-
)
|
|
703
|
-
public
|
|
704
|
-
{
|
|
705
|
-
uint256 nOfOtherTiers = 31;
|
|
706
|
-
vm.assume(nUsersWithWinningTier > 1 && nUsersWithWinningTier < 100);
|
|
707
|
-
uint256 totalWeight = baseCashOutWeight * (nOfOtherTiers + 1) + winningTierExtraWeight;
|
|
708
|
-
vm.assume(totalWeight > 1);
|
|
709
|
-
|
|
710
|
-
address[] memory _users = new address[](nOfOtherTiers + nUsersWithWinningTier);
|
|
711
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
712
|
-
DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData(uint8(nOfOtherTiers + 1));
|
|
713
|
-
(uint256 _projectId, DefifaHook _nft, DefifaGovernor _governor) = createDefifaProject(defifaData);
|
|
714
|
-
// Phase 1: minting
|
|
715
|
-
vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
716
|
-
//deployer.queueNextPhaseOf(_projectId);
|
|
717
|
-
|
|
718
|
-
for (uint256 i = 0; i < nOfOtherTiers + nUsersWithWinningTier; i++) {
|
|
719
|
-
// Generate a new address for each tier
|
|
720
|
-
_users[i] = address(bytes20(keccak256(abi.encode("user", Strings.toString(i)))));
|
|
721
|
-
// fund user
|
|
722
|
-
vm.deal(_users[i], 1 ether);
|
|
723
|
-
if (i < nOfOtherTiers) {
|
|
724
|
-
// Build metadata to buy specific NFT
|
|
725
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
726
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
727
|
-
rawMetadata[0] = uint16(i + 1); // reward tier, 1 indexed
|
|
728
|
-
bytes memory metadata = _buildPayMetadata(abi.encode(_users[i], rawMetadata));
|
|
729
|
-
// Pay to the project and mint an NFT
|
|
730
|
-
vm.prank(_users[i]);
|
|
731
|
-
jbMultiTerminal().pay{value: 1 ether}(
|
|
732
|
-
_projectId, JBConstants.NATIVE_TOKEN, 1 ether, _users[i], 0, "", metadata
|
|
733
|
-
);
|
|
734
|
-
// Forward 1 block, user should receive all the voting power of the tier, as its the only NFT
|
|
735
|
-
vm.warp(block.timestamp + 1);
|
|
736
|
-
assertEq(
|
|
737
|
-
_governor.MAX_ATTESTATION_POWER_TIER(),
|
|
738
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
739
|
-
_governor.getAttestationWeight(_gameId, _users[i], uint48(block.timestamp))
|
|
740
|
-
);
|
|
741
|
-
} else {
|
|
742
|
-
// Build metadata to buy specific NFT
|
|
743
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
744
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
745
|
-
rawMetadata[0] = uint16(nOfOtherTiers + 1); // reward tier, 1 indexed
|
|
746
|
-
bytes memory metadata = _buildPayMetadata(abi.encode(_users[i], rawMetadata));
|
|
747
|
-
// Pay to the project and mint an NFT
|
|
748
|
-
vm.prank(_users[i]);
|
|
749
|
-
jbMultiTerminal().pay{value: 1 ether}(
|
|
750
|
-
_projectId, JBConstants.NATIVE_TOKEN, 1 ether, _users[i], 0, "", metadata
|
|
751
|
-
);
|
|
752
|
-
// Forward 1 block, user should have a part of the voting power of their tier
|
|
753
|
-
vm.warp(block.timestamp + 1);
|
|
754
|
-
assertEq(
|
|
755
|
-
_governor.MAX_ATTESTATION_POWER_TIER() / (i - nOfOtherTiers + 1),
|
|
756
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
757
|
-
_governor.getAttestationWeight(_gameId, _users[i], uint48(block.timestamp))
|
|
758
|
-
);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
// Phase 2: Redeem
|
|
762
|
-
vm.warp(block.timestamp + defifaData.mintPeriodDuration);
|
|
763
|
-
//deployer.queueNextPhaseOf(_projectId);
|
|
764
|
-
|
|
765
|
-
vm.prank(_users[0]);
|
|
766
|
-
vm.expectRevert(abi.encodeWithSignature("DefifaHook_DelegateChangesUnavailableInThisPhase()"));
|
|
767
|
-
_nft.setTierDelegateTo(_users[1], 1);
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
function testSetCashOutRatesAndRedeem_singleTier(
|
|
771
|
-
uint8 nUsersWithWinningTier,
|
|
772
|
-
uint8 winningTierExtraWeight,
|
|
773
|
-
uint8 baseCashOutWeight
|
|
774
|
-
)
|
|
775
|
-
public
|
|
776
|
-
{
|
|
777
|
-
uint256 nOfOtherTiers = 31;
|
|
778
|
-
vm.assume(nUsersWithWinningTier > 1 && nUsersWithWinningTier < 100);
|
|
779
|
-
uint256 totalWeight = baseCashOutWeight * (nOfOtherTiers + 1) + winningTierExtraWeight;
|
|
780
|
-
vm.assume(totalWeight > 1);
|
|
781
|
-
|
|
782
|
-
address[] memory _users = new address[](nOfOtherTiers + nUsersWithWinningTier);
|
|
783
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
784
|
-
DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData(uint8(nOfOtherTiers + 1));
|
|
785
|
-
(uint256 _projectId, DefifaHook _nft, DefifaGovernor _governor) = createDefifaProject(defifaData);
|
|
786
|
-
// Phase 1: minting
|
|
787
|
-
vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
788
|
-
//deployer.queueNextPhaseOf(_projectId);
|
|
789
|
-
|
|
790
|
-
for (uint256 i = 0; i < nOfOtherTiers + nUsersWithWinningTier; i++) {
|
|
791
|
-
// Generate a new address for each tier
|
|
792
|
-
_users[i] = address(bytes20(keccak256(abi.encode("user", Strings.toString(i)))));
|
|
793
|
-
// fund user
|
|
794
|
-
vm.deal(_users[i], 1 ether);
|
|
795
|
-
if (i < nOfOtherTiers) {
|
|
796
|
-
// Build metadata to buy specific NFT
|
|
797
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
798
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
799
|
-
rawMetadata[0] = uint16(i + 1); // reward tier, 1 indexed
|
|
800
|
-
bytes memory metadata = _buildPayMetadata(abi.encode(_users[i], rawMetadata));
|
|
801
|
-
// Pay to the project and mint an NFT
|
|
802
|
-
vm.prank(_users[i]);
|
|
803
|
-
jbMultiTerminal().pay{value: 1 ether}(
|
|
804
|
-
_projectId, JBConstants.NATIVE_TOKEN, 1 ether, _users[i], 0, "", metadata
|
|
805
|
-
);
|
|
806
|
-
// Forward 1 block, user should receive all the voting power of the tier, as its the only NFT
|
|
807
|
-
vm.warp(block.timestamp + 1);
|
|
808
|
-
assertEq(
|
|
809
|
-
_governor.MAX_ATTESTATION_POWER_TIER(),
|
|
810
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
811
|
-
_governor.getAttestationWeight(_gameId, _users[i], uint48(block.timestamp))
|
|
812
|
-
);
|
|
813
|
-
} else {
|
|
814
|
-
// Build metadata to buy specific NFT
|
|
815
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
816
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
817
|
-
rawMetadata[0] = uint16(nOfOtherTiers + 1); // reward tier, 1 indexed
|
|
818
|
-
bytes memory metadata = _buildPayMetadata(abi.encode(_users[i], rawMetadata));
|
|
819
|
-
// Pay to the project and mint an NFT
|
|
820
|
-
vm.prank(_users[i]);
|
|
821
|
-
jbMultiTerminal().pay{value: 1 ether}(
|
|
822
|
-
_projectId, JBConstants.NATIVE_TOKEN, 1 ether, _users[i], 0, "", metadata
|
|
823
|
-
);
|
|
824
|
-
// Forward 1 block, user should have a part of the voting power of their tier
|
|
825
|
-
vm.warp(block.timestamp + 1);
|
|
826
|
-
assertEq(
|
|
827
|
-
_governor.MAX_ATTESTATION_POWER_TIER() / (i - nOfOtherTiers + 1),
|
|
828
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
829
|
-
_governor.getAttestationWeight(_gameId, _users[i], uint48(block.timestamp))
|
|
830
|
-
);
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
// Have a user mint and refund the tier
|
|
834
|
-
mintAndRefund(_nft, _projectId, 1);
|
|
835
|
-
// Warp to scoring phase (past start time)
|
|
836
|
-
vm.warp(defifaData.start + 1);
|
|
837
|
-
// Generate the scorecards
|
|
838
|
-
DefifaTierCashOutWeight[] memory scorecards = new DefifaTierCashOutWeight[](nOfOtherTiers + 1);
|
|
839
|
-
|
|
840
|
-
uint256 totalCashOutWeight = _nft.TOTAL_CASHOUT_WEIGHT();
|
|
841
|
-
|
|
842
|
-
// We can't have a neutral outcome, so we only give shares to tiers that are an even number (in our array)
|
|
843
|
-
uint256 assignedCashOutWeight;
|
|
844
|
-
for (uint256 i = 0; i < scorecards.length; i++) {
|
|
845
|
-
scorecards[i].id = i + 1;
|
|
846
|
-
if (baseCashOutWeight != 0) {
|
|
847
|
-
scorecards[i].cashOutWeight = (totalCashOutWeight * uint256(baseCashOutWeight)) / totalWeight;
|
|
848
|
-
}
|
|
849
|
-
if (i == nOfOtherTiers && winningTierExtraWeight != 0) {
|
|
850
|
-
scorecards[i].cashOutWeight += (totalCashOutWeight * uint256(winningTierExtraWeight)) / totalWeight;
|
|
851
|
-
}
|
|
852
|
-
assignedCashOutWeight += scorecards[i].cashOutWeight;
|
|
853
|
-
}
|
|
854
|
-
// Absorb rounding remainder into first tier with weight
|
|
855
|
-
if (assignedCashOutWeight < totalCashOutWeight) {
|
|
856
|
-
uint256 remainder = totalCashOutWeight - assignedCashOutWeight;
|
|
857
|
-
for (uint256 i = 0; i < scorecards.length; i++) {
|
|
858
|
-
if (scorecards[i].cashOutWeight > 0) {
|
|
859
|
-
scorecards[i].cashOutWeight += remainder;
|
|
860
|
-
break;
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
{
|
|
865
|
-
// Forward time so proposals can be created
|
|
866
|
-
uint256 _proposalId = _governor.submitScorecardFor(_gameId, scorecards);
|
|
867
|
-
// Forward time so voting becomes active
|
|
868
|
-
vm.warp(block.timestamp + _governor.attestationStartTimeOf(_gameId) + 1);
|
|
869
|
-
// No voting delay after the initial voting delay has passed in
|
|
870
|
-
// assertEq(_governor.attestationStartTimeOf(_gameId), 0);
|
|
871
|
-
// All the users vote
|
|
872
|
-
// 0 = Against
|
|
873
|
-
// 1 = For
|
|
874
|
-
// 2 = Abstain
|
|
875
|
-
// BWA may reduce beneficiaries' power to zero; skip those gracefully.
|
|
876
|
-
for (uint256 i = 0; i < _users.length; i++) {
|
|
877
|
-
vm.prank(_users[i]);
|
|
878
|
-
try _governor.attestToScorecardFrom(_gameId, _proposalId) {} catch {}
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
// Forward the amount of blocks needed to reach the end (and round up)
|
|
883
|
-
vm.warp(block.timestamp + _governor.attestationGracePeriodOf(_gameId));
|
|
884
|
-
|
|
885
|
-
_governor.ratifyScorecardFrom(_gameId, scorecards);
|
|
886
|
-
vm.warp(block.timestamp + 1);
|
|
887
|
-
|
|
888
|
-
uint256 _pot = jbMultiTerminal().currentSurplusOf(_projectId, new address[](0), 18, JBCurrencyIds.ETH);
|
|
889
|
-
|
|
890
|
-
// Verify that the cashOutWeights actually changed
|
|
891
|
-
for (uint256 i = 0; i < _users.length; i++) {
|
|
892
|
-
address _user = _users[i];
|
|
893
|
-
uint256 _tier = i <= nOfOtherTiers ? i + 1 : nOfOtherTiers + 1;
|
|
894
|
-
// Craft the metadata: redeem the tokenId
|
|
895
|
-
bytes memory cashOutMetadata;
|
|
896
|
-
{
|
|
897
|
-
uint256[] memory cashOutId = new uint256[](1);
|
|
898
|
-
cashOutId[0] = _generateTokenId(_tier, _tier == nOfOtherTiers + 1 ? i - nOfOtherTiers + 1 : 1);
|
|
899
|
-
cashOutMetadata = _buildCashOutMetadata(abi.encode(cashOutId));
|
|
900
|
-
}
|
|
901
|
-
uint256 _expectedTierCashOut;
|
|
902
|
-
{
|
|
903
|
-
// Calculate how much weight his tier has
|
|
904
|
-
uint256 _tierWeight = _tier == nOfOtherTiers + 1
|
|
905
|
-
? uint256(baseCashOutWeight) + uint256(winningTierExtraWeight)
|
|
906
|
-
: baseCashOutWeight;
|
|
907
|
-
|
|
908
|
-
// If the cashOut is 0 this will revert
|
|
909
|
-
vm.prank(_user);
|
|
910
|
-
JBMultiTerminal(address(jbMultiTerminal()))
|
|
911
|
-
.cashOutTokensOf({
|
|
912
|
-
holder: _user,
|
|
913
|
-
projectId: _projectId,
|
|
914
|
-
cashOutCount: 0,
|
|
915
|
-
tokenToReclaim: JBConstants.NATIVE_TOKEN,
|
|
916
|
-
minTokensReclaimed: 0,
|
|
917
|
-
beneficiary: payable(_user),
|
|
918
|
-
metadata: cashOutMetadata
|
|
919
|
-
});
|
|
920
|
-
// We calculate the expected output based on the given distribution and how much is in the pot
|
|
921
|
-
_expectedTierCashOut = (_pot * _tierWeight) / totalWeight;
|
|
922
|
-
}
|
|
923
|
-
{
|
|
924
|
-
// If this is the winning tier then the amount is divided among the nUsersWithWinningTier
|
|
925
|
-
if (_tier == nOfOtherTiers + 1) {
|
|
926
|
-
_expectedTierCashOut = _expectedTierCashOut / nUsersWithWinningTier;
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
// Assert that our expected tier cashOut is ~equal to the actual amount
|
|
930
|
-
// Allowing for some rounding errors, max allowed error is 0.000001 ether
|
|
931
|
-
assertApproxEqRel(_expectedTierCashOut, _user.balance, 0.0001 ether);
|
|
932
|
-
}
|
|
933
|
-
// All NFTs should have been redeemed, only some dust should be left
|
|
934
|
-
// Max allowed dust is 0.0001
|
|
935
|
-
uint256 remainingSurplus =
|
|
936
|
-
jbMultiTerminal().currentSurplusOf(_projectId, new address[](0), 18, JBCurrencyIds.ETH);
|
|
937
|
-
assertApproxEqAbs(
|
|
938
|
-
remainingSurplus, _pot * (totalCashOutWeight - assignedCashOutWeight) / totalCashOutWeight, 10 ** 14
|
|
939
|
-
);
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
function testPhaseTimes(
|
|
943
|
-
uint16 _durationUntilProjectLaunch,
|
|
944
|
-
uint16 _mintPeriodDuration,
|
|
945
|
-
uint16 _inBetweenMintAndFifa,
|
|
946
|
-
uint16 _fifaDuration
|
|
947
|
-
)
|
|
948
|
-
public
|
|
949
|
-
{
|
|
950
|
-
vm.assume(
|
|
951
|
-
_durationUntilProjectLaunch > 2 && _mintPeriodDuration > 1 && _inBetweenMintAndFifa > 1 && _fifaDuration > 1
|
|
952
|
-
);
|
|
953
|
-
uint48 _launchProjectAt = uint48(block.timestamp) + _durationUntilProjectLaunch;
|
|
954
|
-
DefifaTierParams[] memory tierParams = new DefifaTierParams[](1);
|
|
955
|
-
tierParams[0] = DefifaTierParams({
|
|
956
|
-
reservedRate: 1001,
|
|
957
|
-
reservedTokenBeneficiary: address(0),
|
|
958
|
-
encodedIPFSUri: bytes32(0), // this way we dont need more tokenUris
|
|
959
|
-
shouldUseReservedTokenBeneficiaryAsDefault: false,
|
|
960
|
-
name: "DEFIFA"
|
|
961
|
-
});
|
|
962
|
-
|
|
963
|
-
DefifaLaunchProjectData memory _launchData = DefifaLaunchProjectData({
|
|
964
|
-
name: "DEFIFA",
|
|
965
|
-
projectUri: "",
|
|
966
|
-
contractUri: "",
|
|
967
|
-
baseUri: "",
|
|
968
|
-
tierPrice: 1 ether,
|
|
969
|
-
token: JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH}),
|
|
970
|
-
mintPeriodDuration: _mintPeriodDuration,
|
|
971
|
-
start: _launchProjectAt + uint48(_mintPeriodDuration) + _inBetweenMintAndFifa,
|
|
972
|
-
refundPeriodDuration: _inBetweenMintAndFifa,
|
|
973
|
-
store: new JB721TiersHookStore(),
|
|
974
|
-
splits: new JBSplit[](0),
|
|
975
|
-
attestationStartTime: 0,
|
|
976
|
-
attestationGracePeriod: 100_381,
|
|
977
|
-
defaultAttestationDelegate: address(0),
|
|
978
|
-
tiers: tierParams,
|
|
979
|
-
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
980
|
-
terminal: jbMultiTerminal(),
|
|
981
|
-
minParticipation: 0,
|
|
982
|
-
scorecardTimeout: 0,
|
|
983
|
-
timelockDuration: 0
|
|
984
|
-
});
|
|
985
|
-
(uint256 _projectId, DefifaHook _nft,) = createDefifaProject(_launchData);
|
|
986
|
-
// Wait until the phase 1 start
|
|
987
|
-
vm.warp(_launchProjectAt);
|
|
988
|
-
// Get the hook
|
|
989
|
-
_nft = DefifaHook(jbRulesets().currentOf(_projectId).dataHook());
|
|
990
|
-
// We should be in the minting phase now
|
|
991
|
-
assertEq(jbRulesets().currentOf(_projectId).cycleNumber, 1);
|
|
992
|
-
// Queue Phase 2
|
|
993
|
-
//deployer.queueNextPhaseOf(_projectId);
|
|
994
|
-
// Go to the end of the minting phase and check if we are still in the minting phase
|
|
995
|
-
vm.warp(_launchProjectAt + _mintPeriodDuration - 1);
|
|
996
|
-
assertEq(jbRulesets().currentOf(_projectId).cycleNumber, 1);
|
|
997
|
-
// We should now be in phase 2, minting is paused and the treasury is frozen
|
|
998
|
-
vm.warp(_launchProjectAt + _mintPeriodDuration);
|
|
999
|
-
assertEq(jbRulesets().currentOf(_projectId).cycleNumber, 2);
|
|
1000
|
-
// Queue Phase 3
|
|
1001
|
-
|
|
1002
|
-
//deployer.queueNextPhaseOf(_projectId);
|
|
1003
|
-
// We should now be in phase 4, game has ended
|
|
1004
|
-
vm.warp(_launchProjectAt + _mintPeriodDuration + _inBetweenMintAndFifa + _fifaDuration);
|
|
1005
|
-
assertEq(jbRulesets().currentOf(_projectId).cycleNumber, 3);
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
function testWhenScorecardIsSubmittedWithUnmintedTier() public {
|
|
1009
|
-
uint8 nTiers = 10;
|
|
1010
|
-
DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData(nTiers);
|
|
1011
|
-
(,, DefifaGovernor _governor) = createDefifaProject(defifaData);
|
|
1012
|
-
// Phase 1: Mint
|
|
1013
|
-
vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
1014
|
-
|
|
1015
|
-
// Warp to scoring phase (past start time)
|
|
1016
|
-
vm.warp(defifaData.start + 1);
|
|
1017
|
-
// Generate the scorecards
|
|
1018
|
-
DefifaTierCashOutWeight[] memory scorecards = new DefifaTierCashOutWeight[](nTiers);
|
|
1019
|
-
// We can't have a neutral outcome, so we only give shares to tiers that are an even number (in our array)
|
|
1020
|
-
for (uint256 i = 0; i < scorecards.length; i++) {
|
|
1021
|
-
scorecards[i].id = i + 1;
|
|
1022
|
-
scorecards[i].cashOutWeight = i % 2 == 0 ? 1e18 / (scorecards.length / 2) : 0;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
vm.expectRevert(abi.encodeWithSignature("DefifaGovernor_UnownedProposedCashoutValue()"));
|
|
1026
|
-
// Forward time so proposals can be created
|
|
1027
|
-
_governor.submitScorecardFor(_gameId, scorecards);
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
// function testWhenPhaseIsAlreadyQueued() public {
|
|
1031
|
-
// uint8 nTiers = 10;
|
|
1032
|
-
// address[] memory _users = new address[](nTiers);
|
|
1033
|
-
// DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData(nTiers);
|
|
1034
|
-
// (uint256 _projectId, DefifaHook _nft, DefifaGovernor _governor) = createDefifaProject(defifaData);
|
|
1035
|
-
// // Phase 1: Mint
|
|
1036
|
-
// vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
1037
|
-
// //deployer.queueNextPhaseOf(_projectId);
|
|
1038
|
-
// for (uint256 i = 0; i < nTiers; i++) {
|
|
1039
|
-
// // Generate a new address for each tier
|
|
1040
|
-
// _users[i] = address(bytes20(keccak256(abi.encode("user", Strings.toString(i)))));
|
|
1041
|
-
// // fund user
|
|
1042
|
-
// vm.deal(_users[i], 1 ether);
|
|
1043
|
-
// // Build metadata to buy specific NFT
|
|
1044
|
-
// uint16[] memory rawMetadata = new uint16[](1);
|
|
1045
|
-
// rawMetadata[0] = uint16(i + 1); // reward tier, 1 indexed
|
|
1046
|
-
// bytes memory metadata =
|
|
1047
|
-
// _buildPayMetadata(abi.encode(_users[i], rawMetadata);
|
|
1048
|
-
// // Pay to the project and mint an NFT
|
|
1049
|
-
// vm.prank(_users[i]);
|
|
1050
|
-
// jbMultiTerminal().pay{value: 1 ether}(_projectId, JBConstants.NATIVE_TOKEN, 1 ether,_users[i], 0, "",
|
|
1051
|
-
// metadata); // Set the delegate as the user themselves
|
|
1052
|
-
// DefifaDelegation[] memory tiered721SetDelegatesData =
|
|
1053
|
-
// new DefifaDelegation[](1);
|
|
1054
|
-
// tiered721SetDelegatesData[0] =
|
|
1055
|
-
// DefifaDelegation({delegatee: _users[i], tierId: uint256(i + 1)});
|
|
1056
|
-
// vm.prank(_users[i]);
|
|
1057
|
-
// _nft.setTierDelegatesTo(tiered721SetDelegatesData);
|
|
1058
|
-
// // Forward 1 block, user should receive all the voting power of the tier, as its the only NFT
|
|
1059
|
-
// vm.roll(block.number + 1);
|
|
1060
|
-
// assertEq(_governor.MAX_ATTESTATION_POWER_TIER(), _governor.getAttestationWeight(_gameId, _users[i],
|
|
1061
|
-
// block.number - 1)); }
|
|
1062
|
-
// // Phase 2: Redeem
|
|
1063
|
-
// vm.warp(block.timestamp + defifaData.mintPeriodDuration);
|
|
1064
|
-
// //deployer.queueNextPhaseOf(_projectId);
|
|
1065
|
-
// // Right at the end of Phase 2
|
|
1066
|
-
// vm.warp(defifaData.start - 1);
|
|
1067
|
-
// vm.expectRevert(abi.encodeWithSignature("PHASE_ALREADY_QUEUED()"));
|
|
1068
|
-
// //deployer.queueNextPhaseOf(_projectId);
|
|
1069
|
-
// }
|
|
1070
|
-
|
|
1071
|
-
function testSettingTierCashOutWeightBeforeEndPhase() public {
|
|
1072
|
-
uint8 nTiers = 10;
|
|
1073
|
-
address[] memory _users = new address[](nTiers);
|
|
1074
|
-
DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData(nTiers);
|
|
1075
|
-
(uint256 _projectId, DefifaHook _nft, DefifaGovernor _governor) = createDefifaProject(defifaData);
|
|
1076
|
-
// Phase 1: Mint
|
|
1077
|
-
vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
1078
|
-
//deployer.queueNextPhaseOf(_projectId);
|
|
1079
|
-
for (uint256 i = 0; i < nTiers; i++) {
|
|
1080
|
-
// Generate a new address for each tier
|
|
1081
|
-
_users[i] = address(bytes20(keccak256(abi.encode("user", Strings.toString(i)))));
|
|
1082
|
-
// fund user
|
|
1083
|
-
vm.deal(_users[i], 1 ether);
|
|
1084
|
-
// Build metadata to buy specific NFT
|
|
1085
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
1086
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1087
|
-
rawMetadata[0] = uint16(i + 1); // reward tier, 1 indexed
|
|
1088
|
-
bytes memory metadata = _buildPayMetadata(abi.encode(_users[i], rawMetadata));
|
|
1089
|
-
// Pay to the project and mint an NFT
|
|
1090
|
-
vm.prank(_users[i]);
|
|
1091
|
-
jbMultiTerminal().pay{value: 1 ether}(
|
|
1092
|
-
_projectId, JBConstants.NATIVE_TOKEN, 1 ether, _users[i], 0, "", metadata
|
|
1093
|
-
);
|
|
1094
|
-
// Set the delegate as the user themselves
|
|
1095
|
-
DefifaDelegation[] memory tiered721SetDelegatesData = new DefifaDelegation[](1);
|
|
1096
|
-
tiered721SetDelegatesData[0] = DefifaDelegation({delegatee: _users[i], tierId: uint256(i + 1)});
|
|
1097
|
-
vm.prank(_users[i]);
|
|
1098
|
-
_nft.setTierDelegatesTo(tiered721SetDelegatesData);
|
|
1099
|
-
// Forward 1 block, user should receive all the voting power of the tier, as its the only NFT
|
|
1100
|
-
vm.warp(_tsReader.timestamp() + 1);
|
|
1101
|
-
assertEq(
|
|
1102
|
-
_governor.MAX_ATTESTATION_POWER_TIER(),
|
|
1103
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1104
|
-
_governor.getAttestationWeight(_gameId, _users[i], uint48(_tsReader.timestamp()))
|
|
1105
|
-
);
|
|
1106
|
-
}
|
|
1107
|
-
// Warp to scoring phase (past start time)
|
|
1108
|
-
vm.warp(defifaData.start + 1);
|
|
1109
|
-
// Generate the scorecards
|
|
1110
|
-
DefifaTierCashOutWeight[] memory scorecards = new DefifaTierCashOutWeight[](nTiers);
|
|
1111
|
-
// We can't have a neutral outcome, so we only give shares to tiers that are an even number (in our array)
|
|
1112
|
-
for (uint256 i = 0; i < scorecards.length; i++) {
|
|
1113
|
-
scorecards[i].id = i + 1;
|
|
1114
|
-
scorecards[i].cashOutWeight = i % 2 == 0 ? 1e18 / (scorecards.length / 2) : 0;
|
|
1115
|
-
}
|
|
1116
|
-
// Forward time so proposals can be created
|
|
1117
|
-
uint256 _proposalId = _governor.submitScorecardFor(_gameId, scorecards);
|
|
1118
|
-
// Forward time so voting becomes active
|
|
1119
|
-
vm.warp(_tsReader.timestamp() + _governor.attestationStartTimeOf(_gameId));
|
|
1120
|
-
// All the users vote
|
|
1121
|
-
for (uint256 i = 0; i < _users.length; i++) {
|
|
1122
|
-
vm.prank(_users[i]);
|
|
1123
|
-
_governor.attestToScorecardFrom(_gameId, _proposalId);
|
|
1124
|
-
}
|
|
1125
|
-
// Execute the proposal — should fail because grace period hasn't ended
|
|
1126
|
-
vm.expectRevert(DefifaGovernor.DefifaGovernor_NotAllowed.selector);
|
|
1127
|
-
_governor.ratifyScorecardFrom(_gameId, scorecards);
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
function testWhenCashOutWeightisMoreThanMaxCashOutWeight(uint8 nTiers) public {
|
|
1131
|
-
// Anything above 10 should cause the error we are looking for.
|
|
1132
|
-
// As a sanity check we let it also run for less than 10 to see if it does not error in that case.
|
|
1133
|
-
nTiers = uint8(bound(nTiers, 2, 20));
|
|
1134
|
-
|
|
1135
|
-
// With exact-weight validation, only nTiers == 10 produces weights that sum to TOTAL_CASHOUT_WEIGHT.
|
|
1136
|
-
// Delegate to separate helpers to avoid stack-too-deep.
|
|
1137
|
-
if (nTiers == 10) {
|
|
1138
|
-
_testCashOutWeightExact(nTiers);
|
|
1139
|
-
} else {
|
|
1140
|
-
_testCashOutWeightInvalid(nTiers);
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
/// @dev nTiers == 10: all weights valid, full flow (submit → vote → ratify).
|
|
1145
|
-
function _testCashOutWeightExact(uint8 nTiers) internal {
|
|
1146
|
-
address[] memory _users = new address[](nTiers);
|
|
1147
|
-
DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData(nTiers);
|
|
1148
|
-
(uint256 _projectId, DefifaHook _nft, DefifaGovernor _governor) = createDefifaProject(defifaData);
|
|
1149
|
-
|
|
1150
|
-
uint256 cashOutWeight = _nft.TOTAL_CASHOUT_WEIGHT() / 10;
|
|
1151
|
-
|
|
1152
|
-
vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
1153
|
-
for (uint256 i = 0; i < nTiers; i++) {
|
|
1154
|
-
_users[i] = address(bytes20(keccak256(abi.encode("user", Strings.toString(i)))));
|
|
1155
|
-
vm.deal(_users[i], 1 ether);
|
|
1156
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
1157
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1158
|
-
rawMetadata[0] = uint16(i + 1);
|
|
1159
|
-
bytes memory metadata = _buildPayMetadata(abi.encode(_users[i], rawMetadata));
|
|
1160
|
-
vm.prank(_users[i]);
|
|
1161
|
-
jbMultiTerminal().pay{value: 1 ether}(
|
|
1162
|
-
_projectId, JBConstants.NATIVE_TOKEN, 1 ether, _users[i], 0, "", metadata
|
|
1163
|
-
);
|
|
1164
|
-
DefifaDelegation[] memory tiered721SetDelegatesData = new DefifaDelegation[](1);
|
|
1165
|
-
tiered721SetDelegatesData[0] = DefifaDelegation({delegatee: _users[i], tierId: uint256(i + 1)});
|
|
1166
|
-
vm.prank(_users[i]);
|
|
1167
|
-
_nft.setTierDelegatesTo(tiered721SetDelegatesData);
|
|
1168
|
-
assertEq(
|
|
1169
|
-
_governor.MAX_ATTESTATION_POWER_TIER(),
|
|
1170
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1171
|
-
_governor.getAttestationWeight(_gameId, _users[i], uint48(block.timestamp))
|
|
1172
|
-
);
|
|
1173
|
-
}
|
|
1174
|
-
vm.warp(defifaData.start + 1);
|
|
1175
|
-
|
|
1176
|
-
DefifaTierCashOutWeight[] memory scorecards = new DefifaTierCashOutWeight[](nTiers);
|
|
1177
|
-
for (uint256 i = 0; i < scorecards.length; i++) {
|
|
1178
|
-
scorecards[i].id = i + 1;
|
|
1179
|
-
scorecards[i].cashOutWeight = cashOutWeight;
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
uint256 _proposalId = _governor.submitScorecardFor(_gameId, scorecards);
|
|
1183
|
-
vm.warp(block.timestamp + _governor.attestationStartTimeOf(_gameId));
|
|
1184
|
-
for (uint256 i = 0; i < _users.length; i++) {
|
|
1185
|
-
vm.prank(_users[i]);
|
|
1186
|
-
_governor.attestToScorecardFrom(_gameId, _proposalId);
|
|
1187
|
-
}
|
|
1188
|
-
vm.warp(block.timestamp + _governor.attestationGracePeriodOf(_gameId) + 1);
|
|
1189
|
-
_governor.ratifyScorecardFrom(_gameId, scorecards);
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
/// @dev nTiers != 10: weights don't sum to TOTAL_CASHOUT_WEIGHT, submitScorecardFor reverts.
|
|
1193
|
-
function _testCashOutWeightInvalid(uint8 nTiers) internal {
|
|
1194
|
-
address[] memory _users = new address[](nTiers);
|
|
1195
|
-
DefifaLaunchProjectData memory defifaData = getBasicDefifaLaunchData(nTiers);
|
|
1196
|
-
(uint256 _projectId, DefifaHook _nft, DefifaGovernor _governor) = createDefifaProject(defifaData);
|
|
1197
|
-
|
|
1198
|
-
uint256 cashOutWeight = _nft.TOTAL_CASHOUT_WEIGHT() / 10;
|
|
1199
|
-
|
|
1200
|
-
vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
1201
|
-
for (uint256 i = 0; i < nTiers; i++) {
|
|
1202
|
-
_users[i] = address(bytes20(keccak256(abi.encode("user", Strings.toString(i)))));
|
|
1203
|
-
vm.deal(_users[i], 1 ether);
|
|
1204
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
1205
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1206
|
-
rawMetadata[0] = uint16(i + 1);
|
|
1207
|
-
bytes memory metadata = _buildPayMetadata(abi.encode(_users[i], rawMetadata));
|
|
1208
|
-
vm.prank(_users[i]);
|
|
1209
|
-
jbMultiTerminal().pay{value: 1 ether}(
|
|
1210
|
-
_projectId, JBConstants.NATIVE_TOKEN, 1 ether, _users[i], 0, "", metadata
|
|
1211
|
-
);
|
|
1212
|
-
DefifaDelegation[] memory tiered721SetDelegatesData = new DefifaDelegation[](1);
|
|
1213
|
-
tiered721SetDelegatesData[0] = DefifaDelegation({delegatee: _users[i], tierId: uint256(i + 1)});
|
|
1214
|
-
vm.prank(_users[i]);
|
|
1215
|
-
_nft.setTierDelegatesTo(tiered721SetDelegatesData);
|
|
1216
|
-
assertEq(
|
|
1217
|
-
_governor.MAX_ATTESTATION_POWER_TIER(),
|
|
1218
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1219
|
-
_governor.getAttestationWeight(_gameId, _users[i], uint48(block.timestamp))
|
|
1220
|
-
);
|
|
1221
|
-
}
|
|
1222
|
-
vm.warp(defifaData.start + 1);
|
|
1223
|
-
|
|
1224
|
-
DefifaTierCashOutWeight[] memory scorecards = new DefifaTierCashOutWeight[](nTiers);
|
|
1225
|
-
for (uint256 i = 0; i < scorecards.length; i++) {
|
|
1226
|
-
scorecards[i].id = i + 1;
|
|
1227
|
-
scorecards[i].cashOutWeight = cashOutWeight;
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
vm.expectRevert(DefifaHook.DefifaHook_InvalidCashoutWeights.selector);
|
|
1231
|
-
_governor.submitScorecardFor(_gameId, scorecards);
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
function getBasicDefifaLaunchData(uint8 nTiers) internal returns (DefifaLaunchProjectData memory) {
|
|
1235
|
-
DefifaTierParams[] memory tierParams = new DefifaTierParams[](nTiers);
|
|
1236
|
-
for (uint256 i = 0; i < nTiers; i++) {
|
|
1237
|
-
tierParams[i] = DefifaTierParams({
|
|
1238
|
-
reservedRate: 1001,
|
|
1239
|
-
reservedTokenBeneficiary: address(0),
|
|
1240
|
-
encodedIPFSUri: bytes32(0), // this way we dont need more tokenUris
|
|
1241
|
-
shouldUseReservedTokenBeneficiaryAsDefault: false,
|
|
1242
|
-
name: "DEFIFA"
|
|
1243
|
-
});
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
return DefifaLaunchProjectData({
|
|
1247
|
-
name: "DEFIFA",
|
|
1248
|
-
projectUri: "",
|
|
1249
|
-
contractUri: "",
|
|
1250
|
-
baseUri: "",
|
|
1251
|
-
tierPrice: 1 ether,
|
|
1252
|
-
token: JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH}),
|
|
1253
|
-
mintPeriodDuration: 1 days,
|
|
1254
|
-
start: uint48(block.timestamp + 3 days),
|
|
1255
|
-
refundPeriodDuration: 1 days,
|
|
1256
|
-
store: new JB721TiersHookStore(),
|
|
1257
|
-
splits: new JBSplit[](0),
|
|
1258
|
-
attestationStartTime: 0,
|
|
1259
|
-
attestationGracePeriod: 100_381,
|
|
1260
|
-
defaultAttestationDelegate: address(0),
|
|
1261
|
-
tiers: tierParams,
|
|
1262
|
-
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
1263
|
-
terminal: jbMultiTerminal(),
|
|
1264
|
-
minParticipation: 0,
|
|
1265
|
-
scorecardTimeout: 0,
|
|
1266
|
-
timelockDuration: 0
|
|
1267
|
-
});
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
// ----- internal helpers ------
|
|
1271
|
-
function createDefifaProject(DefifaLaunchProjectData memory defifaLaunchData)
|
|
1272
|
-
internal
|
|
1273
|
-
returns (uint256 projectId, DefifaHook nft, DefifaGovernor _governor)
|
|
1274
|
-
{
|
|
1275
|
-
_governor = governor;
|
|
1276
|
-
(projectId) = deployer.launchGameWith(defifaLaunchData);
|
|
1277
|
-
// Get a reference to the latest configured funding cycle's data hook, which should be the hook that was
|
|
1278
|
-
// deployed and attached to the project.
|
|
1279
|
-
JBRuleset memory _fc = jbRulesets().currentOf(projectId);
|
|
1280
|
-
if (_fc.dataHook() == address(0)) {
|
|
1281
|
-
(_fc,) = jbRulesets().latestQueuedOf(projectId);
|
|
1282
|
-
}
|
|
1283
|
-
nft = DefifaHook(_fc.dataHook());
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
function mintAndRefund(DefifaHook _hook, uint256 _projectId, uint256 _tierId) internal {
|
|
1287
|
-
JB721Tier memory _tier = _hook.store().tierOf(address(_hook), _tierId, false);
|
|
1288
|
-
uint256 _cost = _tier.price;
|
|
1289
|
-
address _refundUser = address(bytes20(keccak256("refund_user")));
|
|
1290
|
-
// The user should have no balance
|
|
1291
|
-
assertEq(_hook.balanceOf(_refundUser), 0);
|
|
1292
|
-
// Build metadata to buy specific NFT
|
|
1293
|
-
uint16[] memory rawMetadata = new uint16[](1);
|
|
1294
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1295
|
-
rawMetadata[0] = uint16(_tierId); // reward tier, 1 indexed
|
|
1296
|
-
bytes memory metadata = _buildPayMetadata(abi.encode(_refundUser, rawMetadata));
|
|
1297
|
-
// Pay to the project and mint an NFT
|
|
1298
|
-
vm.deal(_refundUser, _cost);
|
|
1299
|
-
vm.prank(_refundUser);
|
|
1300
|
-
jbMultiTerminal().pay{value: _cost}(_projectId, JBConstants.NATIVE_TOKEN, _cost, _refundUser, 0, "", metadata);
|
|
1301
|
-
// User should no longer have any funds
|
|
1302
|
-
assertEq(_refundUser.balance, 0);
|
|
1303
|
-
// The user should have have a token
|
|
1304
|
-
assertEq(_hook.balanceOf(_refundUser), 1);
|
|
1305
|
-
// Craft the metadata: redeem the tokenId
|
|
1306
|
-
bytes memory cashOutMetadata;
|
|
1307
|
-
{
|
|
1308
|
-
uint256[] memory cashOutId = new uint256[](1);
|
|
1309
|
-
cashOutId[0] = _generateTokenId(_tierId, _tier.initialSupply - --_tier.remainingSupply);
|
|
1310
|
-
cashOutMetadata = _buildCashOutMetadata(abi.encode(cashOutId));
|
|
1311
|
-
}
|
|
1312
|
-
vm.prank(_refundUser);
|
|
1313
|
-
JBMultiTerminal(address(jbMultiTerminal()))
|
|
1314
|
-
.cashOutTokensOf({
|
|
1315
|
-
holder: _refundUser,
|
|
1316
|
-
projectId: _projectId,
|
|
1317
|
-
cashOutCount: 0,
|
|
1318
|
-
tokenToReclaim: JBConstants.NATIVE_TOKEN,
|
|
1319
|
-
minTokensReclaimed: 0,
|
|
1320
|
-
beneficiary: payable(_refundUser),
|
|
1321
|
-
metadata: cashOutMetadata
|
|
1322
|
-
});
|
|
1323
|
-
// User should have their original funds again
|
|
1324
|
-
assertEq(_refundUser.balance, _cost);
|
|
1325
|
-
// User should no longer have the NFT
|
|
1326
|
-
assertEq(_hook.balanceOf(_refundUser), 0);
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
// Create launchProjectFor(..) payload
|
|
1330
|
-
string name = "NAME";
|
|
1331
|
-
string symbol = "SYM";
|
|
1332
|
-
string baseUri = "http://www.null.com/";
|
|
1333
|
-
string contractUri = "ipfs://null";
|
|
1334
|
-
address reserveBeneficiary = address(bytes20(keccak256("reserveBeneficiary")));
|
|
1335
|
-
//QmWmyoMoctfbAaiEs2G46gpeUmhqFRDW6KWo64y5r581Vz
|
|
1336
|
-
bytes32[] tokenUris = [
|
|
1337
|
-
bytes32(0x7D5A99F603F231D53A4F39D1521F98D2E8BB279CF29BEBFD0687DC98458E7F89),
|
|
1338
|
-
bytes32(0x7D5A99F603F231D53A4F39D1521F98D2E8BB279CF29BEBFD0687DC98458E7F89),
|
|
1339
|
-
bytes32(0x7D5A99F603F231D53A4F39D1521F98D2E8BB279CF29BEBFD0687DC98458E7F89),
|
|
1340
|
-
bytes32(0x7D5A99F603F231D53A4F39D1521F98D2E8BB279CF29BEBFD0687DC98458E7F89),
|
|
1341
|
-
bytes32(0x7D5A99F603F231D53A4F39D1521F98D2E8BB279CF29BEBFD0687DC98458E7F89),
|
|
1342
|
-
bytes32(0x7D5A99F603F231D53A4F39D1521F98D2E8BB279CF29BEBFD0687DC98458E7F89),
|
|
1343
|
-
bytes32(0x7D5A99F603F231D53A4F39D1521F98D2E8BB279CF29BEBFD0687DC98458E7F89),
|
|
1344
|
-
bytes32(0x7D5A99F603F231D53A4F39D1521F98D2E8BB279CF29BEBFD0687DC98458E7F89),
|
|
1345
|
-
bytes32(0x7D5A99F603F231D53A4F39D1521F98D2E8BB279CF29BEBFD0687DC98458E7F89),
|
|
1346
|
-
bytes32(0x7D5A99F603F231D53A4F39D1521F98D2E8BB279CF29BEBFD0687DC98458E7F89)
|
|
1347
|
-
];
|
|
1348
|
-
|
|
1349
|
-
function _generateTokenId(uint256 _tierId, uint256 _tokenNumber) internal pure returns (uint256) {
|
|
1350
|
-
return (_tierId * 1_000_000_000) + _tokenNumber;
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
function _buildPayMetadata(bytes memory metadata) internal view returns (bytes memory) {
|
|
1354
|
-
// Build the metadata using the tiers to mint and the overspending flag.
|
|
1355
|
-
bytes[] memory data = new bytes[](1);
|
|
1356
|
-
data[0] = metadata;
|
|
1357
|
-
|
|
1358
|
-
// Pass the hook ID.
|
|
1359
|
-
bytes4[] memory ids = new bytes4[](1);
|
|
1360
|
-
ids[0] = metadataHelper().getId("pay", address(hook));
|
|
1361
|
-
|
|
1362
|
-
// Generate the metadata.
|
|
1363
|
-
return metadataHelper().createMetadata(ids, data);
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
function _buildCashOutMetadata(bytes memory metadata) internal view returns (bytes memory) {
|
|
1367
|
-
// Build the metadata using the tiers to mint and the overspending flag.
|
|
1368
|
-
bytes[] memory data = new bytes[](1);
|
|
1369
|
-
data[0] = metadata;
|
|
1370
|
-
|
|
1371
|
-
// Pass the hook ID.
|
|
1372
|
-
bytes4[] memory ids = new bytes4[](1);
|
|
1373
|
-
ids[0] = metadataHelper().getId("cashOut", address(hook));
|
|
1374
|
-
|
|
1375
|
-
// Generate the metadata.
|
|
1376
|
-
return metadataHelper().createMetadata(ids, data);
|
|
1377
|
-
}
|
|
1378
|
-
}
|