@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.
Files changed (65) hide show
  1. package/AUDIT_INSTRUCTIONS.md +6 -2
  2. package/README.md +11 -2
  3. package/RISKS.md +3 -1
  4. package/STYLE_GUIDE.md +14 -11
  5. package/package.json +31 -14
  6. package/script/Deploy.s.sol +4 -1
  7. package/src/DefifaDeployer.sol +79 -47
  8. package/src/DefifaGovernor.sol +57 -12
  9. package/src/DefifaHook.sol +83 -26
  10. package/src/DefifaProjectOwner.sol +4 -3
  11. package/src/DefifaTokenUriResolver.sol +113 -20
  12. package/src/enums/DefifaGamePhase.sol +6 -0
  13. package/src/enums/DefifaScorecardState.sol +4 -0
  14. package/src/interfaces/IDefifaDeployer.sol +5 -0
  15. package/src/interfaces/IDefifaGamePhaseReporter.sol +4 -0
  16. package/src/interfaces/IDefifaGamePotReporter.sol +10 -0
  17. package/src/interfaces/IDefifaGovernor.sol +4 -0
  18. package/src/interfaces/IDefifaHook.sol +5 -0
  19. package/src/interfaces/IDefifaTokenUriResolver.sol +3 -0
  20. package/src/libraries/DefifaFontImporter.sol +1 -1
  21. package/src/libraries/DefifaHookLib.sol +9 -10
  22. package/src/structs/DefifaAttestations.sol +3 -2
  23. package/src/structs/DefifaDelegation.sol +1 -0
  24. package/src/structs/DefifaLaunchProjectData.sol +2 -3
  25. package/src/structs/DefifaOpsData.sol +1 -0
  26. package/src/structs/DefifaScorecard.sol +2 -0
  27. package/src/structs/DefifaTierCashOutWeight.sol +3 -1
  28. package/src/structs/DefifaTierParams.sol +1 -0
  29. package/CRYPTO_ECON.pdf +0 -0
  30. package/CRYPTO_ECON.tex +0 -997
  31. package/foundry.lock +0 -17
  32. package/references/operations.md +0 -32
  33. package/references/runtime.md +0 -43
  34. package/slither-ci.config.json +0 -10
  35. package/sphinx.lock +0 -521
  36. package/test/BWAFunctionComparison.t.sol +0 -1320
  37. package/test/DefifaAdversarialQuorum.t.sol +0 -617
  38. package/test/DefifaAuditLowGuards.t.sol +0 -308
  39. package/test/DefifaFeeAccounting.t.sol +0 -581
  40. package/test/DefifaGovernanceHardening.t.sol +0 -1315
  41. package/test/DefifaGovernor.t.sol +0 -1378
  42. package/test/DefifaHookRegressions.t.sol +0 -415
  43. package/test/DefifaMintCostInvariant.t.sol +0 -319
  44. package/test/DefifaNoContest.t.sol +0 -941
  45. package/test/DefifaSecurity.t.sol +0 -741
  46. package/test/DefifaUSDC.t.sol +0 -480
  47. package/test/Fork.t.sol +0 -2388
  48. package/test/TestAuditGaps.sol +0 -984
  49. package/test/TestQALastMile.t.sol +0 -514
  50. package/test/audit/AttestationDoubleCount.t.sol +0 -218
  51. package/test/audit/CodexNemesisCurrencyMismatchBypass.t.sol +0 -112
  52. package/test/audit/CodexNemesisNoContestReserveDrain.t.sol +0 -238
  53. package/test/audit/CodexNemesisOneTierZeroTimeoutLockVerified.t.sol +0 -218
  54. package/test/audit/CodexNemesisSingleTierTimeoutLock.t.sol +0 -237
  55. package/test/audit/CodexRegistryMismatch.t.sol +0 -191
  56. package/test/audit/CodexTierCapMismatch.t.sol +0 -171
  57. package/test/audit/CurrencyMismatchFix.t.sol +0 -265
  58. package/test/audit/FixPendingReserveDilution.t.sol +0 -366
  59. package/test/audit/H5TierCapValidation.t.sol +0 -184
  60. package/test/audit/PendingReserveDilution.t.sol +0 -298
  61. package/test/audit/PendingReserveQuorumGrief.t.sol +0 -355
  62. package/test/audit/PendingReserveSnapshotBypass.t.sol +0 -319
  63. package/test/regression/AttestationDelegateBeneficiary.t.sol +0 -271
  64. package/test/regression/FulfillmentBlocksRatification.t.sol +0 -279
  65. package/test/regression/GracePeriodBypass.t.sol +0 -302
@@ -1,319 +0,0 @@
1
- // SPDX-License-Identifier: UNLICENSED
2
- pragma solidity 0.8.28;
3
-
4
- import {Test} from "forge-std/Test.sol";
5
- import {DefifaGovernor} from "../src/DefifaGovernor.sol";
6
- import {DefifaDeployer} from "../src/DefifaDeployer.sol";
7
- import {DefifaHook} from "../src/DefifaHook.sol";
8
- import {DefifaTokenUriResolver} from "../src/DefifaTokenUriResolver.sol";
9
- import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
10
-
11
- import {MetadataResolverHelper} from "@bananapus/core-v6/test/helpers/MetadataResolverHelper.sol";
12
- import {TestBaseWorkflow} from "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
13
- import {JBTest} from "@bananapus/core-v6/test/helpers/JBTest.sol";
14
- import {JBRulesetMetadataResolver} from "@bananapus/core-v6/src/libraries/JBRulesetMetadataResolver.sol";
15
- import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
16
- import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
17
- import {DefifaLaunchProjectData} from "../src/structs/DefifaLaunchProjectData.sol";
18
- import {DefifaTierParams} from "../src/structs/DefifaTierParams.sol";
19
- import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
20
- import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
21
- import {IJB721TiersHookStore} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookStore.sol";
22
- import {IJBMultiTerminal} from "@bananapus/core-v6/src/interfaces/IJBMultiTerminal.sol";
23
- import {IJBRulesetApprovalHook} from "@bananapus/core-v6/src/interfaces/IJBRulesets.sol";
24
- import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
25
- import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
26
- import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
27
- import {JBCurrencyIds} from "@bananapus/core-v6/src/libraries/JBCurrencyIds.sol";
28
- import {JBFundAccessLimitGroup} from "@bananapus/core-v6/src/structs/JBFundAccessLimitGroup.sol";
29
- import {JBMultiTerminal} from "@bananapus/core-v6/src/JBMultiTerminal.sol";
30
- import {JBRulesetConfig, JBTerminalConfig} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
31
- import {JBRulesetMetadata} from "@bananapus/core-v6/src/structs/JBRulesetMetadata.sol";
32
- import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
33
- import {JBSplitGroup} from "@bananapus/core-v6/src/structs/JBSplitGroup.sol";
34
- import {ITypeface} from "lib/typeface/contracts/interfaces/ITypeface.sol";
35
-
36
- /// @title MintCostHandler
37
- /// @notice Stateful fuzz handler that performs pay and refund operations,
38
- /// tracking the expected _totalMintCost alongside.
39
- contract MintCostHandler is Test {
40
- DefifaHook public nft;
41
- IJB721TiersHookStore public store;
42
- uint256 public pid;
43
- MetadataResolverHelper public metaHelper;
44
- IJBMultiTerminal public terminal;
45
- address public hookCodeOrigin;
46
-
47
- uint256 public expectedMintCost;
48
- uint256 public tierPrice;
49
- uint8 public nTiers;
50
- uint256 public mintCount;
51
- uint256 public burnCount;
52
-
53
- struct TokenInfo {
54
- address holder;
55
- uint256 tierId;
56
- uint256 tokenNumber;
57
- }
58
-
59
- TokenInfo[] public liveTokens;
60
-
61
- constructor(
62
- DefifaHook _nft,
63
- uint256 _pid,
64
- MetadataResolverHelper _metaHelper,
65
- IJBMultiTerminal _terminal,
66
- address _hookCodeOrigin,
67
- uint256 _tierPrice,
68
- uint8 _nTiers
69
- ) {
70
- nft = _nft;
71
- pid = _pid;
72
- metaHelper = _metaHelper;
73
- terminal = _terminal;
74
- hookCodeOrigin = _hookCodeOrigin;
75
- tierPrice = _tierPrice;
76
- nTiers = _nTiers;
77
- store = _nft.store();
78
- }
79
-
80
- /// @notice Mint a new NFT for a random tier.
81
- function mint(uint256 tierSeed) external {
82
- uint256 tierId = bound(tierSeed, 1, nTiers);
83
-
84
- // Check remaining supply
85
- JB721Tier memory tier = store.tierOf(address(nft), tierId, false);
86
- if (tier.remainingSupply == 0) return;
87
-
88
- address user = _userAddr(mintCount + 1000);
89
- vm.deal(user, tierPrice);
90
-
91
- uint16[] memory m = new uint16[](1);
92
- // forge-lint: disable-next-line(unsafe-typecast)
93
- m[0] = uint16(tierId);
94
- bytes[] memory data = new bytes[](1);
95
- data[0] = abi.encode(user, m);
96
- bytes4[] memory ids = new bytes4[](1);
97
- ids[0] = metaHelper.getId("pay", hookCodeOrigin);
98
-
99
- vm.prank(user);
100
- terminal.pay{value: tierPrice}(
101
- pid, JBConstants.NATIVE_TOKEN, tierPrice, user, 0, "", metaHelper.createMetadata(ids, data)
102
- );
103
-
104
- uint256 nb = store.numberOfBurnedFor(address(nft), tierId);
105
- uint256 tokenNumber = tier.initialSupply - tier.remainingSupply + 1 + nb;
106
-
107
- liveTokens.push(TokenInfo({holder: user, tierId: tierId, tokenNumber: tokenNumber}));
108
-
109
- expectedMintCost += tierPrice;
110
- mintCount++;
111
- }
112
-
113
- /// @notice Refund (cashout during mint phase) — burns the NFT and returns exact price.
114
- function refund(uint256 indexSeed) external {
115
- if (liveTokens.length == 0) return;
116
- uint256 idx = bound(indexSeed, 0, liveTokens.length - 1);
117
-
118
- TokenInfo memory info = liveTokens[idx];
119
- uint256 tokenId = (info.tierId * 1_000_000_000) + info.tokenNumber;
120
-
121
- uint256[] memory cid = new uint256[](1);
122
- cid[0] = tokenId;
123
- bytes[] memory data = new bytes[](1);
124
- data[0] = abi.encode(cid);
125
- bytes4[] memory bids = new bytes4[](1);
126
- bids[0] = metaHelper.getId("cashOut", hookCodeOrigin);
127
-
128
- vm.prank(info.holder);
129
- JBMultiTerminal(address(terminal))
130
- .cashOutTokensOf({
131
- holder: info.holder,
132
- projectId: pid,
133
- cashOutCount: 0,
134
- tokenToReclaim: JBConstants.NATIVE_TOKEN,
135
- minTokensReclaimed: 0,
136
- beneficiary: payable(info.holder),
137
- metadata: metaHelper.createMetadata(bids, data)
138
- });
139
-
140
- expectedMintCost -= tierPrice;
141
- burnCount++;
142
-
143
- // Swap-and-pop
144
- liveTokens[idx] = liveTokens[liveTokens.length - 1];
145
- liveTokens.pop();
146
- }
147
-
148
- function liveTokenCount() external view returns (uint256) {
149
- return liveTokens.length;
150
- }
151
-
152
- function _userAddr(uint256 i) internal pure returns (address) {
153
- return address(bytes20(keccak256(abi.encode("inv_user", i))));
154
- }
155
- }
156
-
157
- /// @title DefifaMintCostInvariantTest
158
- /// @notice Invariant: _totalMintCost == sum of tier prices of all live (non-burned) NFTs.
159
- contract DefifaMintCostInvariantTest is JBTest, TestBaseWorkflow {
160
- using JBRulesetMetadataResolver for JBRuleset;
161
-
162
- uint256 _protocolFeeProjectId;
163
- uint256 _defifaProjectId;
164
- address projectOwner = address(bytes20(keccak256("projectOwner")));
165
-
166
- DefifaDeployer deployer;
167
- DefifaHook hookImpl;
168
- DefifaGovernor governor;
169
- DefifaHook nft;
170
-
171
- /// @dev Storage slot of _totalMintCost in DefifaHook (from `forge inspect DefifaHook storage-layout`).
172
- uint256 constant TOTAL_MINT_COST_SLOT = 141;
173
-
174
- uint8 constant N_TIERS = 4;
175
- uint256 constant TIER_PRICE = 1 ether;
176
-
177
- MintCostHandler handler;
178
-
179
- function setUp() public virtual override {
180
- super.setUp();
181
-
182
- JBAccountingContext[] memory _tokens = new JBAccountingContext[](1);
183
- _tokens[0] = JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH});
184
- JBTerminalConfig[] memory tc = new JBTerminalConfig[](1);
185
- tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: _tokens});
186
- JBRulesetConfig[] memory rc = new JBRulesetConfig[](1);
187
- rc[0] = JBRulesetConfig({
188
- mustStartAtOrAfter: 0,
189
- duration: 10 days,
190
- weight: 1e18,
191
- weightCutPercent: 0,
192
- approvalHook: IJBRulesetApprovalHook(address(0)),
193
- metadata: JBRulesetMetadata({
194
- reservedPercent: 0,
195
- cashOutTaxRate: 0,
196
- baseCurrency: JBCurrencyIds.ETH,
197
- pausePay: false,
198
- pauseCreditTransfers: false,
199
- allowOwnerMinting: false,
200
- allowSetCustomToken: false,
201
- allowTerminalMigration: false,
202
- allowSetTerminals: false,
203
- allowSetController: false,
204
- allowAddAccountingContext: false,
205
- allowAddPriceFeed: false,
206
- ownerMustSendPayouts: false,
207
- holdFees: false,
208
- useTotalSurplusForCashOuts: false,
209
- useDataHookForPay: true,
210
- useDataHookForCashOut: true,
211
- dataHook: address(0),
212
- metadata: 0
213
- }),
214
- splitGroups: new JBSplitGroup[](0),
215
- fundAccessLimitGroups: new JBFundAccessLimitGroup[](0)
216
- });
217
-
218
- _protocolFeeProjectId = jbController().launchProjectFor(projectOwner, "", rc, tc, "");
219
- vm.prank(projectOwner);
220
- address _nanaToken =
221
- address(jbController().deployERC20For(_protocolFeeProjectId, "Bananapus", "NANA", bytes32(0)));
222
-
223
- _defifaProjectId = jbController().launchProjectFor(projectOwner, "", rc, tc, "");
224
- vm.prank(projectOwner);
225
- address _defifaToken = address(jbController().deployERC20For(_defifaProjectId, "Defifa", "DEFIFA", bytes32(0)));
226
-
227
- hookImpl = new DefifaHook(jbDirectory(), IERC20(_defifaToken), IERC20(_nanaToken));
228
- governor = new DefifaGovernor(jbController(), address(this));
229
- deployer = new DefifaDeployer(
230
- address(hookImpl),
231
- new DefifaTokenUriResolver(ITypeface(address(0))),
232
- governor,
233
- jbController(),
234
- new JBAddressRegistry(),
235
- _protocolFeeProjectId,
236
- _defifaProjectId
237
- );
238
- hookImpl.transferOwnership(address(deployer));
239
- governor.transferOwnership(address(deployer));
240
-
241
- // Launch the game with a long mint period for invariant testing
242
- DefifaTierParams[] memory tp = new DefifaTierParams[](N_TIERS);
243
- for (uint256 i; i < N_TIERS; i++) {
244
- tp[i] = DefifaTierParams({
245
- reservedRate: 1001,
246
- reservedTokenBeneficiary: address(0),
247
- encodedIPFSUri: bytes32(0),
248
- shouldUseReservedTokenBeneficiaryAsDefault: false,
249
- name: "DEFIFA"
250
- });
251
- }
252
-
253
- DefifaLaunchProjectData memory d = DefifaLaunchProjectData({
254
- name: "DEFIFA",
255
- projectUri: "",
256
- contractUri: "",
257
- baseUri: "",
258
- token: JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH}),
259
- mintPeriodDuration: 100 days,
260
- start: uint48(block.timestamp + 200 days),
261
- refundPeriodDuration: 100 days,
262
- store: new JB721TiersHookStore(),
263
- splits: new JBSplit[](0),
264
- attestationStartTime: 0,
265
- attestationGracePeriod: 100_381,
266
- defaultAttestationDelegate: address(0),
267
- // forge-lint: disable-next-line(unsafe-typecast)
268
- tierPrice: uint104(TIER_PRICE),
269
- tiers: tp,
270
- defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
271
- terminal: jbMultiTerminal(),
272
- minParticipation: 0,
273
- scorecardTimeout: 0,
274
- timelockDuration: 0
275
- });
276
-
277
- uint256 pid = deployer.launchGameWith(d);
278
-
279
- // Get the deployed hook clone
280
- JBRuleset memory fc = jbRulesets().currentOf(pid);
281
- if (fc.dataHook() == address(0)) (fc,) = jbRulesets().latestQueuedOf(pid);
282
- nft = DefifaHook(fc.dataHook());
283
-
284
- // Warp to mint phase
285
- vm.warp(d.start - d.mintPeriodDuration - d.refundPeriodDuration);
286
-
287
- handler =
288
- new MintCostHandler(nft, pid, metadataHelper(), jbMultiTerminal(), address(hookImpl), TIER_PRICE, N_TIERS);
289
-
290
- targetContract(address(handler));
291
-
292
- bytes4[] memory selectors = new bytes4[](2);
293
- selectors[0] = MintCostHandler.mint.selector;
294
- selectors[1] = MintCostHandler.refund.selector;
295
- targetSelector(FuzzSelector({addr: address(handler), selectors: selectors}));
296
- }
297
-
298
- /// @notice Read _totalMintCost directly from storage.
299
- function _readTotalMintCost() internal view returns (uint256) {
300
- return uint256(vm.load(address(nft), bytes32(TOTAL_MINT_COST_SLOT)));
301
- }
302
-
303
- /// @notice INVARIANT: _totalMintCost always equals the handler's tracked expected value.
304
- function invariant_totalMintCostMatchesExpected() external view {
305
- assertEq(_readTotalMintCost(), handler.expectedMintCost(), "totalMintCost drift");
306
- }
307
-
308
- /// @notice INVARIANT: _totalMintCost == tierPrice * live token count.
309
- function invariant_totalMintCostEqualsPriceTimesLiveTokens() external view {
310
- assertEq(_readTotalMintCost(), TIER_PRICE * handler.liveTokenCount(), "totalMintCost != price * liveTokens");
311
- }
312
-
313
- /// @notice INVARIANT: mints - burns == live tokens.
314
- function invariant_tokenCountConsistency() external view {
315
- assertEq(
316
- handler.mintCount() - handler.burnCount(), handler.liveTokenCount(), "mintCount - burnCount != liveTokens"
317
- );
318
- }
319
- }