@ballkidz/defifa 0.0.2 → 0.0.4

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.
@@ -135,37 +135,6 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
135
135
  // ------------------------- external views -------------------------- //
136
136
  //*********************************************************************//
137
137
 
138
- /// @notice The game times.
139
- /// @param gameId The ID of the game for which the game times apply.
140
- /// @return The game's start time, as a unix timestamp.
141
- /// @return The game's minting period duration, in seconds.
142
- /// @return The game's refund period duration, in seconds.
143
- function timesFor(uint256 gameId) external view override returns (uint48, uint24, uint24) {
144
- DefifaOpsData memory _ops = _opsOf[gameId];
145
- return (_ops.start, _ops.mintPeriodDuration, _ops.refundPeriodDuration);
146
- }
147
-
148
- /// @notice The token of a game.
149
- /// @param gameId The ID of the game to get the token of.
150
- /// @return The game's token.
151
- function tokenOf(uint256 gameId) external view override returns (address) {
152
- return _opsOf[gameId].token;
153
- }
154
-
155
- /// @notice The safety mechanism parameters of a game.
156
- /// @param gameId The ID of the game to get the safety params of.
157
- /// @return minParticipation The minimum treasury balance for the game to proceed to scoring.
158
- /// @return scorecardTimeout The maximum time after scoring begins for a scorecard to be ratified.
159
- function safetyParamsOf(uint256 gameId)
160
- external
161
- view
162
- override
163
- returns (uint256 minParticipation, uint32 scorecardTimeout)
164
- {
165
- DefifaOpsData memory _ops = _opsOf[gameId];
166
- return (_ops.minParticipation, _ops.scorecardTimeout);
167
- }
168
-
169
138
  /// @notice The current pot the game is being played with.
170
139
  /// @param gameId The ID of the game for which the pot applies.
171
140
  /// @param includeCommitments A flag indicating if the portion of the pot committed to fulfill preprogrammed
@@ -214,6 +183,37 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
214
183
  return _currentRuleset.duration != 0 && _currentRuleset.id == _queuedRuleset.id;
215
184
  }
216
185
 
186
+ /// @notice The safety mechanism parameters of a game.
187
+ /// @param gameId The ID of the game to get the safety params of.
188
+ /// @return minParticipation The minimum treasury balance for the game to proceed to scoring.
189
+ /// @return scorecardTimeout The maximum time after scoring begins for a scorecard to be ratified.
190
+ function safetyParamsOf(uint256 gameId)
191
+ external
192
+ view
193
+ override
194
+ returns (uint256 minParticipation, uint32 scorecardTimeout)
195
+ {
196
+ DefifaOpsData memory _ops = _opsOf[gameId];
197
+ return (_ops.minParticipation, _ops.scorecardTimeout);
198
+ }
199
+
200
+ /// @notice The game times.
201
+ /// @param gameId The ID of the game for which the game times apply.
202
+ /// @return The game's start time, as a unix timestamp.
203
+ /// @return The game's minting period duration, in seconds.
204
+ /// @return The game's refund period duration, in seconds.
205
+ function timesFor(uint256 gameId) external view override returns (uint48, uint24, uint24) {
206
+ DefifaOpsData memory _ops = _opsOf[gameId];
207
+ return (_ops.start, _ops.mintPeriodDuration, _ops.refundPeriodDuration);
208
+ }
209
+
210
+ /// @notice The token of a game.
211
+ /// @param gameId The ID of the game to get the token of.
212
+ /// @return The game's token.
213
+ function tokenOf(uint256 gameId) external view override returns (address) {
214
+ return _opsOf[gameId].token;
215
+ }
216
+
217
217
  //*********************************************************************//
218
218
  // -------------------------- public views --------------------------- //
219
219
  //*********************************************************************//
@@ -297,6 +297,96 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
297
297
  // ---------------------- external transactions ---------------------- //
298
298
  //*********************************************************************//
299
299
 
300
+ /// @notice Fulfill split amounts between all splits for a game.
301
+ /// @param gameId The ID of the game to fulfill splits for.
302
+ function fulfillCommitmentsOf(uint256 gameId) external virtual override {
303
+ // Make sure commitments haven't already been fulfilled.
304
+ if (fulfilledCommitmentsOf[gameId] != 0) return;
305
+
306
+ // Get the game's current funding cycle along with its metadata.
307
+ // slither-disable-next-line unused-return
308
+ (, JBRulesetMetadata memory _metadata) = controller.currentRulesetOf(gameId);
309
+
310
+ // Make sure the game's commitments can be fulfilled.
311
+ if (!IDefifaHook(_metadata.dataHook).cashOutWeightIsSet()) {
312
+ revert DefifaDeployer_CantFulfillYet();
313
+ }
314
+
315
+ // Get the game token and the terminal.
316
+ address _token = _opsOf[gameId].token;
317
+ IJBMultiTerminal _terminal =
318
+ IJBMultiTerminal(address(controller.DIRECTORY().primaryTerminalOf({projectId: gameId, token: _token})));
319
+
320
+ // Get the current pot and store it. This also prevents re-entrance since the check above will return early.
321
+ uint256 _pot = _terminal.STORE().balanceOf({terminal: address(_terminal), projectId: gameId, token: _token});
322
+ // slither-disable-next-line incorrect-equality
323
+ if (_pot == 0) revert DefifaDeployer_NothingToFulfill();
324
+
325
+ // Compute the fee amount based on the total absolute split percent stored at game creation.
326
+ uint256 _feeAmount = mulDiv(_pot, _commitmentPercentOf[gameId], JBConstants.SPLITS_TOTAL_PERCENT);
327
+
328
+ // Store the actual fee amount for accurate currentGamePotOf reporting.
329
+ // Use max(feeAmount, 1) to preserve the reentrancy guard when pot is 0.
330
+ fulfilledCommitmentsOf[gameId] = _feeAmount > 0 ? _feeAmount : 1;
331
+
332
+ // Send only the fee portion as payouts. The remaining balance stays as surplus for cash-outs.
333
+ // slither-disable-next-line unused-return
334
+ _terminal.sendPayoutsOf({
335
+ projectId: gameId,
336
+ token: _token,
337
+ amount: _feeAmount,
338
+ currency: _token == JBConstants.NATIVE_TOKEN ? _metadata.baseCurrency : uint32(uint160(_token)),
339
+ minTokensPaidOut: _feeAmount
340
+ });
341
+
342
+ // Queue the final ruleset.
343
+ JBRulesetConfig[] memory rulesetConfigs = new JBRulesetConfig[](1);
344
+ rulesetConfigs[0] = JBRulesetConfig({
345
+ mustStartAtOrAfter: 0,
346
+ duration: 0,
347
+ weight: 0,
348
+ weightCutPercent: 0,
349
+ approvalHook: IJBRulesetApprovalHook(address(0)),
350
+ metadata: JBRulesetMetadata({
351
+ reservedPercent: 0,
352
+ cashOutTaxRate: 0,
353
+ baseCurrency: _metadata.baseCurrency,
354
+ pausePay: true,
355
+ pauseCreditTransfers: false,
356
+ allowOwnerMinting: false,
357
+ allowSetCustomToken: false,
358
+ allowTerminalMigration: false,
359
+ allowSetTerminals: false,
360
+ allowSetController: false,
361
+ allowAddAccountingContext: false,
362
+ allowAddPriceFeed: false,
363
+ // Set this to true so only the deployer can fulfill the commitments.
364
+ ownerMustSendPayouts: true,
365
+ holdFees: false,
366
+ useTotalSurplusForCashOuts: false,
367
+ useDataHookForPay: true,
368
+ useDataHookForCashOut: true,
369
+ dataHook: _metadata.dataHook,
370
+ metadata: uint16(
371
+ JB721TiersRulesetMetadataResolver.pack721TiersRulesetMetadata(
372
+ JB721TiersRulesetMetadata({pauseTransfers: false, pauseMintPendingReserves: false})
373
+ )
374
+ )
375
+ }),
376
+ // No more payouts.
377
+ splitGroups: new JBSplitGroup[](0),
378
+ fundAccessLimitGroups: new JBFundAccessLimitGroup[](0)
379
+ });
380
+
381
+ // Update the ruleset to the final one.
382
+ // slither-disable-next-line unused-return
383
+ controller.queueRulesetsOf({
384
+ projectId: gameId, rulesetConfigurations: rulesetConfigs, memo: "Defifa game has finished."
385
+ });
386
+
387
+ emit FulfilledCommitments({gameId: gameId, pot: _pot, caller: msg.sender});
388
+ }
389
+
300
390
  /// @notice Launches a new game owned by this contract with a DefifaHook attached.
301
391
  /// @param launchProjectData Data necessary to fulfill the transaction to launch a game.
302
392
  /// @return gameId The ID of the newly configured game.
@@ -482,94 +572,9 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
482
572
  emit LaunchGame(gameId, _hook, governor, _uriResolver, msg.sender);
483
573
  }
484
574
 
485
- /// @notice Fulfill split amounts between all splits for a game.
486
- /// @param gameId The ID of the game to fulfill splits for.
487
- function fulfillCommitmentsOf(uint256 gameId) external virtual override {
488
- // Make sure commitments haven't already been fulfilled.
489
- if (fulfilledCommitmentsOf[gameId] != 0) return;
490
-
491
- // Get the game's current funding cycle along with its metadata.
492
- // slither-disable-next-line unused-return
493
- (, JBRulesetMetadata memory _metadata) = controller.currentRulesetOf(gameId);
494
-
495
- // Make sure the game's commitments can be fulfilled.
496
- if (!IDefifaHook(_metadata.dataHook).cashOutWeightIsSet()) {
497
- revert DefifaDeployer_CantFulfillYet();
498
- }
499
-
500
- // Get the game token and the terminal.
501
- address _token = _opsOf[gameId].token;
502
- IJBMultiTerminal _terminal =
503
- IJBMultiTerminal(address(controller.DIRECTORY().primaryTerminalOf({projectId: gameId, token: _token})));
504
-
505
- // Get the current pot and store it. This also prevents re-entrance since the check above will return early.
506
- uint256 _pot = _terminal.STORE().balanceOf({terminal: address(_terminal), projectId: gameId, token: _token});
507
- // slither-disable-next-line incorrect-equality
508
- if (_pot == 0) revert DefifaDeployer_NothingToFulfill();
509
-
510
- // Compute the fee amount based on the total absolute split percent stored at game creation.
511
- uint256 _feeAmount = mulDiv(_pot, _commitmentPercentOf[gameId], JBConstants.SPLITS_TOTAL_PERCENT);
512
-
513
- // Store the actual fee amount for accurate currentGamePotOf reporting.
514
- // Use max(feeAmount, 1) to preserve the reentrancy guard when pot is 0.
515
- fulfilledCommitmentsOf[gameId] = _feeAmount > 0 ? _feeAmount : 1;
516
-
517
- // Send only the fee portion as payouts. The remaining balance stays as surplus for cash-outs.
518
- // slither-disable-next-line unused-return
519
- _terminal.sendPayoutsOf({
520
- projectId: gameId,
521
- token: _token,
522
- amount: _feeAmount,
523
- currency: _token == JBConstants.NATIVE_TOKEN ? _metadata.baseCurrency : uint32(uint160(_token)),
524
- minTokensPaidOut: _feeAmount
525
- });
526
-
527
- // Queue the final ruleset.
528
- JBRulesetConfig[] memory rulesetConfigs = new JBRulesetConfig[](1);
529
- rulesetConfigs[0] = JBRulesetConfig({
530
- mustStartAtOrAfter: 0,
531
- duration: 0,
532
- weight: 0,
533
- weightCutPercent: 0,
534
- approvalHook: IJBRulesetApprovalHook(address(0)),
535
- metadata: JBRulesetMetadata({
536
- reservedPercent: 0,
537
- cashOutTaxRate: 0,
538
- baseCurrency: _metadata.baseCurrency,
539
- pausePay: true,
540
- pauseCreditTransfers: false,
541
- allowOwnerMinting: false,
542
- allowSetCustomToken: false,
543
- allowTerminalMigration: false,
544
- allowSetTerminals: false,
545
- allowSetController: false,
546
- allowAddAccountingContext: false,
547
- allowAddPriceFeed: false,
548
- // Set this to true so only the deployer can fulfill the commitments.
549
- ownerMustSendPayouts: true,
550
- holdFees: false,
551
- useTotalSurplusForCashOuts: false,
552
- useDataHookForPay: true,
553
- useDataHookForCashOut: true,
554
- dataHook: _metadata.dataHook,
555
- metadata: uint16(
556
- JB721TiersRulesetMetadataResolver.pack721TiersRulesetMetadata(
557
- JB721TiersRulesetMetadata({pauseTransfers: false, pauseMintPendingReserves: false})
558
- )
559
- )
560
- }),
561
- // No more payouts.
562
- splitGroups: new JBSplitGroup[](0),
563
- fundAccessLimitGroups: new JBFundAccessLimitGroup[](0)
564
- });
565
-
566
- // Update the ruleset to the final one.
567
- // slither-disable-next-line unused-return
568
- controller.queueRulesetsOf({
569
- projectId: gameId, rulesetConfigurations: rulesetConfigs, memo: "Defifa game has finished."
570
- });
571
-
572
- emit FulfilledCommitments({gameId: gameId, pot: _pot, caller: msg.sender});
575
+ /// @notice Allows this contract to receive 721s.
576
+ function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) {
577
+ return IERC721Receiver.onERC721Received.selector;
573
578
  }
574
579
 
575
580
  /// @notice Triggers the no-contest refund mechanism for a game.
@@ -639,11 +644,6 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
639
644
  emit QueuedNoContest(gameId, msg.sender);
640
645
  }
641
646
 
642
- /// @notice Allows this contract to receive 721s.
643
- function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) {
644
- return IERC721Receiver.onERC721Received.selector;
645
- }
646
-
647
647
  //*********************************************************************//
648
648
  // ------------------------ internal functions ----------------------- //
649
649
  //*********************************************************************//
@@ -123,49 +123,12 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
123
123
  // -------------------------- public views --------------------------- //
124
124
  //*********************************************************************//
125
125
 
126
- /// @notice The state of a proposal.
127
- /// @param gameId The ID of the game to get a proposal state of.
128
- /// @param scorecardId The ID of the proposal to get the state of.
129
- /// @return The state.
130
- /// @dev Boundary semantics (inclusive):
131
- /// - At exactly `attestationsBegin`, the state transitions from PENDING to ACTIVE (attestations are open).
132
- /// - At exactly `gracePeriodEnds`, the grace period has elapsed and the state transitions from ACTIVE to
133
- /// SUCCEEDED (if quorum is met) or remains ACTIVE (if not).
134
- function stateOf(uint256 gameId, uint256 scorecardId) public view virtual override returns (DefifaScorecardState) {
135
- // Keep a reference to the ratified scorecard ID.
136
- uint256 _ratifiedScorecardId = ratifiedScorecardIdOf[gameId];
137
-
138
- // If the game has already ratified a scorecard, return succeeded if the ratified proposal is being checked.
139
- // Else return defeated.
140
- if (_ratifiedScorecardId != 0) {
141
- return _ratifiedScorecardId == scorecardId ? DefifaScorecardState.RATIFIED : DefifaScorecardState.DEFEATED;
142
- }
143
-
144
- // Get a reference to the scorecard.
145
- DefifaScorecard memory _scorecard = _scorecardOf[gameId][scorecardId];
146
-
147
- // Make sure the proposal is known.
148
- // slither-disable-next-line incorrect-equality
149
- if (_scorecard.attestationsBegin == 0) {
150
- revert DefifaGovernor_UnknownProposal();
151
- }
152
-
153
- // If the scorecard has attestations beginning in the future, the state is PENDING.
154
- // At exactly `attestationsBegin`, attestations are open so the state is ACTIVE.
155
- if (_scorecard.attestationsBegin > block.timestamp) {
156
- return DefifaScorecardState.PENDING;
157
- }
158
-
159
- // If the scorecard's grace period has not yet ended, the state is ACTIVE.
160
- // At exactly `gracePeriodEnds`, the grace period has elapsed so we fall through to the quorum check.
161
- if (_scorecard.gracePeriodEnds > block.timestamp) {
162
- return DefifaScorecardState.ACTIVE;
163
- }
164
-
165
- // If quorum has been reached, the state is SUCCEEDED, otherwise it is ACTIVE.
166
- return quorum(gameId) <= _scorecardAttestationsOf[gameId][scorecardId].count
167
- ? DefifaScorecardState.SUCCEEDED
168
- : DefifaScorecardState.ACTIVE;
126
+ /// @notice The amount of time that must go by before a scorecard can be ratified.
127
+ /// @param gameId The ID of the game to get the attestation period of.
128
+ /// @return The attestation period in number of blocks.
129
+ function attestationGracePeriodOf(uint256 gameId) public view override returns (uint256) {
130
+ // attestation grace period in bits 48-95 (48 bits).
131
+ return uint256(uint48(_packedScorecardInfoOf[gameId] >> 48));
169
132
  }
170
133
 
171
134
  /// @notice The amount of time between a scorecard being submitted and attestations to it being enabled, measured in
@@ -179,43 +142,6 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
179
142
  return uint256(uint48(_packedScorecardInfoOf[gameId]));
180
143
  }
181
144
 
182
- /// @notice The amount of time that must go by before a scorecard can be ratified.
183
- /// @param gameId The ID of the game to get the attestation period of.
184
- /// @return The attestation period in number of blocks.
185
- function attestationGracePeriodOf(uint256 gameId) public view override returns (uint256) {
186
- // attestation grace period in bits 48-95 (48 bits).
187
- return uint256(uint48(_packedScorecardInfoOf[gameId] >> 48));
188
- }
189
-
190
- /// @notice The number of attestation units that must have participated in a proposal for it to be ratified.
191
- /// @dev Each tier with at least one minted token contributes MAX_ATTESTATION_POWER_TIER to the total
192
- /// eligible weight. Quorum is 50% of this total. Because every tier has equal max attestation power
193
- /// regardless of supply, each tier's community has equal influence — a tier with 1 token and a tier
194
- /// with 100 tokens both cap at MAX_ATTESTATION_POWER_TIER when fully attested. This prevents
195
- /// high-supply tiers from dominating governance, keeping the game fair across all outcomes.
196
- /// @return The quorum number of attestations.
197
- function quorum(uint256 gameId) public view override returns (uint256) {
198
- // Get the game's current funding cycle along with its metadata.
199
- // slither-disable-next-line unused-return
200
- (, JBRulesetMetadata memory _metadata) = controller.currentRulesetOf(gameId);
201
-
202
- // Get a reference to the number of tiers.
203
- uint256 _numberOfTiers = IDefifaHook(_metadata.dataHook).store().maxTierIdOf(_metadata.dataHook);
204
-
205
- // Keep a reference to the total eligible tier weight.
206
- uint256 _eligibleTierWeights;
207
-
208
- for (uint256 _i; _i < _numberOfTiers; _i++) {
209
- // Each minted tier contributes MAX_ATTESTATION_POWER_TIER to the quorum denominator.
210
- if (IDefifaHook(_metadata.dataHook).currentSupplyOfTier(_i + 1) != 0) {
211
- _eligibleTierWeights += MAX_ATTESTATION_POWER_TIER;
212
- }
213
- }
214
-
215
- // Quorum = 50% of all minted tiers' attestation power.
216
- return _eligibleTierWeights / 2;
217
- }
218
-
219
145
  /// @notice Gets an account's attestation power given a number of tiers to look through.
220
146
  /// @dev An account's power per tier = MAX_ATTESTATION_POWER_TIER * (account's units / tier's total units).
221
147
  /// This means within a tier, power is proportional to token holdings, but across tiers, each tier's
@@ -267,6 +193,80 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
267
193
  }
268
194
  }
269
195
 
196
+ /// @notice The number of attestation units that must have participated in a proposal for it to be ratified.
197
+ /// @dev Each tier with at least one minted token contributes MAX_ATTESTATION_POWER_TIER to the total
198
+ /// eligible weight. Quorum is 50% of this total. Because every tier has equal max attestation power
199
+ /// regardless of supply, each tier's community has equal influence — a tier with 1 token and a tier
200
+ /// with 100 tokens both cap at MAX_ATTESTATION_POWER_TIER when fully attested. This prevents
201
+ /// high-supply tiers from dominating governance, keeping the game fair across all outcomes.
202
+ /// @return The quorum number of attestations.
203
+ function quorum(uint256 gameId) public view override returns (uint256) {
204
+ // Get the game's current funding cycle along with its metadata.
205
+ // slither-disable-next-line unused-return
206
+ (, JBRulesetMetadata memory _metadata) = controller.currentRulesetOf(gameId);
207
+
208
+ // Get a reference to the number of tiers.
209
+ uint256 _numberOfTiers = IDefifaHook(_metadata.dataHook).store().maxTierIdOf(_metadata.dataHook);
210
+
211
+ // Keep a reference to the total eligible tier weight.
212
+ uint256 _eligibleTierWeights;
213
+
214
+ for (uint256 _i; _i < _numberOfTiers; _i++) {
215
+ // Each minted tier contributes MAX_ATTESTATION_POWER_TIER to the quorum denominator.
216
+ if (IDefifaHook(_metadata.dataHook).currentSupplyOfTier(_i + 1) != 0) {
217
+ _eligibleTierWeights += MAX_ATTESTATION_POWER_TIER;
218
+ }
219
+ }
220
+
221
+ // Quorum = 50% of all minted tiers' attestation power.
222
+ return _eligibleTierWeights / 2;
223
+ }
224
+
225
+ /// @notice The state of a proposal.
226
+ /// @param gameId The ID of the game to get a proposal state of.
227
+ /// @param scorecardId The ID of the proposal to get the state of.
228
+ /// @return The state.
229
+ /// @dev Boundary semantics (inclusive):
230
+ /// - At exactly `attestationsBegin`, the state transitions from PENDING to ACTIVE (attestations are open).
231
+ /// - At exactly `gracePeriodEnds`, the grace period has elapsed and the state transitions from ACTIVE to
232
+ /// SUCCEEDED (if quorum is met) or remains ACTIVE (if not).
233
+ function stateOf(uint256 gameId, uint256 scorecardId) public view virtual override returns (DefifaScorecardState) {
234
+ // Keep a reference to the ratified scorecard ID.
235
+ uint256 _ratifiedScorecardId = ratifiedScorecardIdOf[gameId];
236
+
237
+ // If the game has already ratified a scorecard, return succeeded if the ratified proposal is being checked.
238
+ // Else return defeated.
239
+ if (_ratifiedScorecardId != 0) {
240
+ return _ratifiedScorecardId == scorecardId ? DefifaScorecardState.RATIFIED : DefifaScorecardState.DEFEATED;
241
+ }
242
+
243
+ // Get a reference to the scorecard.
244
+ DefifaScorecard memory _scorecard = _scorecardOf[gameId][scorecardId];
245
+
246
+ // Make sure the proposal is known.
247
+ // slither-disable-next-line incorrect-equality
248
+ if (_scorecard.attestationsBegin == 0) {
249
+ revert DefifaGovernor_UnknownProposal();
250
+ }
251
+
252
+ // If the scorecard has attestations beginning in the future, the state is PENDING.
253
+ // At exactly `attestationsBegin`, attestations are open so the state is ACTIVE.
254
+ if (_scorecard.attestationsBegin > block.timestamp) {
255
+ return DefifaScorecardState.PENDING;
256
+ }
257
+
258
+ // If the scorecard's grace period has not yet ended, the state is ACTIVE.
259
+ // At exactly `gracePeriodEnds`, the grace period has elapsed so we fall through to the quorum check.
260
+ if (_scorecard.gracePeriodEnds > block.timestamp) {
261
+ return DefifaScorecardState.ACTIVE;
262
+ }
263
+
264
+ // If quorum has been reached, the state is SUCCEEDED, otherwise it is ACTIVE.
265
+ return quorum(gameId) <= _scorecardAttestationsOf[gameId][scorecardId].count
266
+ ? DefifaScorecardState.SUCCEEDED
267
+ : DefifaScorecardState.ACTIVE;
268
+ }
269
+
270
270
  //*********************************************************************//
271
271
  // -------------------------- constructor ---------------------------- //
272
272
  //*********************************************************************//
@@ -316,79 +316,6 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
316
316
  // ---------------------- external transactions ---------------------- //
317
317
  //*********************************************************************//
318
318
 
319
- /// @notice Submits a scorecard to be attested to.
320
- /// @param _tierWeights The weights of each tier in the scorecard.
321
- /// @return scorecardId The scorecard's ID.
322
- function submitScorecardFor(
323
- uint256 _gameId,
324
- DefifaTierCashOutWeight[] calldata _tierWeights
325
- )
326
- external
327
- override
328
- returns (uint256 scorecardId)
329
- {
330
- // Make sure a proposal hasn't yet been ratified.
331
- if (ratifiedScorecardIdOf[_gameId] != 0) revert DefifaGovernor_AlreadyRatified();
332
-
333
- // Make sure the game has been initialized.
334
- // slither-disable-next-line incorrect-equality
335
- if (_packedScorecardInfoOf[_gameId] == 0) revert DefifaGovernor_GameNotFound();
336
-
337
- // Make sure no weight is assigned to an unowned tier.
338
- uint256 _numberOfTierWeights = _tierWeights.length;
339
-
340
- // Get the game's current funding cycle along with its metadata.
341
- // slither-disable-next-line unused-return
342
- (, JBRulesetMetadata memory _metadata) = controller.currentRulesetOf(_gameId);
343
-
344
- // Make sure the game is in its scoring phase.
345
- if (IDefifaHook(_metadata.dataHook).gamePhaseReporter().currentGamePhaseOf(_gameId) != DefifaGamePhase.SCORING)
346
- {
347
- revert DefifaGovernor_NotAllowed();
348
- }
349
-
350
- // If there's a weight assigned to the tier, make sure there is a token backed by it.
351
- for (uint256 _i; _i < _numberOfTierWeights; _i++) {
352
- if (
353
- _tierWeights[_i].cashOutWeight > 0
354
- && IDefifaHook(_metadata.dataHook).currentSupplyOfTier(_tierWeights[_i].id) == 0
355
- ) {
356
- revert DefifaGovernor_UnownedProposedCashoutValue();
357
- }
358
- }
359
-
360
- // Hash the scorecard.
361
- scorecardId =
362
- _hashScorecardOf({_gameHook: _metadata.dataHook, _calldata: _buildScorecardCalldataFor(_tierWeights)});
363
-
364
- // Store the scorecard
365
- DefifaScorecard storage _scorecard = _scorecardOf[_gameId][scorecardId];
366
- if (_scorecard.attestationsBegin != 0) revert DefifaGovernor_DuplicateScorecard();
367
-
368
- uint256 _attestationStartTime = attestationStartTimeOf(_gameId);
369
- uint256 _timeUntilAttestationsBegin =
370
- block.timestamp > _attestationStartTime ? 0 : _attestationStartTime - block.timestamp;
371
-
372
- uint48 _attestationsBegin = uint48(block.timestamp + _timeUntilAttestationsBegin);
373
- _scorecard.attestationsBegin = _attestationsBegin;
374
- // Grace period extends from when attestations begin, not from submission time.
375
- // This prevents the grace period from expiring before attestations even start
376
- // when a scorecard is submitted early.
377
- _scorecard.gracePeriodEnds = uint48(_attestationsBegin + attestationGracePeriodOf(_gameId));
378
-
379
- // Keep a reference to the default attestation delegate.
380
- address _defaultAttestationDelegate = IDefifaHook(_metadata.dataHook).defaultAttestationDelegate();
381
-
382
- // If the scorecard is being sent from the default attestation delegate, store it.
383
- if (msg.sender == _defaultAttestationDelegate) {
384
- defaultAttestationDelegateProposalOf[_gameId] = scorecardId;
385
- }
386
-
387
- emit ScorecardSubmitted(
388
- _gameId, scorecardId, _tierWeights, msg.sender == _defaultAttestationDelegate, msg.sender
389
- );
390
- }
391
-
392
319
  /// @notice Attests to a scorecard.
393
320
  /// @param gameId The ID of the game to which the scorecard belongs.
394
321
  /// @param scorecardId The scorecard ID.
@@ -480,6 +407,79 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
480
407
  emit ScorecardRatified(gameId, scorecardId, msg.sender);
481
408
  }
482
409
 
410
+ /// @notice Submits a scorecard to be attested to.
411
+ /// @param _tierWeights The weights of each tier in the scorecard.
412
+ /// @return scorecardId The scorecard's ID.
413
+ function submitScorecardFor(
414
+ uint256 _gameId,
415
+ DefifaTierCashOutWeight[] calldata _tierWeights
416
+ )
417
+ external
418
+ override
419
+ returns (uint256 scorecardId)
420
+ {
421
+ // Make sure a proposal hasn't yet been ratified.
422
+ if (ratifiedScorecardIdOf[_gameId] != 0) revert DefifaGovernor_AlreadyRatified();
423
+
424
+ // Make sure the game has been initialized.
425
+ // slither-disable-next-line incorrect-equality
426
+ if (_packedScorecardInfoOf[_gameId] == 0) revert DefifaGovernor_GameNotFound();
427
+
428
+ // Make sure no weight is assigned to an unowned tier.
429
+ uint256 _numberOfTierWeights = _tierWeights.length;
430
+
431
+ // Get the game's current funding cycle along with its metadata.
432
+ // slither-disable-next-line unused-return
433
+ (, JBRulesetMetadata memory _metadata) = controller.currentRulesetOf(_gameId);
434
+
435
+ // Make sure the game is in its scoring phase.
436
+ if (IDefifaHook(_metadata.dataHook).gamePhaseReporter().currentGamePhaseOf(_gameId) != DefifaGamePhase.SCORING)
437
+ {
438
+ revert DefifaGovernor_NotAllowed();
439
+ }
440
+
441
+ // If there's a weight assigned to the tier, make sure there is a token backed by it.
442
+ for (uint256 _i; _i < _numberOfTierWeights; _i++) {
443
+ if (
444
+ _tierWeights[_i].cashOutWeight > 0
445
+ && IDefifaHook(_metadata.dataHook).currentSupplyOfTier(_tierWeights[_i].id) == 0
446
+ ) {
447
+ revert DefifaGovernor_UnownedProposedCashoutValue();
448
+ }
449
+ }
450
+
451
+ // Hash the scorecard.
452
+ scorecardId =
453
+ _hashScorecardOf({_gameHook: _metadata.dataHook, _calldata: _buildScorecardCalldataFor(_tierWeights)});
454
+
455
+ // Store the scorecard
456
+ DefifaScorecard storage _scorecard = _scorecardOf[_gameId][scorecardId];
457
+ if (_scorecard.attestationsBegin != 0) revert DefifaGovernor_DuplicateScorecard();
458
+
459
+ uint256 _attestationStartTime = attestationStartTimeOf(_gameId);
460
+ uint256 _timeUntilAttestationsBegin =
461
+ block.timestamp > _attestationStartTime ? 0 : _attestationStartTime - block.timestamp;
462
+
463
+ uint48 _attestationsBegin = uint48(block.timestamp + _timeUntilAttestationsBegin);
464
+ _scorecard.attestationsBegin = _attestationsBegin;
465
+ // Grace period extends from when attestations begin, not from submission time.
466
+ // This prevents the grace period from expiring before attestations even start
467
+ // when a scorecard is submitted early.
468
+ _scorecard.gracePeriodEnds = uint48(_attestationsBegin + attestationGracePeriodOf(_gameId));
469
+
470
+ // Keep a reference to the default attestation delegate.
471
+ address _defaultAttestationDelegate = IDefifaHook(_metadata.dataHook).defaultAttestationDelegate();
472
+
473
+ // If the scorecard is being sent from the default attestation delegate, store it.
474
+ if (msg.sender == _defaultAttestationDelegate) {
475
+ defaultAttestationDelegateProposalOf[_gameId] = scorecardId;
476
+ }
477
+
478
+ emit ScorecardSubmitted(
479
+ _gameId, scorecardId, _tierWeights, msg.sender == _defaultAttestationDelegate, msg.sender
480
+ );
481
+ }
482
+
483
483
  //*********************************************************************//
484
484
  // ------------------------ internal functions ----------------------- //
485
485
  //*********************************************************************//