Defifa is an onchain prediction game system built on Juicebox. Each game is a Juicebox project with staged rulesets, tiered NFT game pieces, a referee that submits the game's scorecard, and a final settlement path that turns the project's surplus into winner payouts.
Defifa ships two game types side by side: the ERC-721 game (DefifaHook, one NFT per position) and the ERC-1155 game (Defifa1155Hook, one balance per tier, so large entries mint in one transfer). Each has its own DefifaDeployer and DefifaGovernor; both share one DefifaTiersHookStore, one DefifaTokenUriResolver, and one DefifaVotingReferee.
Use this repo when the question is about game lifecycle, referees and scorecard ratification, holder attestation in the voting referee, or Defifa-specific settlement behavior. Do not start here for generic project accounting, terminal behavior, or standard 721-tier mechanics.
- ARCHITECTURE.md — system overview, module boundaries, where state lives.
- USER_JOURNEYS.md — end-to-end flows for players, scorers, and operators.
- INVARIANTS.md — structural, authorization, and lifecycle invariants of the game protocol.
- CRYPTO_ECON.md — cryptoeconomic model: pot formation, prize distribution formula, fee pipeline, parimutuel game theory, parameter design.
- RISKS.md — known risk surfaces and trust assumptions.
- ADMINISTRATION.md — privileged roles and operational responsibilities.
- AUDIT_INSTRUCTIONS.md — guidance for auditors entering the codebase.
- SKILLS.md — reusable patterns and gotchas captured during development.
- STYLE_GUIDE.md — repo conventions.
- CHANGELOG.md - V5 to V6 migration changelog.
Defifa adds game-specific behavior on top of Juicebox and the 721 hook stack:
- phased game launch and completion packaging in
DefifaDeployer - game-piece behavior, delegation, and settlement weighting in
DefifaHook(ERC-721) andDefifa1155Hook(ERC-1155) - count-based tier records in
DefifaTiersHookStore, the upstreamJB721TiersHookStoreplusrecordMintCounts,recordBurnCountsand a count form ofrecordTransferForTier - each game's referee, timelock, and scorecard ratification in
DefifaGovernor - the sample holder-vote referee in
DefifaVotingReferee: attestation, BWA power, quorum, grace periods, and revocation - onchain card rendering and token metadata in
DefifaTokenUriResolver
This repo does not own:
- canonical terminal accounting or surplus math
- generic 721 tier storage and most shared NFT-hook machinery
- generic Juicebox permission, project, or ruleset semantics
Defifa is easiest to read as one state machine split across three contracts, plus the game's referee:
DefifaDeployerlaunches the game and wires the components together- the game's referee decides the result and submits it to the governor. A referee is any address: a person, a multisig, or a contract that runs its own rule, such as
DefifaVotingReferee's holder vote DefifaGovernorholds the referee's pending scorecard through the game's timelock, then lets anyone ratify it- the game hook (
DefifaHookorDefifa1155Hook) converts that ratified result into cash-out weights for game-piece holders
The governor counts no votes. A game is as safe as its referee.
Most real issues live at the seams between those contracts.
src/DefifaDeployer.solsrc/DefifaHook.solsrc/DefifaGovernor.solsrc/referees/DefifaVotingReferee.solandsrc/interfaces/IDefifaReferee.solsrc/libraries/DefifaHookLib.solsrc/Defifa1155Hook.solandsrc/libraries/Defifa1155HookLib.solfor the ERC-1155 gametest/referee/DefifaGovernorCore.t.solandtest/referee/DefifaVotingReferee.t.soltest/DefifaGovernor.t.soltest/DefifaFeeAccounting.t.soltest/DefifaNoContest.t.sol
Then read the upstream repos this package depends on:
| Contract | Role |
|---|---|
DefifaDeployer |
Launches games, clones hooks, resolves ERC-2771 callers for launch attribution, names each game's referee in the governor, passes refereeData to a contract referee, and fulfills post-game fee commitments. |
DefifaHook |
ERC-721 game-piece hook that tracks tiers, delegation, pending reserves, and cash-out weights for settlement. |
DefifaGovernor |
Accepts scorecards from each game's referee only, holds the pending one through the game's timelock, and lets anyone ratify it. Counts no votes. |
DefifaVotingReferee |
Sample referee, one deployment for both game types. Anyone submits; holders attest with BWA power; a scorecard at its 50% to 75% quorum after the grace period is forwarded to the governor. Only the first approved scorecard per game is forwarded. |
Defifa1155Hook |
ERC-1155 game-piece hook (one balance per tier) that tracks delegation, pending reserves, and cash-out weights for settlement. |
DefifaHookLib |
Shared validation and weight logic extracted from the hook, used by both hooks. |
Defifa1155HookLib |
ERC-1155 ID and amount logic for Defifa1155Hook; calls DefifaHookLib for shared rules. |
DefifaTiersHookStore |
JB721TiersHookStore plus count-based records. Shared by both game types, required by the ERC-1155 hook. |
DefifaTokenUriResolver |
Onchain metadata renderer for game cards, shared by both game types. |
Each Defifa game is a Juicebox project with a staged lifecycle:
- countdown before minting opens
- mint phase where players buy outcome NFTs (ERC-721 game) or outcome positions (ERC-1155 game)
- optional refund or no-contest handling if the game cannot settle normally
- scoring phase where the game's referee submits a scorecard. A new submission replaces the pending one and restarts the timelock
- completion after anyone ratifies the pending scorecard once the timelock has passed
- final cash out where winning pieces redeem against the prize pot
If no scorecard is ratified within scorecardTimeout, the game ends in NO_CONTEST and players refund at mint price.
The important boundary is that the pot is still ordinary Juicebox project value until the governor ratifies a scorecard. Defifa-specific settlement starts only when the governor installs final cash-out weights on the hook.
npm install @ballkidz/defifanpm install
forge build --deny notes
forge test --deny notesUseful checks before opening or updating a PR:
forge fmt --check
forge build --deny notes --sizes --skip "*/test/**" --skip "*/script/**"
forge build --deny notes --build-info --skip "*/test/**" --skip "*/script/**"
npm pack --dry-run --jsonUseful scripts:
npm run deploy:mainnetsnpm run deploy:testnets
src/
DefifaDeployer.sol
DefifaGovernor.sol
DefifaHook.sol
DefifaTokenUriResolver.sol
enums/
interfaces/
libraries/
referees/
DefifaVotingReferee.sol
structs/
test/
governance, fee-accounting, no-contest, adversarial, regression, fork, and review coverage
referee/: core governor and voting referee tests
script/
Deploy.s.sol
helpers/
references/
operations.md
runtime.md
- Defifa is not a generic tournament payout primitive
- canonical Defifa infrastructure does not make every terminal or game instance canonical; frontends should still curate game addresses and scorecard parameters
DefifaGovernorandDefifaHookmust be read together- fee accounting and prize accounting are coupled
- a timeout into
NO_CONTESTis a real terminal state - a game's result is whatever its referee submits; read
DefifaGovernor.refereeOf(gameId)and show it before a player mints. Holder attestation means something only when the referee is a voting referee such asDefifaVotingReferee - an address referee decides alone; trust it as you'd trust a bookmaker
- the deployer checks only that
scorecardTimeoutexceedstimelockDuration; a voting referee's attestation delay and grace period must also fit inside the timeout, and the UI should check that NO_CONTESTview state should not be shown as refund-ready until the relevant settlement path is actually available- games with many outcome tiers need curation around scorecard liveness and, under a voting referee, attestation overhead
- the shared
JB721TiersHookStoresurface is an upstream ecosystem dependency, not a Defifa-only detail - detect the game type (for example
supportsInterface(type(IERC1155).interfaceId)) before encoding pay or cash-out metadata or reading holdings: the two hooks use different metadata layouts - in an ERC-1155 game, paid positions use token ID = tier ID and reserve-minted positions use
tierId + RESERVE_TOKEN_ID_OFFSET(1 << 16); read holdings withhook.balanceOf(account, id), because the store's per-holder views (balanceOf,tierBalanceOf,votingUnitsOf,tierVotingUnitsOf) count a tier's paid and reserve IDs together
test/referee/DefifaGovernorCore.t.sol: referee-only submit, timelock, replacement, ratify, states, timeouttest/referee/DefifaVotingReferee.t.sol: the voting referee, including first-forwarded-winstest/DefifaGovernor.t.soltest/DefifaGovernanceHardening.t.soltest/DefifaFeeAccounting.t.soltest/DefifaNoContest.t.soltest/DefifaAdversarialQuorum.t.soltest/regression/PendingReserveDilution.t.soltest/regression/AttestationDoubleCount.t.soltest/regression/GracePeriodBypass.t.soltest/erc1155/: the ERC-1155 suite, same file names as the 721 suite, plustest/erc1155/DefifaERC1155.t.soltest/DefifaHookParity.t.sol: both hooks expose the selectors the shared deployer, governor and resolver calltest/DefifaTiersHookStore.t.sol: count-based store records match the per-position originals
Deployments are handled through Sphinx. The deployer composes Juicebox core, the 721 hook stack, Defifa-specific governance, metadata rendering, and the core trusted forwarder into one ERC-2771-aware game-launch surface. script/Deploy.s.sol deploys one DefifaDeployer plus DefifaGovernor pair per game type over one shared DefifaTiersHookStore and DefifaTokenUriResolver; the ERC-1155 pair uses the salt keccak256(abi.encode(_salt, "ERC1155")). It also deploys one DefifaVotingReferee (salt DefifaVotingRefereeV6_) for both game types; it finds each game's governor through the game's project owner.
- game lifecycle config and post-game fulfillment bookkeeping:
DefifaDeployer - each game's referee, timelock, pending scorecard, and ratified scorecard:
DefifaGovernor - voting windows, submitted scorecards, attestations, quorum snapshots, reserve snapshots, and the forwarded scorecard:
DefifaVotingReferee - tier state, delegation, pending reserve awareness, and final cash-out weights:
DefifaHook(ERC-721) orDefifa1155Hook(ERC-1155 position balances) - tier config, supply, reserve, and burn accounting for both game types:
DefifaTiersHookStore - base project balance and terminal settlement:
nana-core-v6 - shared 721 tier store semantics:
nana-721-hook-v6
- Treat Defifa as a game-specific layer on top of
nana-core-v6andnana-721-hook-v6, not as a standalone accounting system. - Use
DefifaDeployer,DefifaHookorDefifa1155Hook, andDefifaGovernoras the primary execution surfaces. Both game types are supported products. Holder voting lives inDefifaVotingReferee, not the governor. - Treat
NO_CONTEST, refund handling, the governor's timelock, and a voting referee's grace period as normal product behavior. - Do not describe Defifa as an unlimited-outcome market deployer; game parameters should be curated for practical settlement and scorecard review.
- If the question is about generic tier storage, terminal settlement, or project accounting, hand off to upstream repos first.