@ballkidz/defifa 0.0.12 → 0.0.14
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/ADMINISTRATION.md +3 -3
- package/ARCHITECTURE.md +3 -2
- package/AUDIT_INSTRUCTIONS.md +5 -5
- package/CHANGE_LOG.md +62 -5
- package/CRYPTO_ECON.md +506 -271
- package/CRYPTO_ECON.pdf +0 -0
- package/CRYPTO_ECON.tex +438 -241
- package/RISKS.md +13 -1
- package/SKILLS.md +5 -3
- package/USER_JOURNEYS.md +4 -3
- package/package.json +6 -6
- package/src/DefifaDeployer.sol +128 -130
- package/src/DefifaGovernor.sol +304 -83
- package/src/DefifaHook.sol +184 -171
- package/src/enums/DefifaScorecardState.sol +1 -0
- package/src/interfaces/IDefifaGovernor.sol +42 -2
- package/src/libraries/DefifaHookLib.sol +69 -62
- package/src/structs/DefifaAttestations.sol +3 -3
- package/src/structs/DefifaLaunchProjectData.sol +1 -0
- package/src/structs/DefifaScorecard.sol +2 -0
- package/test/BWAFunctionComparison.t.sol +1320 -0
- package/test/DefifaAdversarialQuorum.t.sol +52 -37
- package/test/DefifaAuditLowGuards.t.sol +9 -5
- package/test/DefifaFeeAccounting.t.sol +2 -1
- package/test/DefifaGovernanceHardening.t.sol +1315 -0
- package/test/DefifaGovernor.t.sol +8 -4
- package/test/DefifaHookRegressions.t.sol +2 -1
- package/test/DefifaMintCostInvariant.t.sol +2 -1
- package/test/DefifaNoContest.t.sol +3 -2
- package/test/DefifaSecurity.t.sol +55 -47
- package/test/DefifaUSDC.t.sol +3 -2
- package/test/Fork.t.sol +37 -32
- package/test/TestAuditGaps.sol +6 -4
- package/test/TestQALastMile.t.sol +6 -3
- package/test/audit/{CodexAttestationDoubleCount.t.sol → AttestationDoubleCount.t.sol} +3 -2
- package/test/audit/FixPendingReserveDilution.t.sol +366 -0
- package/test/audit/PendingReserveDilution.t.sol +298 -0
- package/test/audit/PendingReserveQuorumGrief.t.sol +355 -0
- package/test/audit/PendingReserveSnapshotBypass.t.sol +279 -0
- package/test/regression/AttestationDelegateBeneficiary.t.sol +2 -1
- package/test/regression/FulfillmentBlocksRatification.t.sol +2 -1
- package/test/regression/GracePeriodBypass.t.sol +2 -1
- package/test/SVG.t.sol +0 -164
- package/test/deployScript.t.sol +0 -144
package/ADMINISTRATION.md
CHANGED
|
@@ -31,8 +31,8 @@ Admin privileges and their scope in defifa-collection-deployer-v6.
|
|
|
31
31
|
| Function | Required Role | Permission Check | What It Does |
|
|
32
32
|
|----------|--------------|-----------------|-------------|
|
|
33
33
|
| `initializeGame()` | DefifaDeployer (owner) | `onlyOwner` | Sets attestation start time and grace period for a game. Enforces minimum 1-day grace period. Called automatically during `launchGameWith()`. |
|
|
34
|
-
| `submitScorecardFor()` | Anyone | Must be in SCORING phase; no ratified scorecard yet; no duplicate scorecard hash; weighted tiers must have nonzero supply | Submits a scorecard for attestation. Sets `attestationsBegin` and `gracePeriodEnds` timestamps. |
|
|
35
|
-
| `attestToScorecardFrom()` | Any NFT holder | Must be in SCORING phase; scorecard must be ACTIVE or SUCCEEDED; caller cannot have already attested | Records attestation weight based on tier holdings at the
|
|
34
|
+
| `submitScorecardFor()` | Anyone | Must be in SCORING phase; no ratified scorecard yet; no duplicate scorecard hash; weighted tiers must have nonzero supply | Submits a scorecard for attestation. Sets `attestationsBegin` and `gracePeriodEnds` timestamps. Snapshots pending reserves per tier for BWA computation. |
|
|
35
|
+
| `attestToScorecardFrom()` | Any NFT holder | Must be in SCORING phase; scorecard must be ACTIVE or SUCCEEDED; caller cannot have already attested | Records attestation weight based on tier holdings at the `attestationsBegin - 1` checkpoint timestamp. Uses pending reserve snapshot from submission time. |
|
|
36
36
|
| `ratifyScorecardFrom()` | Anyone | Scorecard must be in SUCCEEDED state (quorum met + grace period elapsed); no scorecard already ratified | Executes the scorecard via low-level call to `setTierCashOutWeightsTo` on the hook, then calls `fulfillCommitmentsOf`. |
|
|
37
37
|
|
|
38
38
|
### DefifaHook
|
|
@@ -79,7 +79,7 @@ COUNTDOWN --> MINT --> REFUND (optional) --> SCORING --> COMPLETE or NO_CONTEST
|
|
|
79
79
|
|
|
80
80
|
### Attestation Quorum Details
|
|
81
81
|
|
|
82
|
-
The quorum threshold is 50% of the total attestation power across all tiers with nonzero mint supply. Attestation power per tier is proportional to the tier's minted supply at the `attestationsBegin`
|
|
82
|
+
The quorum threshold is 50% of the total attestation power across all tiers with nonzero mint supply. Attestation power per tier is proportional to the tier's minted supply at the `attestationsBegin - 1` checkpoint timestamp, with pending reserves snapshotted at scorecard submission time to prevent reserve minting from inflating attestation power.
|
|
83
83
|
|
|
84
84
|
**Edge cases:**
|
|
85
85
|
- **Tiers with zero mints:** Tiers with `currentSupplyOfTier(tierId) == 0` are excluded from the quorum calculation. They have no attestation power and cannot influence scoring.
|
package/ARCHITECTURE.md
CHANGED
|
@@ -53,10 +53,11 @@ COMPLETE Phase:
|
|
|
53
53
|
Scorer → DefifaGovernor.submitScorecard(tierWeights[])
|
|
54
54
|
→ Validate: correct phase, valid tier order, weights sum correctly
|
|
55
55
|
→ Create proposal hash
|
|
56
|
+
→ Snapshot pending reserves per tier for BWA computation
|
|
56
57
|
|
|
57
58
|
Attestor → DefifaGovernor.attestToScorecard(proposalId)
|
|
58
|
-
→ Must hold NFT tier tokens
|
|
59
|
-
→ Attestation weight = voting power from held tiers
|
|
59
|
+
→ Must hold NFT tier tokens at attestationsBegin - 1 checkpoint
|
|
60
|
+
→ Attestation weight = voting power from held tiers (diluted by snapshotted pending reserves)
|
|
60
61
|
→ When quorum reached → scorecard ratified
|
|
61
62
|
→ DefifaHook.setScorecard() called
|
|
62
63
|
```
|
package/AUDIT_INSTRUCTIONS.md
CHANGED
|
@@ -150,7 +150,7 @@ NO_CONTEST (safety mechanism triggered)
|
|
|
150
150
|
**Attest (DefifaGovernor.attestToScorecardFrom):**
|
|
151
151
|
1. Require SCORING phase, scorecard ACTIVE or SUCCEEDED.
|
|
152
152
|
2. Prevent double attestation per account per scorecard.
|
|
153
|
-
3. Compute weight via `
|
|
153
|
+
3. Compute weight via `getBWAAttestationWeight()` at `attestationsBegin - 1` timestamp, using pending reserve snapshot from submission time.
|
|
154
154
|
4. Increment `_scorecardAttestationsOf[gameId][scorecardId].count += weight`.
|
|
155
155
|
|
|
156
156
|
**Ratify (DefifaGovernor.ratifyScorecardFrom):**
|
|
@@ -188,7 +188,7 @@ tierPower = MAX_ATTESTATION_POWER_TIER * (account's attestation units / tier's t
|
|
|
188
188
|
|
|
189
189
|
Where:
|
|
190
190
|
- `MAX_ATTESTATION_POWER_TIER = 1,000,000,000` (1e9)
|
|
191
|
-
- Account's units come from `getPastTierAttestationUnitsOf()` (checkpoint at `attestationsBegin` timestamp)
|
|
191
|
+
- Account's units come from `getPastTierAttestationUnitsOf()` (checkpoint at `attestationsBegin - 1` timestamp)
|
|
192
192
|
- Total tier units from `getPastTierTotalAttestationUnitsOf()`
|
|
193
193
|
|
|
194
194
|
Total attestation power = sum of per-tier powers across all tiers.
|
|
@@ -296,9 +296,9 @@ Start with the money: follow ETH from payment to cash-out.
|
|
|
296
296
|
|
|
297
297
|
6. **Quorum manipulation via live supply**: `quorum()` reads `currentSupplyOfTier()` at call time (not snapshotted). Verify that burning tokens during SCORING is prevented by `DefifaHook_NothingToClaim` (cash-out weights not set yet). Check if any other burn path exists that could reduce quorum after attestations have begun.
|
|
298
298
|
|
|
299
|
-
7. **Attestation snapshotting**: Attestation weight is computed at the `attestationsBegin` timestamp via `getPastTierAttestationUnitsOf()`. Verify that the `Checkpoints.Trace208.upperLookup()` correctly captures the state at that
|
|
299
|
+
7. **Attestation snapshotting**: Attestation weight is computed at the `attestationsBegin - 1` timestamp via `getPastTierAttestationUnitsOf()`. Pending reserves are snapshotted at submission time (`_pendingReservesSnapshotOf`). Verify that the `Checkpoints.Trace208.upperLookup()` correctly captures the state at that timestamp, that minting or transferring NFTs after `attestationsBegin - 1` does not retroactively affect attestation power, and that minting reserves after submission does not change the snapshotted pending reserve counts.
|
|
300
300
|
|
|
301
|
-
8. **Double attestation prevention**: `_attestations.
|
|
301
|
+
8. **Double attestation prevention**: `_attestations.attestedWeightOf[msg.sender]` prevents double voting. Verify that an attacker cannot attest, transfer NFTs to another address, and have that address attest with the same attestation power (the checkpoint at `attestationsBegin - 1` should prevent this because same-block transfers are invisible at `attestationsBegin - 1`).
|
|
302
302
|
|
|
303
303
|
9. **Grace period anchoring**: `gracePeriodEnds = attestationsBegin + attestationGracePeriod`. Verify that early scorecard submission (before `attestationStartTime`) correctly delays the grace period start, preventing instant ratification.
|
|
304
304
|
|
|
@@ -344,7 +344,7 @@ These properties should hold for all games in all states. The test suite validat
|
|
|
344
344
|
|
|
345
345
|
### Governance
|
|
346
346
|
- Each account can attest to a given scorecard at most once
|
|
347
|
-
- Attestation power is
|
|
347
|
+
- Attestation power is checkpointed at `attestationsBegin - 1` (not live); pending reserves snapshotted at submission time
|
|
348
348
|
- Quorum threshold: 50% of minted tiers' total max attestation power (live at call time)
|
|
349
349
|
- Only one scorecard can be ratified per game
|
|
350
350
|
- Minimum grace period: 1 day (enforced in `initializeGame`)
|
package/CHANGE_LOG.md
CHANGED
|
@@ -8,7 +8,20 @@ This document describes the changes between `defifa-collection-deployer` (v5) an
|
|
|
8
8
|
- **Dependency modernization**: Core, permission IDs, address registry, and ownable dependencies all updated to v6. New ecosystem dependencies on `croptop-core-v6` and `revnet-core-v6`.
|
|
9
9
|
- **Error naming standardized**: Error names changed from bare names (e.g., `InvalidCashoutWeights`) to contract-prefixed names (e.g., `DefifaHook_InvalidCashoutWeights`).
|
|
10
10
|
- **Cash out hook spec gains `noop` field**: `beforeCashOutRecordedWith` now returns `noop=false` in all specifications, ensuring the terminal always calls the hook callback.
|
|
11
|
-
- **
|
|
11
|
+
- **Compiler/tooling updated**: The v6 repo now builds and tests on Solidity `0.8.28`, matching the rest of the V6 ecosystem.
|
|
12
|
+
- **Game lifecycle preserved**: The core game phases (COUNTDOWN → MINT → REFUND → SCORING → COMPLETE) and governance model (50% quorum, scorecard ratification) remain the same at the product level even though the underlying hook/controller integrations changed.
|
|
13
|
+
|
|
14
|
+
## ABI Status
|
|
15
|
+
|
|
16
|
+
This repo does have meaningful ABI migration surface. The main ABI-facing contracts for integrators are:
|
|
17
|
+
- `IDefifaDeployer`
|
|
18
|
+
- `IDefifaHook`
|
|
19
|
+
- `IDefifaGovernor`
|
|
20
|
+
|
|
21
|
+
The largest ABI risks are:
|
|
22
|
+
- inherited 721-hook/core-v6 struct and return-shape changes flowing through Defifa interfaces;
|
|
23
|
+
- event families now living on v6 interfaces/contracts with prefixed errors and updated dependent types;
|
|
24
|
+
- hook return values that now include v6 `noop`-aware hook-spec structures.
|
|
12
25
|
|
|
13
26
|
---
|
|
14
27
|
|
|
@@ -42,7 +55,8 @@ The v6 `JBCashOutHookSpecification` struct has a `noop` boolean field. `DefifaHo
|
|
|
42
55
|
|
|
43
56
|
### 1.4 Solidity Version
|
|
44
57
|
|
|
45
|
-
|
|
58
|
+
- **v5:** `pragma solidity 0.8.23`
|
|
59
|
+
- **v6:** `pragma solidity 0.8.28`
|
|
46
60
|
|
|
47
61
|
### 1.5 721 Hook API Changes
|
|
48
62
|
|
|
@@ -52,6 +66,14 @@ Inherited from `nana-721-hook-v6`:
|
|
|
52
66
|
- `JB721TierConfig` gained `splitPercent` and `splits` fields
|
|
53
67
|
- `JB721TiersHookFlags` gained `issueTokensForSplits` field
|
|
54
68
|
|
|
69
|
+
### 1.6 Function-Level Integration Changes
|
|
70
|
+
|
|
71
|
+
These are the main V6 surface changes integrators should care about:
|
|
72
|
+
- `beforeCashOutRecordedWith(...)` now returns a `JBCashOutHookSpecification` that includes the new `noop` field from core-v6.
|
|
73
|
+
- Any integration constructing or decoding `JB721TierConfig` must account for the added `splitPercent` and `splits` fields.
|
|
74
|
+
- Any integration reading pricing context from the inherited 721 hook must expect 2 return values, not 3.
|
|
75
|
+
- Defifa deployer integrations should re-check `launchGameWith(...)`, `fulfillCommitmentsOf(...)`, `triggerNoContestFor(...)`, `nextPhaseNeedsQueueing(...)`, `safetyParamsOf(...)`, and `timesFor(...)` against the v6 ABIs and dependent core-v6 structs.
|
|
76
|
+
|
|
55
77
|
---
|
|
56
78
|
|
|
57
79
|
## 2. Game Lifecycle (Unchanged)
|
|
@@ -89,9 +111,22 @@ DefifaHook instances are deployed as minimal proxy clones via `Clones.cloneDeter
|
|
|
89
111
|
- One-time `initialize()` call per clone
|
|
90
112
|
- Owned by DefifaGovernor for scorecard weight setting
|
|
91
113
|
|
|
114
|
+
## 4. Events and Errors
|
|
115
|
+
|
|
116
|
+
The most important integration-facing patterns are:
|
|
117
|
+
- Errors are now consistently contract-prefixed (`DefifaHook_*`, `DefifaDeployer_*`, `DefifaGovernor_*`, `DefifaProjectOwner_*`) instead of using the older unprefixed style.
|
|
118
|
+
- Defifa-specific events remain centered around game launch, phase transitions, fulfillment, scoring, minting, claims, and delegation, but they now live against the V6 hook/controller stack and should be indexed using the V6 ABIs.
|
|
119
|
+
- `CommitmentPayoutFailed`, `LaunchGame`, `QueuedRefundPhase`, `QueuedScoringPhase`, `QueuedNoContest`, `GameInitialized`, `ScorecardSubmitted`, `ScorecardAttested`, `ScorecardRatified`, `Mint`, `MintReservedToken`, `ClaimedTokens`, and `TierCashOutWeightsSet` are the key integration events to watch across the deployer, governor, and hook.
|
|
120
|
+
- Other hook-level events worth indexing for governance clients are `TierDelegateAttestationsChanged` and `DelegateChanged`.
|
|
121
|
+
|
|
122
|
+
Key runtime errors now exposed by the v6 contracts include:
|
|
123
|
+
- `DefifaDeployer_*` errors such as `DefifaDeployer_InvalidGameConfiguration`, `DefifaDeployer_TerminalNotFound`, `DefifaDeployer_SplitsDontAddUp`, `DefifaDeployer_CantFulfillYet`, and `DefifaDeployer_NoContestAlreadyTriggered`.
|
|
124
|
+
- `DefifaHook_*` errors such as `DefifaHook_InvalidCashoutWeights`, `DefifaHook_InvalidTierId`, `DefifaHook_ReservedTokenMintingPaused`, `DefifaHook_TransfersPaused`, and `DefifaHook_Unauthorized(...)`.
|
|
125
|
+
- `DefifaGovernor_*` errors such as `DefifaGovernor_AlreadyAttested`, `DefifaGovernor_AlreadyRatified`, `DefifaGovernor_DuplicateScorecard`, `DefifaGovernor_NotAllowed`, and `DefifaGovernor_UnknownProposal`.
|
|
126
|
+
|
|
92
127
|
---
|
|
93
128
|
|
|
94
|
-
##
|
|
129
|
+
## 5. Migration Table
|
|
95
130
|
|
|
96
131
|
| Aspect | v5 | v6 |
|
|
97
132
|
|--------|----|----|
|
|
@@ -100,8 +135,30 @@ DefifaHook instances are deployed as minimal proxy clones via `Clones.cloneDeter
|
|
|
100
135
|
| Permission IDs | Not a direct dependency | `@bananapus/permission-ids-v6` |
|
|
101
136
|
| Error naming | Bare names | Contract-prefixed names |
|
|
102
137
|
| `JBCashOutHookSpecification` | No `noop` field | `noop=false` on all specs |
|
|
103
|
-
| Solidity version | `0.8.23` | `0.8.
|
|
138
|
+
| Solidity version | `0.8.23` | `0.8.28` |
|
|
104
139
|
| Game lifecycle | COUNTDOWN->MINT->REFUND->SCORING->COMPLETE | Identical |
|
|
105
140
|
| Governance model | 50% quorum, tier-delegated | Identical |
|
|
106
141
|
|
|
107
|
-
> **Cross-repo impact**: Uses `nana-721-hook-v6` for all tier management and cashout weight distribution. The `nana-permission-ids-v6` ID shifts affect any hardcoded permission checks.
|
|
142
|
+
> **Cross-repo impact**: Uses `nana-721-hook-v6` for all tier management and cashout weight distribution. The `nana-permission-ids-v6` ID shifts affect any hardcoded permission checks. `deploy-all-v6` now includes Defifa as Phase 10, so canonical deployments can source Defifa addresses from the top-level rollout.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 6. Post-Audit Fixes (Codex R2)
|
|
147
|
+
|
|
148
|
+
### 6.1 H-1: Prevent same-block double attestation via checkpoint snapshot
|
|
149
|
+
|
|
150
|
+
**File:** `DefifaGovernor.sol` -- `attestToScorecardFrom()`
|
|
151
|
+
|
|
152
|
+
Previously, attestation weight was snapshot at `_scorecard.attestationsBegin`, which could equal `block.timestamp` during the same block as a transfer. This allowed a holder to attest, transfer the NFT, and have the recipient also attest in the same block -- both counting because `upperLookup(block.timestamp)` includes same-block checkpoints.
|
|
153
|
+
|
|
154
|
+
**Fix:** Changed the checkpoint timestamp to `attestationsBegin - 1`, ensuring only state from before the attestation window opens is visible. Same-block transfer recipients now receive zero attestation weight. Note: NFTs minted in the same block as `attestationsBegin` have zero weight for attestation -- a negligible trade-off since attestation typically happens well after minting.
|
|
155
|
+
|
|
156
|
+
### 6.2 H-2: Snapshot pending reserves at scorecard submission and include in denominators
|
|
157
|
+
|
|
158
|
+
**Files:** `DefifaGovernor.sol` -- `submitScorecardFor()`, `getBWAAttestationWeight()`; `DefifaHook.sol` -- `afterCashOutRecordedWith()`
|
|
159
|
+
|
|
160
|
+
Two related fixes to prevent gaming via pending reserve manipulation:
|
|
161
|
+
|
|
162
|
+
**Governance (attestation weight):** Previously, `getBWAAttestationWeight()` read pending reserve counts live from the store. Minting reserves between scorecard submission and attestation could inflate a holder's BWA power by removing the pending-reserve dilution. **Fix:** `submitScorecardFor()` now snapshots `numberOfPendingReservesFor()` for every tier into `_pendingReservesSnapshotOf[gameId][scorecardId][tierId]`. `getBWAAttestationWeight()` reads from this snapshot instead of live state, locking the dilution in place regardless of subsequent reserve minting.
|
|
163
|
+
|
|
164
|
+
**Cash-out (fee token distribution):** Previously, the fee token claim denominator (`_totalMintCost`) only counted minted tokens. Pending (unminted) reserve NFTs were excluded, allowing paid holders to cash out before reserves were minted and claim a disproportionate share of fee tokens ($DEFIFA/$NANA). **Fix:** `afterCashOutRecordedWith()` now passes `_totalMintCost + _pendingReserveMintCost()` as the denominator when distributing fee tokens. The new `_pendingReserveMintCost()` function iterates all tiers and sums `pendingReserves * tier.price`, ensuring pending reserves are accounted for in fee token distribution.
|