Augur / Zoltar Paired whitepaper set
Companion paper Prediction Market Layer

Branching Oracle Layer

Protocol draft • Base layer for Placeholder

Zoltar White Paper

A visual guide to Zoltar universes, deterministic question encoding, scalar outcomes, and Colored Coins-style REP branching.

Core primitive Explicit child universes
Question types Categorical and scalar
Fork output Multiple valid child universes

Abstract

#

Zoltar is the base oracle layer in this repository. It does not run a market. It registers questions, records forks, and lets REP holders move value from a parent universe into one or more child universes after a fork.

What forks do Create valid branches instead of choosing one winner onchain.
What REP holders do Choose child universes and receive child REP for migrated balances.
What Zoltar omits Market collateral, trading, and underwriting mechanics.

Zoltar preserves disagreement explicitly. Instead of picking one winner onchain when a dispute becomes irresolvable inside one branch, Zoltar creates the valid child branches and lets later coordination determine which branch matters economically.

Zoltar supports both categorical and scalar questions, treats Invalid as a legitimate answer state, and keeps market collateral, trading, and underwriting logic out of the oracle layer. In Zoltar terminology, Invalid is a valid answer branch for a well-formed question, while Malformed means an encoded answer option is rejected before it can become a branch or final answer.

Lifecycle Timeline

#

Zoltar's lifecycle has six contract-visible steps, from question registration through post-fork coordination.

1. Register question

ZoltarQuestionData stores the question and defines its valid answer space.

2. Disagreement persists

After the question end time, any caller with threshold REP can call forkUniverse; applications decide when disagreement justifies doing so.

3. Parent universe forks

Zoltar records the fork question and deterministically defines every valid child branch.

4. Child universes deploy lazily

Branches exist by deterministic id first; contracts are deployed only when needed.

5. REP holders split

Migration balances mint child REP into one or more selected, non-malformed branches.

6. Coordination concentrates value

The protocol does not pick a winner. Users and applications decide where durable activity continues.

1. System Overview

#

Zoltar has one job: define the truth-layer objects that higher-level applications can rely on. It does not implement the market, collateral, or underwriting system built on top of it elsewhere in the repo. Its role is narrower and more fundamental:

  • register questions
  • encode valid answer spaces
  • represent forks as child universes
  • mint and burn child-universe REP
  • turn migration balances into selected child REP

In practice, that means an application can hand Zoltar a question, later trigger a fork if disagreement persists, and then consume the child-universe structure that Zoltar defines. Applications therefore depend on the same order in operation: question, disagreement, fork, then child-universe state.

Core Contract Map

Implementation entry points

Zoltar owns universe branching and splitMigrationRep; ZoltarQuestionData owns question validity.

Boundary to Placeholder

The market, vault, collateral, and auction systems live above this layer and are intentionally not encoded into Zoltar.

Core Layer Boundary

Zoltar questions, universes, forks REP minting, burning, splitting registered questions child universes

Core Layer BoundaryZoltar is the branching oracle layer: applications feed it questions and later consume the child-universe structure it defines.

2. Universe Model

#

A universe is one branch of protocol state. The Zoltar.Universe struct stores the minimum data needed to identify that branch and connect it to its parent:

  • forkTime: when the universe forked
  • forkQuestionId: the question id recorded by forkUniverse and used to define child branches
  • forkingOutcomeIndex: the outcome index represented by the universe when it is a child
  • reputationToken: the REP token used inside that universe
  • parentUniverseId: the parent branch

Universe 0 is the genesis universe. Its REP token is an external genesis token configured in Constants.GENESIS_REPUTATION_TOKEN. Child universes are identified deterministically as:

childUniverseId = uint248 ( keccak256 ( abi.encode ( parentUniverseId , outcomeIndex ) ) )

Child Universe IdChild universe ids are deterministic hashes of the parent universe and outcome index.

Deterministic child ids make each branch reproducible from parent universe and outcome index alone. Child universes are deployed lazily when a forked branch is actually needed.

Implementation entry points

forkUniverse records the parent fork, and deployChild materializes a deterministic child universe.

Determinism check

Child ids are derived from parentUniverseId and outcomeIndex, so callers can predict branch ids before deployment.

Fork Branch Set

Parent universe fork on disputed question parentUniverseId deterministic child ids Invalid Outcome 1 Outcome 2 Outcome N
Forking parent Invalid branch Valid outcome branches

Fork Branch SetA fork creates the full valid branch set; no branch is privileged by the contract.

Important distinction Zoltar defines the branch set. It does not select one canonical child universe or delete the others.

Global Question Scope

Questions are global protocol objects in Zoltar. forkUniverse checks that the target universe exists, still has supply, has not already forked, and that the supplied question exists and has ended. It does not require the question to have been created for that universe. Applications that need a stricter universe/question relationship enforce it above Zoltar.

Question Registry vs Application Binding

Question Registry created and ended forkUniverse question + universe id Forked parent universe Application Layer optional stricter market/pool binding

Question Registry BoundaryZoltar enforces global question validity and fork timing. Higher-level protocols decide whether a specific question is eligible for a specific market or pool.

3. Fork Thresholds and REP Economics

#

The threshold is a REP commitment large enough to force branch creation, but it is not itself a choice of the winning branch. The constructor stores forkThresholdDivisor; the current deployment default is 20.

forkThreshold = floor ( totalTheoreticalRepSupply forkThresholdDivisor )

Fork ThresholdWith the default divisor of 20, the threshold is one twentieth of theoretical REP supply, rounded down by Solidity integer division.

Initiating a fork requires supplying the computed threshold. With the default divisor, that is 5% of the universe's theoretical supply before integer flooring. The full threshold deposit leaves the parent universe: forkUniverse burns or transfers that entire parent-REP amount and reduces the parent theoretical supply by the full threshold. The burn divisor then controls how much child-REP minting credit the initiator receives. Under the current deployment default forkBurnDivisor = 5, one fifth of the threshold is an uncredited haircut and the remaining four fifths becomes the initiator's migration balance.

uncreditedForkHaircut = floor ( forkThreshold forkBurnDivisor )

Fork HaircutThe default uncredited share is one fifth of the threshold deposit, rounded down; the full parent-REP threshold is still burned or transferred out of the parent universe.

forkInitiatorMigrationBalance = forkThreshold - uncreditedForkHaircut

Initiator Migration BalanceWith the default burn divisor of 5, the initiator receives the threshold minus the floored one-fifth haircut.

Implementation entry points

forkUniverse applies the threshold, burns or transfers the full parent-REP threshold out of the parent universe, reduces parent theoretical supply by that full amount, and credits only the post-haircut migration balance to the initiator.

Genesis token exception

Genesis REP is transferred to BURN_ADDRESS; child REP can be burned directly by ReputationToken.

Threshold Deposit Split

Fork initiator deposits forkThreshold REP Threshold deposit 5% of theoretical REP supply 20% uncredited haircut 80% migration balance
Required fork deposit Uncredited migration haircut Migration balance

Threshold Deposit SplitThe full parent-REP threshold leaves parent supply. Most of that amount is re-expressed as migratable child-REP credit, while the haircut is not credited to the initiator.

Genesis REP cannot be burned natively, so the contract transfers it to the configured burn address. Child-universe REP is minted and burned directly by ReputationToken under Zoltar’s control.

A child's theoretical supply is a maximum for REP that can be minted in that branch. It starts from the parent's pre-fork theoretical supply and subtracts only the uncredited haircut. The initiator's remaining threshold deposit is migration credit, so it remains part of the maximum amount that could be minted in each child.

Repeated-fork threshold decay

Along one lineage, let theoreticalSupplyBeforeForkₙ be the theoretical supply before fork n. With the mainnet threshold divisor of 20 and burn divisor of 5, the exact integer recurrence is forkThresholdₙ = floor(theoreticalSupplyBeforeForkₙ / 20), haircutₙ = floor(forkThresholdₙ / 5), and theoreticalSupplyBeforeForkₙ₊₁ = theoreticalSupplyBeforeForkₙ - haircutₙ. This is close to multiplying supply by 0.99 at each generation, but both Solidity flooring operations matter once supply becomes small.

Launch-supply-dependent limit The number of generations is not a universal constant because the genesis supply snapshot is read when Zoltar is deployed. At Ethereum block 25,501,749 on July 10, 2026, calling getTotalTheoreticalSupply() on REPv2 at 0x221657776846890989a759BA2973e427DfF5C9bB returned 7,825,488.326666847200078019 REP. Starting from that value, the default recurrence reaches three relevant boundaries: at child depth 1,213, the fork threshold is approximately 1.986 REP, so half of it no longer exceeds the configured 1 REP escalation starting bond; at depth 1,282, the fork threshold is below 1 REP; and at depth 5,303, theoretical supply is 99 wei, the fork threshold is 4 wei, and the haircut floors to zero. Supply then stops decreasing along further descendants rather than reaching a zero fork threshold.

These depths require more than a thousand consecutive forks along the same surviving branch before escalation parameters become incompatible. They are therefore remote limits rather than near-term fork incentives. Implementers should nevertheless treat the first boundary as an explicit parameter limit: SecurityPool initializes an escalation game's non-decision threshold as half the current fork threshold, while EscalationGame requires that value to be strictly greater than its starting bond. A deployment expected to survive thousands of recursive forks needs either a minimum fork threshold or a compatible rule for scaling escalation parameters.

Parameter Current value Meaning
GENESIS_REPUTATION_TOKEN 0x221657776846890989a759BA2973e427DfF5C9bB Genesis-universe REP token address
BURN_ADDRESS 0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF Burn sink used for genesis REP
forkThresholdDivisor 20 Constructor immutable. Fork threshold is floor(totalTheoreticalRepSupply / forkThresholdDivisor); the default deployment value makes it about 5% of supply.
forkBurnDivisor 5 Constructor immutable. The uncredited migration haircut is floor(forkThreshold / forkBurnDivisor); the full threshold parent-REP amount still leaves parent supply.

4. Child Universes and REP Splitting

#

Once a universe forks, child universes can be deployed lazily through deployChild. A user can also add more REP into the migration balance with addRepToMigrationBalance. The core post-fork action is splitMigrationRep, which lets a holder mint child-universe REP for non-malformed outcome indices. Supplying no outcome indices is accepted as a no-op at the Zoltar layer.

For categorical questions, Invalid and any in-range categorical outcome are allowed, while out-of-range values are rejected. For scalar questions, only well-formed scalar encodings are allowed.

The fork question defines the child-branch shape for the whole universe. A child branch may therefore be keyed by Invalid, a categorical outcome index, or a scalar encoding depending on which parent-universe question forkUniverse used for the fork. Downstream applications should treat that branch selector as universe-level metadata rather than assume every child branch is binary.

Implementation entry points

addRepToMigrationBalance prepares additional REP, and splitMigrationRep mints child REP for selected outcomes.

Validation dependency

isMalformedAnswerOption distinguishes valid branches from rejected malformed answers.

Migration Balance Reproduction

Post-fork migration balance splitMigrationRep(selected outcomes) One selected branch mint only there Multiple branches reproduce same claim Selected child REP Invalid REP Outcome A REP Outcome B REP
Source migration balance Reproduced selected branches Single selected branch

Migration Balance ReproductionSelecting multiple branches mints child REP in each selected child universe from the same migration balance.

Colored Coins-style property The same migration balance can mint child REP in each selected child. Later value concentration determines which branch matters economically.

At the implementation level, splitMigrationRep validates each selected outcome against the parent universe's fork question, lazily deploys any missing child universe, and records how much of the caller's migration balance has already been minted into each child. A caller cannot mint more into one child than the source migration balance available to that caller, but the same source balance can be reproduced into multiple valid children.

5. Assumptions and Security Model

#

Zoltar is a Colored Coins-style system, so its security argument depends on user behavior and value concentration rather than on the contract being able to identify one objectively correct branch onchain.

  • users can choose which child universe to continue using after a fork
  • users prefer to continue in the universe they regard as truthful
  • the protocol itself does not know which universe is truthful and treats all valid branches symmetrically
  • most durable economic activity concentrates in the branch that users expect other users to keep using
  • dishonest or abandoned branches may continue to exist, but are assumed to retain little long-term value compared with the branch that market participants keep coordinating around

A fork only creates branches. If users and future activity concentrate in the branch they regard as truthful, the value of child-universe REP also concentrates there. Holders can reproduce a migration balance into multiple valid child universes, subject to the per-child cap and the gas and coordination costs of doing so. The economic pressure is therefore value concentration: REP in an abandoned branch may keep existing, but it is expected to have little long-term value compared with REP in the branch that markets keep using.

Global forks are migrations, not permanent shutdowns

Zoltar questions are global protocol objects: any existing question whose end time has passed can identify the branches of any still-unforked universe. Recording the fork stops new operational changes in parent-universe pools so that their accounting cannot move while it is being snapshotted. Placeholder provides permissionless, user-driven paths to migrate REP, shares, vault positions, collateral, and unresolved escalation state into child-universe pools. A child pool can return to the operational state once the migration window and any required truth-auction and finalization transactions complete. Timely progress therefore depends on callers submitting those transactions and on the available migration participation. The parent boundary is a transition into child universes, not an intended permanent freeze of market activity.

A caller cannot trigger that transition for free under normal parameters. The caller supplies the full fork threshold in parent REP, loses the uncredited haircut, and receives migration credit for only the remainder. Existing REP holders can migrate their own positions and benefit from the reduced supply in the branches where economic activity continues. Consequently, an unrelated fork is economically evaluated as a costly forced migration: its risk is operational migration latency and coordination burden, while its deterrent is the initiator's threshold commitment and permanent haircut.

Likewise, a question with many valid outcomes creates more possible children, but it does not multiply value in the branch that ultimately retains economic coordination. Deploying and using many children costs gas, and REP in abandoned branches is expected to have little value. Outcome count by itself is therefore a gas and state-growth consideration, not a demonstrated value-extraction attack.

6. Questions and Outcome Encoding

#

ZoltarQuestionData.QuestionData stores title, description, start and end time, scalar metadata such as numTicks, displayValueMin, displayValueMax, and answerUnit.

Question ids are deterministic hashes of the question data. For categorical questions, that hash path also includes the sorted categorical outcome options. For scalar questions, there are no categorical labels to include, so the id is determined from the scalar question fields alone.

Categorical Questions

Categorical questions store sorted outcome labels. The implementation requires labels to be non-empty and strictly ordered by hash: each label's keccak256(abi.encode(label)) value must be lower than the previous label's hash, so callers provide labels in descending hash order. The contract stores the labels in outcomeLabels[questionId]. Any number of categorical outcomes can exist at the Zoltar level as long as those conditions hold.

Scalar Questions

Scalar questions store no categorical labels. Instead, numTicks, displayValueMin, displayValueMax, and answerUnit define the answer space. At creation time, scalar questions must satisfy numTicks > 0 and displayValueMax > displayValueMin.

Onchain, each scalar answer is encoded into a single uint256 that packs:

  • the highest bit as an invalid flag
  • a 120-bit first payout numerator
  • a 120-bit second payout numerator
Implementation entry points

createQuestion stores categorical or scalar questions, and ScalarOutcomes formats scalar displays.

Scalar validity rule

The two 120-bit payout numerators must sum to numTicks; otherwise the answer is malformed, not invalid.

Packed Scalar Answer

most significant least significant bit 255 flag bits 239...120 first payout numerator bits 119...0 second payout numerator Unused high-order padding bits are not part of this scalar payload. Namespace 0 = Invalid 1 = scalar value Left Payout numTicks - tickIndex Right Payout tickIndex combined rule Scalar validity check first payout numerator + second payout numerator = numTicks
Namespace bit First payout Second payout

Packed Scalar AnswerA scalar answer is easier to read as a namespace flag plus two payout fields: 0 means invalid namespace, while 1 means the payout fields must sum to numTicks.

The all-zero encoding is the canonical Invalid answer for scalar questions. For a valid scalar answer, the two payout numerators must sum exactly to numTicks:

firstPayoutNumerator + secondPayoutNumerator = numTicks

Scalar ValidityA valid scalar answer must allocate all ticks across the two payout numerators.

At the helper and UI level, a scalar tick index named tickIndex is encoded as:

  • firstPayoutNumerator = numTicks - tickIndex
  • secondPayoutNumerator = tickIndex
  • highest bit = 1
( numTicks - tickIndex , tickIndex )

Scalar Tick EncodingThe helper encodes a tick as left and right payout numerators.

ScalarOutcomes interprets secondPayoutNumerator as the position along the scalar range:

displayedAtomic = displayValueMin + floor ( secondPayoutNumerator ( displayValueMax - displayValueMin ) numTicks )

Scalar Display ValueThe contract interpolates the scalar answer with mulDiv, so uneven divisions round down before the value is formatted.

displayValueMin and displayValueMax are stored as 18-decimal fixed-point display bounds. Formatting divides the atomic result by 1e18 and trims trailing zeroes. For example, with displayValueMin = 0, displayValueMax = 10e18, numTicks = 6, and secondPayoutNumerator = 1, the atomic value is floor(10e18 / 6), which displays as 1.666666666666666666 rather than an unrounded real number.

7. Question Types Supported by Zoltar

#

At the Zoltar layer, market type support comes from how questions and outcomes are encoded in ZoltarQuestionData.

  • categorical questions, implemented as an ordered array of non-empty outcome labels
  • scalar questions, implemented as a tick-based numeric range with no categorical labels

Zoltar can represent arbitrary categorical questions and scalar questions, while higher-level protocols may choose narrower market shapes on top of it. It defines answer spaces and forkable resolution state, not the collateralized trading mechanics that may later be attached to those questions.

8. Invalid vs Malformed

#

Zoltar distinguishes Invalid answers from Malformed answers.

Term Meaning Effect
Invalid A legitimate resolution state. Can be a valid branch and final outcome.
Malformed A submitted outcome index or scalar encoding that does not fit the question’s answer space. Rejected during child-universe REP splitting and fork-aware asset branching.

This distinction matters because malformed answers are rejected, while Invalid remains a valid branch and a valid final outcome.

9. Example Fork Lifecycle

#

Consider a universe parentUniverse and a question forkQuestion. After that question ends, forkUniverse can record it as the fork question for parentUniverse.

  1. forkUniverse(parentUniverse, forkQuestion) records forkQuestion as the fork question for parentUniverse.
  2. Child universes are defined deterministically from parentUniverse and each valid outcome index.
  3. The fork initiator’s full parent-REP deposit leaves parent supply, and the post-haircut amount becomes migration balance.
  4. Any REP holder can add more REP into migration balance for the forked universe.
  5. REP holders call splitMigrationRep to mint child-universe REP across the outcome branches they support.

At that point, Zoltar has turned one disputed universe into multiple child universes with separate reputation tokens. The higher-level economic meaning of those branches is left to protocols built on top.

10. Design Thesis

#

Zoltar provides a generalized branching oracle layer for categorical and scalar questions. Rather than forcing a single answer when disagreement persists, it turns unresolved decisions into explicit child universes and lets REP holders split post-fork claims across one or more selected branches.

In that sense it closely follows the Colored Coins model: the protocol branches state first, and later coordination determines which branch carries durable value.