@ballkidz/defifa 0.0.1 → 0.0.2

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 (25) hide show
  1. package/package.json +5 -5
  2. package/src/DefifaDeployer.sol +1 -0
  3. package/src/DefifaGovernor.sol +22 -7
  4. package/src/DefifaHook.sol +0 -6
  5. package/src/interfaces/IDefifaGovernor.sol +2 -0
  6. package/test/regression/M35_GracePeriodBypass.t.sol +296 -0
  7. package/test/regression/M36_FulfillmentBlocksRatification.t.sol +272 -0
  8. package/.gas-snapshot +0 -2
  9. package/deployments/defifa-v5/arbitrum_sepolia/DefifaDelegate.json +0 -4867
  10. package/deployments/defifa-v5/arbitrum_sepolia/DefifaDeployer.json +0 -1719
  11. package/deployments/defifa-v5/arbitrum_sepolia/DefifaGovernor.json +0 -1535
  12. package/deployments/defifa-v5/arbitrum_sepolia/DefifaTokenUriResolver.json +0 -295
  13. package/deployments/defifa-v5/base_sepolia/DefifaDelegate.json +0 -4875
  14. package/deployments/defifa-v5/base_sepolia/DefifaDeployer.json +0 -1725
  15. package/deployments/defifa-v5/base_sepolia/DefifaGovernor.json +0 -1543
  16. package/deployments/defifa-v5/base_sepolia/DefifaTokenUriResolver.json +0 -301
  17. package/deployments/defifa-v5/optimism_sepolia/DefifaDelegate.json +0 -4875
  18. package/deployments/defifa-v5/optimism_sepolia/DefifaDeployer.json +0 -1725
  19. package/deployments/defifa-v5/optimism_sepolia/DefifaGovernor.json +0 -1543
  20. package/deployments/defifa-v5/optimism_sepolia/DefifaTokenUriResolver.json +0 -301
  21. package/deployments/defifa-v5/sepolia/DefifaDelegate.json +0 -4875
  22. package/deployments/defifa-v5/sepolia/DefifaDeployer.json +0 -1725
  23. package/deployments/defifa-v5/sepolia/DefifaGovernor.json +0 -1543
  24. package/deployments/defifa-v5/sepolia/DefifaTokenUriResolver.json +0 -301
  25. package/foundry.lock +0 -17
@@ -0,0 +1,272 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ pragma solidity 0.8.26;
3
+
4
+ import "forge-std/Test.sol";
5
+ import "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
6
+
7
+ import {DefifaGovernor} from "../../src/DefifaGovernor.sol";
8
+ import {DefifaDeployer} from "../../src/DefifaDeployer.sol";
9
+ import {DefifaHook} from "../../src/DefifaHook.sol";
10
+ import {DefifaTokenUriResolver} from "../../src/DefifaTokenUriResolver.sol";
11
+ import {DefifaScorecardState} from "../../src/enums/DefifaScorecardState.sol";
12
+ import {IDefifaGovernor} from "../../src/interfaces/IDefifaGovernor.sol";
13
+ import {IDefifaDeployer} from "../../src/interfaces/IDefifaDeployer.sol";
14
+ import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
15
+
16
+ import {JBMetadataResolver} from "@bananapus/core-v6/src/libraries/JBMetadataResolver.sol";
17
+ import {MetadataResolverHelper} from "@bananapus/core-v6/test/helpers/MetadataResolverHelper.sol";
18
+ import {JBTest} from "@bananapus/core-v6/test/helpers/JBTest.sol";
19
+ import {JBRulesetMetadataResolver} from "@bananapus/core-v6/src/libraries/JBRulesetMetadataResolver.sol";
20
+ import {
21
+ JB721TiersRulesetMetadataResolver
22
+ } from "@bananapus/721-hook-v6/src/libraries/JB721TiersRulesetMetadataResolver.sol";
23
+ import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
24
+
25
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
26
+ import {ITypeface} from "lib/typeface/contracts/interfaces/ITypeface.sol";
27
+ import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
28
+ import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
29
+ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
30
+ import {DefifaDelegation} from "../../src/structs/DefifaDelegation.sol";
31
+ import {DefifaLaunchProjectData} from "../../src/structs/DefifaLaunchProjectData.sol";
32
+ import {DefifaTierParams} from "../../src/structs/DefifaTierParams.sol";
33
+ import {DefifaTierCashOutWeight} from "../../src/structs/DefifaTierCashOutWeight.sol";
34
+
35
+ /// @dev Helper to read block.timestamp via an external call, bypassing the via-ir optimizer's timestamp caching.
36
+ contract TimestampReader3 {
37
+ function timestamp() external view returns (uint256) {
38
+ return block.timestamp;
39
+ }
40
+ }
41
+
42
+ /// @title M36_FulfillmentBlocksRatification
43
+ /// @notice Regression test: ratification should succeed even when fulfillCommitmentsOf reverts.
44
+ /// @dev Tests the try-catch wrapper around fulfillCommitmentsOf in ratifyScorecardFrom.
45
+ /// The test verifies that the FulfillmentFailed event is emitted and ratification completes.
46
+ contract M36_FulfillmentBlocksRatification is JBTest, TestBaseWorkflow {
47
+ using JBRulesetMetadataResolver for JBRuleset;
48
+
49
+ TimestampReader3 private _tsReader = new TimestampReader3();
50
+
51
+ address _protocolFeeProjectTokenAccount;
52
+ address _defifaProjectTokenAccount;
53
+ uint256 _protocolFeeProjectId;
54
+ uint256 _defifaProjectId;
55
+ uint256 _gameId = 3;
56
+
57
+ DefifaDeployer deployer;
58
+ DefifaHook hook;
59
+ DefifaGovernor governor;
60
+
61
+ address projectOwner = address(bytes20(keccak256("projectOwner")));
62
+
63
+ function setUp() public virtual override {
64
+ super.setUp();
65
+
66
+ JBAccountingContext[] memory _tokens = new JBAccountingContext[](1);
67
+ _tokens[0] = JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH});
68
+
69
+ JBTerminalConfig[] memory terminalConfigs = new JBTerminalConfig[](1);
70
+ terminalConfigs[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: _tokens});
71
+
72
+ JBRulesetConfig[] memory rulesetConfigs = new JBRulesetConfig[](1);
73
+ rulesetConfigs[0] = JBRulesetConfig({
74
+ mustStartAtOrAfter: 0,
75
+ duration: 10 days,
76
+ weight: 1e18,
77
+ weightCutPercent: 0,
78
+ approvalHook: IJBRulesetApprovalHook(address(0)),
79
+ metadata: JBRulesetMetadata({
80
+ reservedPercent: 0,
81
+ cashOutTaxRate: 0,
82
+ baseCurrency: JBCurrencyIds.ETH,
83
+ pausePay: false,
84
+ pauseCreditTransfers: false,
85
+ allowOwnerMinting: false,
86
+ allowSetCustomToken: false,
87
+ allowTerminalMigration: false,
88
+ allowSetTerminals: false,
89
+ allowSetController: false,
90
+ allowAddAccountingContext: false,
91
+ allowAddPriceFeed: false,
92
+ ownerMustSendPayouts: false,
93
+ holdFees: false,
94
+ useTotalSurplusForCashOuts: false,
95
+ useDataHookForPay: true,
96
+ useDataHookForCashOut: true,
97
+ dataHook: address(0),
98
+ metadata: 0
99
+ }),
100
+ splitGroups: new JBSplitGroup[](0),
101
+ fundAccessLimitGroups: new JBFundAccessLimitGroup[](0)
102
+ });
103
+
104
+ _protocolFeeProjectId =
105
+ jbController().launchProjectFor(address(projectOwner), "", rulesetConfigs, terminalConfigs, "");
106
+ vm.prank(projectOwner);
107
+ _protocolFeeProjectTokenAccount =
108
+ address(jbController().deployERC20For(_protocolFeeProjectId, "Bananapus", "NANA", bytes32(0)));
109
+
110
+ _defifaProjectId =
111
+ jbController().launchProjectFor(address(projectOwner), "", rulesetConfigs, terminalConfigs, "");
112
+ vm.prank(projectOwner);
113
+ _defifaProjectTokenAccount =
114
+ address(jbController().deployERC20For(_defifaProjectId, "Defifa", "DEFIFA", bytes32(0)));
115
+
116
+ hook = new DefifaHook(
117
+ jbDirectory(), IERC20(address(_defifaProjectTokenAccount)), IERC20(_protocolFeeProjectTokenAccount)
118
+ );
119
+ governor = new DefifaGovernor(jbController(), address(this));
120
+ JBAddressRegistry _registry = new JBAddressRegistry();
121
+ DefifaTokenUriResolver _tokenURIResolver = new DefifaTokenUriResolver(ITypeface(address(0)));
122
+ deployer = new DefifaDeployer(
123
+ address(hook),
124
+ _tokenURIResolver,
125
+ governor,
126
+ jbController(),
127
+ _registry,
128
+ _defifaProjectId,
129
+ _protocolFeeProjectId
130
+ );
131
+
132
+ hook.transferOwnership(address(deployer));
133
+ governor.transferOwnership(address(deployer));
134
+ }
135
+
136
+ /// @notice Test that ratification emits FulfillmentFailed when fulfillment reverts,
137
+ /// but the scorecard is still ratified.
138
+ /// @dev We mock fulfillCommitmentsOf to revert, then verify the ratification still succeeds.
139
+ function test_ratificationSucceedsWhenFulfillmentReverts() public {
140
+ uint8 nTiers = 4;
141
+ address[] memory _users = new address[](nTiers);
142
+ DefifaLaunchProjectData memory defifaData = _getBasicLaunchData(nTiers);
143
+ (uint256 _projectId, DefifaHook _nft, DefifaGovernor _governor) = _createProject(defifaData);
144
+
145
+ // Phase 1: Mint
146
+ vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
147
+ for (uint256 i = 0; i < nTiers; i++) {
148
+ _users[i] = address(bytes20(keccak256(abi.encode("user", Strings.toString(i)))));
149
+ vm.deal(_users[i], 1 ether);
150
+ uint16[] memory rawMetadata = new uint16[](1);
151
+ rawMetadata[0] = uint16(i + 1);
152
+ bytes memory metadata = _buildPayMetadata(abi.encode(_users[i], rawMetadata));
153
+ vm.prank(_users[i]);
154
+ jbMultiTerminal().pay{value: 1 ether}(
155
+ _projectId, JBConstants.NATIVE_TOKEN, 1 ether, _users[i], 0, "", metadata
156
+ );
157
+ vm.warp(_tsReader.timestamp() + 1);
158
+ }
159
+
160
+ // Warp to scoring phase
161
+ vm.warp(defifaData.start + 1);
162
+
163
+ // Build scorecards
164
+ DefifaTierCashOutWeight[] memory scorecards = new DefifaTierCashOutWeight[](nTiers);
165
+ uint256 weightPerTier = _nft.TOTAL_CASHOUT_WEIGHT() / nTiers;
166
+ uint256 assigned;
167
+ for (uint256 i = 0; i < nTiers; i++) {
168
+ scorecards[i].id = i + 1;
169
+ scorecards[i].cashOutWeight = weightPerTier;
170
+ assigned += weightPerTier;
171
+ }
172
+ if (assigned < _nft.TOTAL_CASHOUT_WEIGHT()) {
173
+ scorecards[0].cashOutWeight += _nft.TOTAL_CASHOUT_WEIGHT() - assigned;
174
+ }
175
+
176
+ // Submit and attest
177
+ uint256 _proposalId = _governor.submitScorecardFor(_gameId, scorecards);
178
+ vm.warp(_tsReader.timestamp() + _governor.attestationStartTimeOf(_gameId) + 1);
179
+ for (uint256 i = 0; i < _users.length; i++) {
180
+ vm.prank(_users[i]);
181
+ _governor.attestToScorecardFrom(_gameId, _proposalId);
182
+ }
183
+ vm.warp(_tsReader.timestamp() + _governor.attestationGracePeriodOf(_gameId) + 1);
184
+
185
+ // Mock fulfillCommitmentsOf to revert
186
+ address _deployer = jbController().PROJECTS().ownerOf(_gameId);
187
+ vm.mockCallRevert(
188
+ _deployer,
189
+ abi.encodeWithSelector(IDefifaDeployer.fulfillCommitmentsOf.selector, _gameId),
190
+ abi.encodeWithSignature("Error(string)", "simulated fulfillment failure")
191
+ );
192
+
193
+ // Ratification should succeed even though fulfillment will revert.
194
+ // We expect the FulfillmentFailed event to be emitted.
195
+ vm.expectEmit(true, false, false, false);
196
+ emit IDefifaGovernor.FulfillmentFailed(_gameId, "");
197
+
198
+ _governor.ratifyScorecardFrom(_gameId, scorecards);
199
+
200
+ // Verify the scorecard was ratified
201
+ assertEq(
202
+ _governor.ratifiedScorecardIdOf(_gameId),
203
+ _proposalId,
204
+ "Scorecard should be ratified despite fulfillment failure"
205
+ );
206
+
207
+ // Verify the state is RATIFIED
208
+ assertEq(
209
+ uint256(_governor.stateOf(_gameId, _proposalId)),
210
+ uint256(DefifaScorecardState.RATIFIED),
211
+ "Scorecard state should be RATIFIED"
212
+ );
213
+ }
214
+
215
+ // ----- Internal helpers ------
216
+
217
+ function _getBasicLaunchData(uint8 nTiers) internal returns (DefifaLaunchProjectData memory) {
218
+ DefifaTierParams[] memory tierParams = new DefifaTierParams[](nTiers);
219
+ for (uint256 i = 0; i < nTiers; i++) {
220
+ tierParams[i] = DefifaTierParams({
221
+ reservedRate: 1001,
222
+ reservedTokenBeneficiary: address(0),
223
+ encodedIPFSUri: bytes32(0),
224
+ shouldUseReservedTokenBeneficiaryAsDefault: false,
225
+ name: "DEFIFA"
226
+ });
227
+ }
228
+
229
+ return DefifaLaunchProjectData({
230
+ name: "DEFIFA",
231
+ projectUri: "",
232
+ contractUri: "",
233
+ baseUri: "",
234
+ tierPrice: 1 ether,
235
+ token: JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH}),
236
+ mintPeriodDuration: 1 days,
237
+ start: uint48(block.timestamp + 3 days),
238
+ refundPeriodDuration: 1 days,
239
+ store: new JB721TiersHookStore(),
240
+ splits: new JBSplit[](0),
241
+ attestationStartTime: 0,
242
+ attestationGracePeriod: 100_381,
243
+ defaultAttestationDelegate: address(0),
244
+ tiers: tierParams,
245
+ defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
246
+ terminal: jbMultiTerminal(),
247
+ minParticipation: 0,
248
+ scorecardTimeout: 0
249
+ });
250
+ }
251
+
252
+ function _createProject(DefifaLaunchProjectData memory defifaLaunchData)
253
+ internal
254
+ returns (uint256 projectId, DefifaHook nft, DefifaGovernor _governor)
255
+ {
256
+ _governor = governor;
257
+ (projectId) = deployer.launchGameWith(defifaLaunchData);
258
+ JBRuleset memory _fc = jbRulesets().currentOf(projectId);
259
+ if (_fc.dataHook() == address(0)) {
260
+ (_fc,) = jbRulesets().latestQueuedOf(projectId);
261
+ }
262
+ nft = DefifaHook(_fc.dataHook());
263
+ }
264
+
265
+ function _buildPayMetadata(bytes memory metadata) internal returns (bytes memory) {
266
+ bytes[] memory data = new bytes[](1);
267
+ data[0] = metadata;
268
+ bytes4[] memory ids = new bytes4[](1);
269
+ ids[0] = metadataHelper().getId("pay", address(hook));
270
+ return metadataHelper().createMetadata(ids, data);
271
+ }
272
+ }
package/.gas-snapshot DELETED
@@ -1,2 +0,0 @@
1
- DefifaGovernorTest:testReceiveVotingPower(uint8,uint8) (runs: 256, μ: 13609553, ~: 12959755)
2
- DefifaGovernorTest:testSetRedemptionRates(bool) (runs: 256, μ: 16433285, ~: 16434669)