@ballkidz/defifa 0.0.31 → 0.0.34
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 +3 -1
- package/CHANGELOG.md +2 -0
- package/CRYPTO_ECON.md +3 -3
- package/RISKS.md +19 -2
- package/package.json +5 -5
- package/src/DefifaDeployer.sol +21 -6
- package/src/DefifaHook.sol +18 -10
- package/src/interfaces/IDefifaHook.sol +5 -0
- package/src/structs/DefifaLaunchProjectData.sol +3 -2
- package/src/structs/DefifaOpsData.sol +4 -3
package/AUDIT_INSTRUCTIONS.md
CHANGED
|
@@ -4,7 +4,9 @@ Defifa is a staged prediction-game system built on Juicebox and the tiered 721 s
|
|
|
4
4
|
|
|
5
5
|
## Audit Objective
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
There is a billion dollars of well-meaning projects' money in the Juicebox Money Engine, growing exponentially. Your job is to hack it before anyone else. Whoever hacks it first saves/steals the money, and you are obsessed with being this winner, while also being a steward of the protocol and wanting it to keep growing safely.
|
|
8
|
+
|
|
9
|
+
Suggestions of where to look:
|
|
8
10
|
|
|
9
11
|
- let players extract more than their fair share of the game pot
|
|
10
12
|
- break the game-phase lifecycle or allow actions in the wrong phase
|
package/CHANGELOG.md
CHANGED
|
@@ -23,6 +23,8 @@ This file instead describes the current v6 repo at a high level and the broad mi
|
|
|
23
23
|
## Local review remediations
|
|
24
24
|
|
|
25
25
|
- Reserve-minted NFTs are now excluded from refund calculations during MINT, REFUND, and NO_CONTEST phases. A public `isReserveMint` mapping tracks which tokens were created via tier reserve frequency rather than paid for. `beforeCashOutRecordedWith` subtracts their tier price from `cumulativeMintPrice`, preventing reserve beneficiaries from withdrawing funds they never contributed.
|
|
26
|
+
- One-tier games now revert at launch with `DefifaDeployer_InvalidGameConfiguration` if `scorecardTimeout == 0`. A one-tier game cannot reach quorum (the BWA multiplier reduces the sole tier's power to zero), so a zero timeout would leave funds permanently locked with no exit path. Enforcement moves this from a launcher-side responsibility (previously documented in `RISKS.md §8.6`) to a contract-level guarantee.
|
|
27
|
+
- `DefifaHook.mintReservesFor` now reverts with `DefifaHook_ReservedTokenMintingBlockedInNoContest` while the game is in `NO_CONTEST`. Reserve mints inflate `totalMintCost` so reserved recipients can claim fee tokens; without the block, a game that failed `minParticipation` could be revived back to SCORING via free notional face value before `triggerNoContestFor` latches the failure.
|
|
26
28
|
|
|
27
29
|
## Migration notes
|
|
28
30
|
|
package/CRYPTO_ECON.md
CHANGED
|
@@ -777,11 +777,11 @@ Defifa includes a comprehensive safety system — the **NO_CONTEST** mechanism
|
|
|
777
777
|
|
|
778
778
|
#### 9.1.1 Trigger 1: Minimum Participation Threshold
|
|
779
779
|
|
|
780
|
-
**Mechanism.** At game creation, the organizer sets `minParticipation` — a minimum
|
|
780
|
+
**Mechanism.** At game creation, the organizer sets `minParticipation` — a minimum cumulative NFT mint cost required for the game to proceed to scoring. The `currentGamePhaseOf()` function checks the hook's `totalMintCost()` against this threshold before returning SCORING. If the mint cost is below the threshold, it returns NO_CONTEST. `totalMintCost` tracks only actual paid mint value and is decremented on refunds/burns — it cannot be inflated by `addToBalanceOf` top-ups.
|
|
781
781
|
|
|
782
782
|
**What it solves.** Ghost games with negligible participation skip directly to refundability without requiring any governance action. A 32-team World Cup game with `minParticipation = 1 ETH` won't enter scoring if only 50 people mint (0.5 ETH pot).
|
|
783
783
|
|
|
784
|
-
**Attack surface.** An adversary who wants to force no-contest can
|
|
784
|
+
**Attack surface.** An adversary who wants to force no-contest can refund enough NFTs during the refund phase to push `totalMintCost` below the threshold. Direct balance top-ups (via `addToBalanceOf`) cannot inflate participation since the check uses `totalMintCost`, not treasury balance. Mitigation: set the threshold conservatively low relative to expected participation.
|
|
785
785
|
|
|
786
786
|
**Configuration.** Set to 0 to disable. The threshold is set at launch before any minting occurs, so calibration depends on organizer judgment.
|
|
787
787
|
|
|
@@ -820,7 +820,7 @@ The phase resolution follows strict priority:
|
|
|
820
820
|
|
|
821
821
|
2. **Explicit trigger is sticky.** Once `noContestTriggeredFor[gameId]` is set, the game stays in NO_CONTEST permanently (cannot transition to SCORING even if conditions change).
|
|
822
822
|
|
|
823
|
-
3. **Both thresholds are checked independently.** A game can enter NO_CONTEST from either `minParticipation` (
|
|
823
|
+
3. **Both thresholds are checked independently.** A game can enter NO_CONTEST from either `minParticipation` (cumulative mint cost too low) or `scorecardTimeout` (time elapsed) — whichever condition is met first.
|
|
824
824
|
|
|
825
825
|
#### 9.1.5 The Default Attestation Delegate
|
|
826
826
|
|
package/RISKS.md
CHANGED
|
@@ -22,6 +22,7 @@ This file focuses on the game-theoretic, governance, and settlement risks in Def
|
|
|
22
22
|
- **Deployer as project owner.** The deployer owns game projects and controls ruleset queuing and commitment fulfillment.
|
|
23
23
|
- **DefifaProjectOwner irrecoverability.** Once the project NFT is transferred there, it cannot be recovered.
|
|
24
24
|
- **External dependencies.** Core protocol and shared 721-store behavior remain upstream trust boundaries.
|
|
25
|
+
- **Terminal provenance is the launcher's responsibility.** The hook authenticates terminals via directory registration, not implementation identity. A game is only as trustworthy as the terminal its launcher chose. Users must verify a game's terminal before participating.
|
|
25
26
|
- **Default attestation delegate.** If set, it can accumulate meaningful governance power across new minters.
|
|
26
27
|
|
|
27
28
|
## 2. Economic Risks
|
|
@@ -86,6 +87,22 @@ Completion and ratification paths use one-way state to prevent replay or double-
|
|
|
86
87
|
|
|
87
88
|
This is conservative, but it prevents users from front-running reserve dilution out of governance power or fee-token distribution.
|
|
88
89
|
|
|
89
|
-
### 8.5
|
|
90
|
+
### 8.5 Launcher-selected terminals are trusted per game
|
|
90
91
|
|
|
91
|
-
|
|
92
|
+
`DefifaDeployer.launchGameWith(...)` is permissionless and allows the launcher to choose the terminal registered for the game. `DefifaHook` trusts any registered terminal as the source of pay and cash-out callbacks. This means a malicious launcher can register a callback-forging terminal that fabricates hook contexts without recording real payments. Users and integrators must verify a game's registered terminal before trusting or participating in it. The same applies to scorecard timing parameters and tier configuration — a game's safety depends on the launcher choosing sane inputs. Frontends and aggregators should cross-reference a game's terminal against the canonical `JBMultiTerminal` for the chain before displaying it as trustworthy.
|
|
93
|
+
|
|
94
|
+
### 8.6 One-tier games always resolve via no-contest
|
|
95
|
+
|
|
96
|
+
A single-tier game cannot complete normal governance because the governance attestation model gives zero weight to holders of a tier that receives 100% of the scorecard, making quorum unreachable. This is expected: the game falls through to `NO_CONTEST` once `scorecardTimeout` elapses, and players recover their mint price via the permissionless `triggerNoContestFor()` refund path that queues a refund ruleset. This only works when `scorecardTimeout > 0` — a one-tier game launched with `scorecardTimeout = 0` would disable the timeout path entirely and leave funds permanently locked. `DefifaDeployer.launchGameWith` now enforces this at the contract level: a one-tier game with `scorecardTimeout == 0` reverts with `DefifaDeployer_InvalidGameConfiguration`. Two-tier games are still rounding-fragile in the same direction and should also be launched with a nonzero `scorecardTimeout`, but this is not enforced because some two-tier configurations can still reach quorum.
|
|
97
|
+
|
|
98
|
+
### 8.7 Commitment splits are responsible for never reverting
|
|
99
|
+
|
|
100
|
+
`DefifaDeployer.fulfillCommitmentsOf` calls `terminal.sendPayoutsOf` to distribute the commitment portion of the pot. Core processes splits inside a try/catch — when an individual split reverts (rejecting split hook, recipient terminal that does not accept the game token, fee project with a missing currency feed) the failed amount is silently re-credited to the game's terminal balance and the outer call succeeds. The unpaid commitment funds then remain available to game players via the cash-out path.
|
|
101
|
+
|
|
102
|
+
This means **a single bad split cannot block the others from being paid**, and the unpaid funds are never lost — but it also means the recipient configured behind the failing split does not get paid through `fulfillCommitmentsOf` and must be settled separately if the game launcher cares. Game launchers are responsible for configuring commitment splits that will not revert under the game's terminal/currency setup. Recipients of commitment splits should treat split delivery as best-effort; the canonical post-game pot accounting still holds.
|
|
103
|
+
|
|
104
|
+
`fulfilledCommitmentsOf[gameId]` always equals the requested commitment amount whenever any portion was attempted, regardless of whether every split succeeded. `currentGamePotOf(gameId, true)` adds this back to the remaining balance so the displayed pot represents the original pre-commitment value, but the actual on-terminal balance may be higher than the displayed pot when splits silently failed — by exactly the failed split amounts, which remain redeemable by game players. The bound therefore stays one-sided: real balance ≥ reported pot.
|
|
105
|
+
|
|
106
|
+
### 8.8 Reserve minting is blocked during NO_CONTEST
|
|
107
|
+
|
|
108
|
+
`DefifaHook.mintReservesFor` reverts with `DefifaHook_ReservedTokenMintingBlockedInNoContest` once `currentGamePhaseOf` reports `NO_CONTEST`. Reserve mints inflate `totalMintCost` so reserved recipients can claim a proportional share of fee tokens. Without this block, a game that failed `minParticipation` could be revived back into a SCORING-eligible state via free notional face value (reserves bump `totalMintCost` over the threshold) before `triggerNoContestFor()` latches the failure into a refund ruleset. The block tightens the §8.4 trust assumption that pending reserves are folded into governance and fee accounting: that remains true while the game is live, but once a game has already failed participation the reserve channel is closed so the no-contest outcome is final.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ballkidz/defifa",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.34",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": "25.9.0"
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"remappings.txt"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@bananapus/721-hook-v6": "0.0.46",
|
|
35
|
-
"@bananapus/address-registry-v6": "0.0.25",
|
|
36
|
-
"@bananapus/core-v6": "0.0.
|
|
37
|
-
"@bananapus/permission-ids-v6": "0.0.22",
|
|
34
|
+
"@bananapus/721-hook-v6": "^0.0.46",
|
|
35
|
+
"@bananapus/address-registry-v6": "^0.0.25",
|
|
36
|
+
"@bananapus/core-v6": "^0.0.48",
|
|
37
|
+
"@bananapus/permission-ids-v6": "^0.0.22",
|
|
38
38
|
"@openzeppelin/contracts": "5.6.1",
|
|
39
39
|
"@prb/math": "4.1.1",
|
|
40
40
|
"scripty.sol": "2.1.1"
|
package/src/DefifaDeployer.sol
CHANGED
|
@@ -186,7 +186,7 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
|
|
|
186
186
|
|
|
187
187
|
/// @notice The safety mechanism parameters of a game.
|
|
188
188
|
/// @param gameId The ID of the game to get the safety params of.
|
|
189
|
-
/// @return minParticipation The minimum
|
|
189
|
+
/// @return minParticipation The minimum cumulative NFT mint cost for the game to proceed to scoring.
|
|
190
190
|
/// @return scorecardTimeout The maximum time after scoring begins for a scorecard to be ratified.
|
|
191
191
|
function safetyParamsOf(uint256 gameId)
|
|
192
192
|
external
|
|
@@ -249,12 +249,13 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
|
|
|
249
249
|
// Get the game's ops data for the safety mechanism checks. Cache to avoid repeated SLOAD.
|
|
250
250
|
DefifaOpsData memory ops = _opsOf[gameId];
|
|
251
251
|
|
|
252
|
-
// Check minimum participation threshold using
|
|
253
|
-
//
|
|
254
|
-
//
|
|
252
|
+
// Check minimum participation threshold using cumulative NFT mint cost from the hook.
|
|
253
|
+
// Terminal balance is NOT used because `addToBalanceOf` can inflate it without minting NFTs.
|
|
254
|
+
// `totalMintCost` tracks only actual paid mint value and is decremented on refunds/burns.
|
|
255
255
|
if (ops.minParticipation > 0) {
|
|
256
|
-
|
|
257
|
-
|
|
256
|
+
if (IDefifaHook(metadata.dataHook).totalMintCost() < ops.minParticipation) {
|
|
257
|
+
return DefifaGamePhase.NO_CONTEST;
|
|
258
|
+
}
|
|
258
259
|
}
|
|
259
260
|
|
|
260
261
|
// Check scorecard ratification timeout: if enough time has passed without a ratified scorecard, the game is
|
|
@@ -424,6 +425,20 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
|
|
|
424
425
|
});
|
|
425
426
|
}
|
|
426
427
|
|
|
428
|
+
// One-tier games cannot reach quorum (the BWA multiplier reduces the sole beneficiary tier's power to zero),
|
|
429
|
+
// so they must have a nonzero scorecardTimeout to resolve via NO_CONTEST. Without a timeout they would lock
|
|
430
|
+
// forever with no exit. Two-tier games are also rounding-fragile (see RISKS.md §8.6) and should set a
|
|
431
|
+
// scorecardTimeout, but this is not enforced at the contract level because some configurations can still
|
|
432
|
+
// reach quorum with luck-of-the-draw holder distributions.
|
|
433
|
+
if (launchProjectData.tiers.length == 1 && launchProjectData.scorecardTimeout == 0) {
|
|
434
|
+
revert DefifaDeployer_InvalidGameConfiguration({
|
|
435
|
+
start: launchProjectData.start,
|
|
436
|
+
mintPeriodDuration: launchProjectData.mintPeriodDuration,
|
|
437
|
+
refundPeriodDuration: launchProjectData.refundPeriodDuration,
|
|
438
|
+
tierCount: launchProjectData.tiers.length
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
427
442
|
// Reject ERC-20 games with a zero currency. A zero baseCurrency would cause payout limit lookups
|
|
428
443
|
// in fulfillCommitmentsOf to silently fail, skipping all commitment payouts.
|
|
429
444
|
if (launchProjectData.token.token != JBConstants.NATIVE_TOKEN && launchProjectData.token.currency == 0) {
|
package/src/DefifaHook.sol
CHANGED
|
@@ -57,6 +57,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
57
57
|
error DefifaHook_Overspending(uint256 leftoverAmount);
|
|
58
58
|
error DefifaHook_CashoutWeightsAlreadySet(uint256 projectId);
|
|
59
59
|
error DefifaHook_ReservedTokenMintingPaused(uint256 projectId, uint256 tierId);
|
|
60
|
+
error DefifaHook_ReservedTokenMintingBlockedInNoContest(uint256 projectId, uint256 tierId);
|
|
60
61
|
error DefifaHook_TransfersPaused(uint256 projectId, uint256 tokenId, address from, address to);
|
|
61
62
|
error DefifaHook_Unauthorized(uint256 tokenId, address owner, address caller);
|
|
62
63
|
|
|
@@ -97,10 +98,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
97
98
|
/// @dev _tierId The ID of the tier to get a name for.
|
|
98
99
|
mapping(uint256 => string) internal _tierNameOf;
|
|
99
100
|
|
|
100
|
-
/// @notice The cumulative mint price of all tokens (paid and reserved). Used as the denominator for fee token
|
|
101
|
-
/// ($DEFIFA/$NANA) distribution.
|
|
102
|
-
uint256 internal _totalMintCost;
|
|
103
|
-
|
|
104
101
|
//*********************************************************************//
|
|
105
102
|
// ---------------- public immutable stored properties --------------- //
|
|
106
103
|
//*********************************************************************//
|
|
@@ -115,6 +112,10 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
115
112
|
// --------------------- public stored properties -------------------- //
|
|
116
113
|
//*********************************************************************//
|
|
117
114
|
|
|
115
|
+
/// @notice The cumulative mint price of all paid and reserved NFTs. Decremented on burns/refunds. Used as the
|
|
116
|
+
/// participation metric — immune to `addToBalanceOf` inflation because only actual mints increment it.
|
|
117
|
+
uint256 public override totalMintCost;
|
|
118
|
+
|
|
118
119
|
/// @notice The amount that has been redeemed from this game, refunds are not counted.
|
|
119
120
|
uint256 public override amountRedeemed;
|
|
120
121
|
|
|
@@ -411,7 +412,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
411
412
|
tokenIds: tokenIds,
|
|
412
413
|
hookStore: store,
|
|
413
414
|
hook: address(this),
|
|
414
|
-
totalMintCost:
|
|
415
|
+
totalMintCost: totalMintCost + _pendingReserveMintCost(),
|
|
415
416
|
defifaBalance: DEFIFA_TOKEN.balanceOf(address(this)),
|
|
416
417
|
baseProtocolBalance: BASE_PROTOCOL_TOKEN.balanceOf(address(this))
|
|
417
418
|
});
|
|
@@ -568,6 +569,13 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
568
569
|
revert DefifaHook_ReservedTokenMintingPaused({projectId: PROJECT_ID, tierId: tierId});
|
|
569
570
|
}
|
|
570
571
|
|
|
572
|
+
// Block reserve minting while the game is in NO_CONTEST. Reserve mints inflate `totalMintCost` (so reserved
|
|
573
|
+
// recipients can claim fee tokens), which would otherwise let a game that failed `minParticipation` revive
|
|
574
|
+
// back to SCORING via free notional face value before `triggerNoContestFor` latches the failure.
|
|
575
|
+
if (_currentGamePhaseOf(PROJECT_ID) == DefifaGamePhase.NO_CONTEST) {
|
|
576
|
+
revert DefifaHook_ReservedTokenMintingBlockedInNoContest({projectId: PROJECT_ID, tierId: tierId});
|
|
577
|
+
}
|
|
578
|
+
|
|
571
579
|
// Cache the store reference in a local variable to avoid repeated SLOAD.
|
|
572
580
|
IJB721TiersHookStore hookStore = store;
|
|
573
581
|
|
|
@@ -597,11 +605,11 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
597
605
|
// Fetch the tier details (needed for votingUnits below).
|
|
598
606
|
JB721Tier memory tier = hookStore.tierOf({hook: address(this), id: tierId, includeResolvedUri: false});
|
|
599
607
|
|
|
600
|
-
// Increment
|
|
608
|
+
// Increment totalMintCost so reserved recipients can claim their share of fee tokens ($DEFIFA/$NANA).
|
|
601
609
|
// Note: reserved mints dilute existing fee token claimants because they increase the total mint cost
|
|
602
610
|
// denominator without contributing new funds to the fee token balances. This is the intended design —
|
|
603
611
|
// reserved recipients receive a proportional claim on fee tokens as if they had paid to mint.
|
|
604
|
-
|
|
612
|
+
totalMintCost += tier.price * count;
|
|
605
613
|
|
|
606
614
|
for (uint256 i; i < count;) {
|
|
607
615
|
// Set the token ID.
|
|
@@ -724,7 +732,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
724
732
|
beneficiaryReceivedTokens = _claimTokensFor({
|
|
725
733
|
beneficiary: context.beneficiary,
|
|
726
734
|
shareToBeneficiary: cumulativeMintPrice,
|
|
727
|
-
outOfTotal:
|
|
735
|
+
outOfTotal: totalMintCost + _pendingReserveMintCost()
|
|
728
736
|
});
|
|
729
737
|
}
|
|
730
738
|
|
|
@@ -739,7 +747,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
739
747
|
}
|
|
740
748
|
|
|
741
749
|
// Decrement the paid mint cost by the cumulative mint price of the tokens being burned.
|
|
742
|
-
|
|
750
|
+
totalMintCost -= cumulativeMintPrice;
|
|
743
751
|
}
|
|
744
752
|
|
|
745
753
|
/// @notice Mint reserved tokens within the tier for the provided value.
|
|
@@ -917,7 +925,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
917
925
|
uint256 tokenId;
|
|
918
926
|
|
|
919
927
|
// Increment the paid mint cost.
|
|
920
|
-
|
|
928
|
+
totalMintCost += amount;
|
|
921
929
|
|
|
922
930
|
// Loop through each token ID and mint.
|
|
923
931
|
for (uint256 i; i < mintsLength;) {
|
|
@@ -117,6 +117,11 @@ interface IDefifaHook is IJB721Hook {
|
|
|
117
117
|
/// @return The Defifa ERC-20 token.
|
|
118
118
|
function DEFIFA_TOKEN() external view returns (IERC20);
|
|
119
119
|
|
|
120
|
+
/// @notice The cumulative mint price of all paid and reserved NFTs. Decremented on burns/refunds. Used as the
|
|
121
|
+
/// participation metric — immune to `addToBalanceOf` inflation because only actual mints increment it.
|
|
122
|
+
/// @return The total mint cost in the game's payment token.
|
|
123
|
+
function totalMintCost() external view returns (uint256);
|
|
124
|
+
|
|
120
125
|
/// @notice The first owner of a given token ID.
|
|
121
126
|
/// @param tokenId The token ID.
|
|
122
127
|
/// @return The address of the first owner.
|
|
@@ -30,8 +30,9 @@ import {DefifaTierParams} from "./DefifaTierParams.sol";
|
|
|
30
30
|
/// @custom:member defaultAttestationDelegate The address that'll be set as the attestation delegate by default.
|
|
31
31
|
/// @custom:member defaultTokenUriResolver The contract used to resolve token URIs if not provided by a tier
|
|
32
32
|
/// specifically. @custom:member terminal The payment terminal where the project will accept funds through.
|
|
33
|
-
/// @custom:member minParticipation The minimum
|
|
34
|
-
///
|
|
33
|
+
/// @custom:member minParticipation The minimum cumulative NFT mint cost required for the game to proceed to scoring.
|
|
34
|
+
/// Compared against the hook's `totalMintCost` (immune to `addToBalanceOf` inflation). If below this when scoring
|
|
35
|
+
/// would begin, the game enters NO_CONTEST. Set to 0 to disable. @custom:member
|
|
35
36
|
/// scorecardTimeout The maximum time (in seconds) after the scoring phase begins for a scorecard to be ratified. If
|
|
36
37
|
/// exceeded, the game enters NO_CONTEST. Set to 0 to disable.
|
|
37
38
|
struct DefifaLaunchProjectData {
|
|
@@ -6,9 +6,10 @@ pragma solidity ^0.8.0;
|
|
|
6
6
|
/// @custom:member start The time at which the game should start, measured in seconds.
|
|
7
7
|
/// @custom:member mintPeriodDuration The duration of the game's mint phase, measured in seconds.
|
|
8
8
|
/// @custom:member refundPeriodDuration The time between the mint phase and the start time when mint's are no longer
|
|
9
|
-
/// open but refunds are still allowed, measured in seconds. @custom:member minParticipation The minimum
|
|
10
|
-
///
|
|
11
|
-
/// enters NO_CONTEST. Set to 0 to
|
|
9
|
+
/// open but refunds are still allowed, measured in seconds. @custom:member minParticipation The minimum cumulative NFT
|
|
10
|
+
/// mint cost required for the game to proceed to scoring. Compared against the hook's `totalMintCost` (immune to
|
|
11
|
+
/// `addToBalanceOf` inflation). If below this when scoring would begin, the game enters NO_CONTEST. Set to 0 to
|
|
12
|
+
/// disable.
|
|
12
13
|
/// @custom:member scorecardTimeout The maximum time (in seconds) after the scoring phase begins for a scorecard to be
|
|
13
14
|
/// ratified. If exceeded, the game enters NO_CONTEST. Set to 0 to disable.
|
|
14
15
|
struct DefifaOpsData {
|