@ballkidz/defifa 0.0.12 → 0.0.13
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/CHANGE_LOG.md +60 -5
- package/CRYPTO_ECON.md +505 -270
- package/CRYPTO_ECON.pdf +0 -0
- package/CRYPTO_ECON.tex +438 -241
- package/RISKS.md +9 -1
- package/SKILLS.md +3 -2
- package/package.json +6 -6
- package/src/DefifaDeployer.sol +128 -130
- package/src/DefifaGovernor.sol +278 -83
- package/src/DefifaHook.sol +158 -171
- package/src/enums/DefifaScorecardState.sol +1 -0
- package/src/interfaces/IDefifaGovernor.sol +41 -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 +1311 -0
- package/test/DefifaGovernor.t.sol +4 -2
- 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 +54 -41
- package/test/DefifaUSDC.t.sol +3 -2
- package/test/Fork.t.sol +11 -12
- package/test/TestAuditGaps.sol +6 -4
- package/test/TestQALastMile.t.sol +4 -2
- 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/regression/AttestationDelegateBeneficiary.t.sol +2 -1
- package/test/regression/FulfillmentBlocksRatification.t.sol +2 -1
- package/test/regression/GracePeriodBypass.t.sol +2 -1
package/RISKS.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
## 2. Economic Risks
|
|
13
13
|
|
|
14
14
|
- **Scorecard manipulation via 50% quorum.** A single entity that acquires 50%+ of attestation power across tiers can unilaterally ratify any scorecard, directing the entire pot to chosen tiers. Per-tier cap at `MAX_ATTESTATION_POWER_TIER` limits single-tier dominance. 1-day minimum grace period gives counter-attestors time to respond.
|
|
15
|
-
- **Dynamic quorum from live supply.**
|
|
15
|
+
- **Dynamic quorum from live supply (mitigated).** `quorum()` counts tiers with circulating supply (`currentSupplyOfTier > 0`) OR pending reserves (`numberOfPendingReservesFor > 0`). No snapshot is needed because during SCORING, supply is frozen (no new paid mints, no burns) and reserve minting doesn't change which tiers are counted — tiers with pending reserves are already included. Pending reserves also dilute attestation power — `getAttestationWeight` includes them in the denominator so every token holder's voting power already accounts for reserves that will eventually be minted. When the reserve beneficiary mints later, power redistributes smoothly (no shift). Consistent with the cash-out path which also dilutes by pending reserves.
|
|
16
16
|
- **Cash-out weight integer division truncation.** `_weight / _totalTokensForCashoutInTier` rounds down, permanently locking dust in the contract. Maximum loss: 1 wei per tier per game (128 wei max with 128 tiers).
|
|
17
17
|
- **Fee token dilution from reserved mints.** Reserved mints increment `_totalMintCost` by `tier.price * count` even though no ETH was paid. This dilutes paid minters' share of fee tokens (`$DEFIFA` / `$NANA`). Example: if 1000 NFTs are minted by payers (paying 1 ETH each = 1000 ETH total), and 100 reserved NFTs are minted (adding 100 ETH to `_totalMintCost` with no ETH deposited), fee token claims are diluted by ~9.1% (100/1100). The dilution is bounded by the reserve frequency — at `reserveFrequency=10`, every 10th mint is a reserve, capping dilution at ~10%.
|
|
18
18
|
- **128-tier limit hard-coded.** `_tierCashOutWeights` is a fixed `uint256[128]` array. Games with more than 128 tiers have tiers beyond index 128 unable to receive cash-out weights.
|
|
@@ -67,3 +67,11 @@ Cash-out weights set via `ratifyScorecardFrom` cannot be updated or corrected. T
|
|
|
67
67
|
### 8.4 ratifyScorecardFrom reentrancy is double-guarded
|
|
68
68
|
|
|
69
69
|
`ratifyScorecardFrom` executes arbitrary calldata on the hook via low-level call. The hook's `setTierCashOutWeightsTo` has an `onlyOwner` guard and a `cashOutWeightIsSet` check preventing double-set. Both guards prevent reentrancy exploitation.
|
|
70
|
+
|
|
71
|
+
### 8.5 Attestation snapshot uses block.timestamp - 1 (Codex R2 fix)
|
|
72
|
+
|
|
73
|
+
`attestToScorecardFrom` snapshots attestation weight at `block.timestamp - 1` instead of `attestationsBegin`. This prevents same-block transfer manipulation where a holder attests, transfers the NFT, and the recipient also attests in the same block. The trade-off is that NFTs minted in the same block as an attestation call have zero weight for that call -- the holder must wait 1 second. This is acceptable because attestation typically happens well after minting, and the 1-second delay is negligible.
|
|
74
|
+
|
|
75
|
+
### 8.6 Pending reserves dilute cash-out weight (Codex R2 fix)
|
|
76
|
+
|
|
77
|
+
`computeCashOutWeight` includes pending (unminted) reserve NFTs in the denominator. This means a paid holder's per-token cash-out share is reduced by the number of pending reserves in their tier. The trade-off is that if reserve NFTs are never minted (e.g., the reserve beneficiary is set to address(0) and minting reverts), those shares remain locked in the contract. This is acceptable because: (1) it prevents paid holders from front-running reserve minting to extract the reserves' share, and (2) reserve beneficiaries are set at deployment and should always be valid.
|
package/SKILLS.md
CHANGED
|
@@ -99,7 +99,7 @@ On-chain prediction game framework built on Juicebox V6. Players mint NFT game p
|
|
|
99
99
|
| `DefifaOpsData` | `token` (address), `start` (uint48), `mintPeriodDuration` (uint24), `refundPeriodDuration` (uint24), `minParticipation` (uint256), `scorecardTimeout` (uint32) | Internal game state in `DefifaDeployer` |
|
|
100
100
|
| `DefifaDelegation` | `delegatee` (address), `tierId` (uint256) | `DefifaHook.setTierDelegatesTo` |
|
|
101
101
|
| `DefifaGamePhase` | `COUNTDOWN`, `MINT`, `REFUND`, `SCORING`, `COMPLETE`, `NO_CONTEST` | Phase reporting throughout |
|
|
102
|
-
| `DefifaScorecard` | `attestationsBegin` (uint48), `gracePeriodEnds` (uint48) | `DefifaGovernor._scorecardOf` |
|
|
102
|
+
| `DefifaScorecard` | `attestationsBegin` (uint48), `gracePeriodEnds` (uint48) — set at submission time | `DefifaGovernor._scorecardOf` |
|
|
103
103
|
| `DefifaAttestations` | `count` (uint256), `hasAttested` (mapping(address => bool)) | `DefifaGovernor._scorecardAttestationsOf` |
|
|
104
104
|
| `DefifaScorecardState` | `PENDING`, `ACTIVE`, `DEFEATED`, `SUCCEEDED`, `RATIFIED` | `DefifaGovernor.stateOf` |
|
|
105
105
|
|
|
@@ -222,7 +222,8 @@ During COMPLETE phase cash outs, players also receive proportional $DEFIFA and $
|
|
|
222
222
|
- All tiers share the same price (`tierPrice` on `DefifaLaunchProjectData`).
|
|
223
223
|
- **Delegation only during MINT phase**. Other phases revert with `DefifaHook_DelegateChangesUnavailableInThisPhase`.
|
|
224
224
|
- If `totalTierUnits` is 0 for a tier (no delegations), that tier contributes no attestation power.
|
|
225
|
-
- **
|
|
225
|
+
- **Quorum stability via pending reserves**: `quorum()` counts tiers with either minted supply (`currentSupplyOfTier > 0`) OR pending reserves (`numberOfPendingReservesFor > 0`). No snapshot is needed because during SCORING, supply is frozen (no new paid mints, no burns) and reserve minting doesn't change which tiers are counted — tiers with pending reserves are already included. The pending reserves check matters when all paid tokens in a tier were burned during REFUND: `currentSupplyOfTier` drops to 0 but pending reserves persist.
|
|
226
|
+
- **Pending reserves dilute attestation power**: `getAttestationWeight` includes pending reserves in the denominator (total attestation units) but NOT the numerator (individual account units). Every token holder's voting power already accounts for reserves that will eventually be minted. When the reserve beneficiary later mints, their new NFTs add to the numerator while pending reserves decrease — no voting power shift for anyone. Consistent with the cash-out path (`computeCashOutWeight`) which also includes pending reserves in `totalTokensForCashoutInTier`.
|
|
226
227
|
- `ratifyScorecardFrom` uses **low-level `.call`** to execute the scorecard on the hook (necessary because `setTierCashOutWeightsTo` is `onlyOwner`).
|
|
227
228
|
- `fulfillCommitmentsOf` uses `max(amount, 1)` as a reentrancy sentinel. `sendPayoutsOf` is wrapped in try-catch: on failure, resets to sentinel (1) and emits `CommitmentPayoutFailed`.
|
|
228
229
|
- `_buildSplits` normalizes split percentages. Rounding remainder absorbed by the protocol fee split (last in array).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ballkidz/defifa",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0"
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
"url": "https://github.com/BallKidz/defifa-collection-deployer"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
17
|
-
"@bananapus/address-registry-v6": "^0.0.
|
|
18
|
-
"@bananapus/core-v6": "^0.0.
|
|
16
|
+
"@bananapus/721-hook-v6": "^0.0.22",
|
|
17
|
+
"@bananapus/address-registry-v6": "^0.0.16",
|
|
18
|
+
"@bananapus/core-v6": "^0.0.28",
|
|
19
19
|
"@bananapus/permission-ids-v6": "^0.0.14",
|
|
20
|
-
"@croptop/core-v6": "^0.0.
|
|
20
|
+
"@croptop/core-v6": "^0.0.23",
|
|
21
21
|
"@openzeppelin/contracts": "^5.6.1",
|
|
22
22
|
"@prb/math": "^4.1.1",
|
|
23
|
-
"@rev-net/core-v6": "^0.0.
|
|
23
|
+
"@rev-net/core-v6": "^0.0.18",
|
|
24
24
|
"scripty.sol": "^2.1.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|