@ballkidz/defifa 0.0.25 → 0.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/AUDIT_INSTRUCTIONS.md +6 -2
  2. package/README.md +11 -2
  3. package/RISKS.md +3 -1
  4. package/STYLE_GUIDE.md +14 -11
  5. package/package.json +31 -14
  6. package/script/Deploy.s.sol +4 -1
  7. package/src/DefifaDeployer.sol +74 -46
  8. package/src/DefifaGovernor.sol +53 -11
  9. package/src/DefifaHook.sol +79 -25
  10. package/src/DefifaTokenUriResolver.sol +111 -19
  11. package/src/interfaces/IDefifaDeployer.sol +5 -0
  12. package/src/interfaces/IDefifaGovernor.sol +4 -0
  13. package/src/interfaces/IDefifaHook.sol +5 -0
  14. package/src/libraries/DefifaHookLib.sol +9 -10
  15. package/src/structs/DefifaLaunchProjectData.sol +0 -3
  16. package/CRYPTO_ECON.pdf +0 -0
  17. package/CRYPTO_ECON.tex +0 -997
  18. package/foundry.lock +0 -17
  19. package/references/operations.md +0 -32
  20. package/references/runtime.md +0 -43
  21. package/slither-ci.config.json +0 -10
  22. package/sphinx.lock +0 -521
  23. package/test/BWAFunctionComparison.t.sol +0 -1320
  24. package/test/DefifaAdversarialQuorum.t.sol +0 -617
  25. package/test/DefifaAuditLowGuards.t.sol +0 -308
  26. package/test/DefifaFeeAccounting.t.sol +0 -581
  27. package/test/DefifaGovernanceHardening.t.sol +0 -1315
  28. package/test/DefifaGovernor.t.sol +0 -1378
  29. package/test/DefifaHookRegressions.t.sol +0 -415
  30. package/test/DefifaMintCostInvariant.t.sol +0 -319
  31. package/test/DefifaNoContest.t.sol +0 -941
  32. package/test/DefifaSecurity.t.sol +0 -741
  33. package/test/DefifaUSDC.t.sol +0 -480
  34. package/test/Fork.t.sol +0 -2388
  35. package/test/TestAuditGaps.sol +0 -984
  36. package/test/TestQALastMile.t.sol +0 -514
  37. package/test/audit/AttestationDoubleCount.t.sol +0 -218
  38. package/test/audit/CodexNemesisCurrencyMismatchBypass.t.sol +0 -112
  39. package/test/audit/CodexNemesisNoContestReserveDrain.t.sol +0 -238
  40. package/test/audit/CodexNemesisOneTierZeroTimeoutLockVerified.t.sol +0 -218
  41. package/test/audit/CodexNemesisSingleTierTimeoutLock.t.sol +0 -237
  42. package/test/audit/CodexRegistryMismatch.t.sol +0 -191
  43. package/test/audit/CodexTierCapMismatch.t.sol +0 -171
  44. package/test/audit/CurrencyMismatchFix.t.sol +0 -265
  45. package/test/audit/FixPendingReserveDilution.t.sol +0 -366
  46. package/test/audit/H5TierCapValidation.t.sol +0 -184
  47. package/test/audit/PendingReserveDilution.t.sol +0 -298
  48. package/test/audit/PendingReserveQuorumGrief.t.sol +0 -355
  49. package/test/audit/PendingReserveSnapshotBypass.t.sol +0 -319
  50. package/test/regression/AttestationDelegateBeneficiary.t.sol +0 -271
  51. package/test/regression/FulfillmentBlocksRatification.t.sol +0 -279
  52. package/test/regression/GracePeriodBypass.t.sol +0 -302
@@ -1,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
- }