Augur / Zoltar Paired whitepaper set
Companion paper Branching Oracle Layer

Prediction Market Layer

Protocol draft • Application layer built on Zoltar

Augur Statoblast White Paper

A visual guide to the market, underwriting, escalation, fork migration, and collateral-repair system built on top of Zoltar.

Collateral ETH-backed complete sets
Security REP vault underwriting
Recovery Migration plus truth auction

Abstract

#

Augur Statoblast is the market layer built on top of Zoltar. It creates security pools scoped to individual questions, lets traders mint and redeem ETH-backed shares, lets REP vaults provide underwriting, and tries to settle disputes locally before asking Zoltar to fork.

If a fork does happen, Statoblast migrates the market into child pools and can repair missing child-pool ETH collateral through a truth auction. Despite the name, that auction repairs collateral; it does not choose the truthful branch.

Market layer Per-question pools combine ETH shares with REP-backed underwriting.
Dispute layer Escalation tries to settle outcomes locally before a fork is needed.
Recovery layer Fork migration and truth auction repair keep child pools usable.

1. System Overview

#

The stack has two protocol identities. Zoltar is the base truth/forking oracle layer for universes, questions, scalar math, and REP branching. Augur Statoblast is the application layer that adds market collateral, underwriting, local disputes, migration, and collateral repair. For the base oracle layer, see zoltar-whitepaper.html.

Statoblast's lifecycle starts by creating a pool scoped to one question and universe, letting traders and REP vaults use it, trying to resolve disputes locally, and only then turning to fork and collateral-repair machinery. The overview sequence below follows that same order.

Statoblast also depends on two different oracle paths that answer different questions. Zoltar represents unresolved market-truth disputes by creating child universes. OpenOracle supplies only a fresh, contestable REP/ETH solvency price for withdrawals, allowance updates, and liquidations. Freshness is time-bounded, but the accepted value and its deviation from an external market price are not bounded onchain.

  1. A question is registered in Zoltar and a Statoblast security pool is deployed for that question in one universe.
  2. REP vaults fund underwriting capacity and users mint ETH-backed complete sets.
  3. After question end, disputes first try to resolve locally through the escalation game.
  4. If local resolution fails and the system reaches non-decision, Zoltar forks and Statoblast migrates pool state into child universes.
  5. If a child pool is missing ETH collateral after migration, it can sell child-universe REP through a truth auction to repair that collateral gap.
  6. The child pool in the branch users choose to keep using resumes operation and users settle or redeem positions there.

Happy Path vs Recovery Path

Checkpoint Happy path Recovery path
Resolution trigger Escalation picks a single local winner before non-decision. Two or more outcomes reach the non-decision threshold and Zoltar forks.
Where state settles The original pool remains the settlement venue. Vaults, shares, and collateral accounting can migrate into child pools. Each child receives one canonical unresolved-escalation snapshot and aggregate backing; winners later settle by proof.
Collateral condition Winning shares redeem against the existing ETH collateral base. A child pool may need truth-auction repair before redemptions are clean.
User mental model One pool, one final outcome, direct redemption. Choose the child universe that retains value, migrate positions, then settle there.
Actor swimlane lifecycle Trader, vault operator, and protocol responsibilities across minting, reporting, forking, migration, auction repair, and redemption. Trader Vault Protocol mint shares deposit REP deploy pool hold outcomes report outcome escalate fork/migrate redeem shares migrate vault repair ETH

Actor SwimlanesThe same lifecycle looks different by role: traders mostly mint, hold, migrate, and redeem; vaults underwrite and report; protocol contracts coordinate fork and repair steps.

Statoblast is not an exchange venue. It issues transferable ERC-1155 outcome shares and manages redemption, migration, and collateral accounting; secondary market trading sits outside this protocol.

2. Architecture

#
SecurityPool Question-specific underwriting and ETH collateral engine.
EscalationGame Local dispute game for Invalid, Yes, and No.
SecurityPoolForker Fork migration and child-pool activation coordinator.
Truth Auction Batch auction that sells child-universe REP for ETH repair.
Oracle Coordinator REP/ETH price flow for solvency-sensitive operations.
ShareToken ERC-1155 positions with universe-aware token ids.

3. Core Economic Roles

#
  • Question creators register questions in Zoltar question data, creating the objects around which Statoblast markets can be built.
  • REP vault operators deposit REP into a security pool and provide REP-backed underwriting capacity. Their deposits back open interest and earn fees extracted from collateral over time.
  • Users mint complete sets with ETH and hold outcome shares.
  • REP vault operators can escrow vault-backed REP behind Invalid, Yes, or No after a question ends so the local escalation process can settle locally or force a non-decision.
  • Anyone can create child pools, while vault owners and share holders move vault state and shares across child universes after a Zoltar fork. Child creation reproduces unresolved escalation state and aggregate backing once; winning proofs can be relayed permissionlessly and always pay their recorded depositors.
  • Truth-auction bidders buy REP with ETH when a child pool in a child universe needs to restore missing collateral.
  • Liquidators move undercollateralized bond allowance away from unsafe vaults.
  • Price reporters and settlers in the OpenOracle flow supply a REP/ETH solvency price for bond accounting, not a truth outcome for the question itself.

4. Security Pools

#

A SecurityPool is a lineage-specific Statoblast contract for a question in a universe. Vault operators deposit REP, choose bond allowance, and underwrite ETH-backed complete sets in that pool's local accounting ledger.

Why a per-question pool? The contract keeps market collateral, REP backing, escalation state, and fork migration scoped to one question and one universe. That boundary lets a fork or local escalation affect the market that depends on the disputed question without rewriting Zoltar's global question registry.

Origin pools are narrower than the general Zoltar question model. The factory requires the question to exist, the universe to be unforked, the universe REP token to be present, and the question to have exactly two categorical labels in this order: Yes, then No. Invalid is added by Statoblast as the third trading and resolution outcome; it is not one of the two origin-pool labels checked by the factory. The factory also records each deployment and exposes paged deployment-history reads for indexers and UIs.

Origin pool validation intentionally lives above Zoltar. Zoltar accepts global questions; Statoblast's factory decides which question shape is eligible for the current security-pool implementation.

Pool-related addresses are also deterministic. The SecurityPoolFactory derives a factory-level securityPoolSalt seed from the parent address, universe id, question id, and securityMultiplier, using a zero parent for an origin. The pool deployment worker's raw CREATE2 salt is zero; constructor init code commits the pool wiring. Coordinator and auction factories derive their own caller-scoped salts from the factory seed. See the Operator Reference for the exact address derivations. Each origin receives originId = keccak256(abi.encode(questionId, securityMultiplier, originUniverseId)). Migrated children retain that origin id and register with poolId = keccak256(abi.encode(originId, childUniverseId)). The origin id is also the share-token salt, so every lineage has a separate share-token contract and position namespace while all children of that lineage reuse the same contract.

An inherited child and a newly created origin may coexist for the same question, multiplier, and universe. This is intentional: their origin ids, pool addresses, share tokens, collateral ledgers, and migration paths are distinct. A caller selecting a pool therefore selects an origin lineage, not merely a question and universe. Because a fresh origin cannot occupy an inherited lineage's registry entry, origin deployment does not scan universe ancestors.

ETH, REP, shares, and fee flow Users send ETH and receive shares; vaults send REP and receive fees; fork repair can sell child REP for ETH. Trader sends ETH SecurityPool collateral accounting fee extraction ShareToken Invalid + Yes + No ETH collateral mint shares redeem full sets while operational or finalized winning shares Vault deposits REP Pool Accounting REP and ETH ledger REP backing fees owed Truth Auction child repair ETH repair child REP sold

Asset FlowThe pool is the accounting hub: ETH backs complete sets, REP backs underwriting, fees accrue to vaults, and child REP can be monetized to repair child collateral after a fork.

The next equations compare REP backing with ETH-denominated obligations. This price is a price-oracle value, not a truth outcome. OpenOracle settles WETH/REP amounts, and the coordinator stores the result as scaled REP per ETH because solvency checks compare REP backing against ETH obligations.

repPerEthPrice = repAmount pricePrecision ethAmount

REP/ETH PricerepPerEthPrice is stored as scaled REP units per ETH, and pricePrecision is the fixed scale factor that keeps REP-side and ETH-side terms in the same integer math units.

remainingRepBacking pricePrecision securityBondAllowance repPerEthPrice

Bond SolvencyVault and pool bond checks require remaining REP value to cover the ETH-denominated security-bond allowance.

repBacking pricePrecision > securityBondAllowance repPerEthPrice

Allowance BackingSetting or increasing bond allowance uses a strict inequality at both the vault and pool level, so equality is not enough to authorize new allowance.

securityBondAllowance securityMultiplier repPerEthPrice > repBacking pricePrecision

Liquidation ConditionA vault becomes liquidable when its scaled debt exceeds its REP backing after applying the security multiplier.

The contract applies related conditions at both the vault level and the whole-pool level before allowing operations such as performWithdrawRep, performLiquidation, and performSetSecurityBondsAllowance.

Users and Complete Sets

Users call createCompleteSet with ETH. The pool mints one Invalid, one Yes, and one No ERC-1155 share through ShareToken for the current universe. The deposited ETH becomes completeSetCollateralAmount.

  • While the pool is operational, a full complete set can be burned with redeemCompleteSet to recover its pro rata share of collateral. A child uses its remaining economic claim supply as the denominator, including fork-time claims whose ERC-1155 balances have not materialized there yet.
  • After the fork route finalizes an outcome, users can redeem winning outcome shares through redeemShares.
  • Minting checks the next collateral amount against available bond capacity before minting shares, then updates pool accounting before the share-token mint call.
  • A positive ETH deposit must convert through cashToShares to at least one complete-set unit. If the current exchange rate would round the mint to zero shares, createCompleteSet reverts with Exchange rate undefined instead of accepting collateral.

Fee Extraction and Retention Rate

Complete-set collateral gradually decays into an unallocated fee reserve. Vault checkpoints turn whole-wei entitlements from that reserve into redeemable vault debt. The retention rate falls as utilization rises, so security becomes more expensive as open interest consumes underwriting slack.

  • completeSetCollateralAmount starts higher and decays gradually.
  • totalFeesOwedToVaults grows only when a vault checkpoints whole-wei fees that it can redeem; accrued but not yet checkpointed fees remain in a separate pool reserve.
  • feeIndex tracks vault fee accounting.
  • currentRetentionRate changes as utilization changes.
Collateral retention and fee flow: Unallocated Reserve to Assigned Vault Debt to Vault Payout Complete-set collateral decays into an unallocated reserve. Vault checkpoints assign whole wei as redeemable vault debt. Complete-Set Collateral starts high; then decays decay Unallocated Reserve accrued, not yet redeemable checkpoint Assigned Vault Debt whole wei, redeemable redeem Vault Payout ETH leaves the pool higher utilization lowers retention and accelerates fee extraction

Fee FlowComplete-set collateral decays into a reserve; checkpointing assigns whole-wei vault debt before redemption.

Retention follows a piecewise heuristic in SecurityPoolUtils.calculateRetentionRate. It starts high, declines linearly until 80% utilization, and then stays at the minimum retention rate. The intent is to charge more aggressively for security as utilization rises and underwriting slack falls. The Retention Rate Curve figure shows the same shape as the contract.

Retention-rate curve Retention starts at the maximum rate, declines linearly until 80 percent utilization, and then stays at the minimum rate. utilization retention rate max min 80% Linear decline higher utilization extracts more fees Floor after dip min retention

Retention Rate CurveThe retention curve is piecewise: linear until the utilization dip, then flat at the minimum retention rate.

Read as a user-facing metric, that retention curve becomes the annualized open-interest fee shown in the UI. At zero utilization, the constants imply roughly a 10% yearly fee. Once utilization reaches the 80% dip, the annualized fee is roughly 50% and then stays flat because the on-chain retention rate is already pinned to its minimum. The plotted line below follows the same annualization the UI uses: annualFee = 1 - (retentionRate / PRICE_PRECISION)^SECONDS_PER_YEAR.

Open-interest fee per year The annualized open-interest fee starts near ten percent at zero utilization, rises as utilization increases, reaches roughly fifty percent at eighty percent utilization, and then stays flat. utilization open interest fee / year 10% 50% 0% 80% 100% Annualized fee rises utilization makes underwriting more expensive Fee cap reached retention floor keeps fee flat

Open Interest Fee CurveThe retention constants translate into an annualized open-interest fee that rises from about 10% per year to about 50% per year, then flattens once the utilization dip is reached.

utilizationScaled = floor ( completeSetCollateralAmount PRICE_PRECISION totalSecurityBondAllowance )

UtilizationThe retention curve uses open collateral divided by total underwriting allowance as 18-decimal fixed-point integer math. Below the dip, the contract then computes utilizationRatio = floor(utilizationScaled * PRICE_PRECISION / RETENTION_RATE_DIP) before applying the linear slope.

retentionRate = { MAX_RETENTION_RATE if totalSecurityBondAllowance = 0 MAX_RETENTION_RATE - floor ( ( MAX_RETENTION_RATE - MIN_RETENTION_RATE ) utilizationRatio PRICE_PRECISION ) if utilizationScaled <= RETENTION_RATE_DIP MIN_RETENTION_RATE otherwise

Retention Rate CurveThe implementation returns the maximum retention rate when allowance is zero, otherwise it linearly declines to the dip and then clamps to the minimum rate.

decayCandidate = collateralIn - floor( collateralInrpow(retentionRate,elapsedTime,pricePrecision) pricePrecision )

Retention Decay Candidaterpow uses fixed-point integer rounding. The candidate feeds the index and remainder steps below; it is not yet the amount removed from stored collateral.

When feeEligibleSecurityBondAllowance == 0, the pool sets feeIndexDelta and reserveCredit to zero, clears feeIndexRemainder, leaves collateral unchanged, and advances the accumulator to the clamped current time. This prevents unclaimed auction allowance from earning retroactive fees. The remaining fee-index equations apply only when fee-eligible allowance is positive.

feeIndexDelta = floor ( decayCandidate pricePrecision + feeIndexRemainderIn feeEligibleSecurityBondAllowance ) feeIndexRemainderOut = (decayCandidatepricePrecision+feeIndexRemainderIn) modfeeEligibleSecurityBondAllowance reserveCredit = floor( feeIndexDeltafeeEligibleSecurityBondAllowance+globalFeeRemainderInpricePrecision ) globalFeeRemainderOut = (feeIndexDeltafeeEligibleSecurityBondAllowance+globalFeeRemainderIn) modpricePrecision collateralOut = collateralIn-reserveCredit vaultFees = floor( vaultSecurityBondAllowancevaultFeeIndexDelta+vaultFeeRemainderInpricePrecision ) vaultFeeRemainderOut = (vaultSecurityBondAllowancevaultFeeIndexDelta+vaultFeeRemainderIn) modpricePrecision

Fee Index AccrualOnly assigned, fee-eligible allowance divides the global index. Only whole-wei reserveCredit leaves stored collateral; index and global remainders retain the rest. Vault checkpoints preserve their own fractional carry while moving whole wei into redeemable debt.

Fee accrual is time-clamped. The pool extracts collateral into fees only until the question end time while the universe remains unforked; after the universe forks, the fee accumulator is instead clamped to the fork time. Retention-rate updates also no-op when total bond allowance is zero or when the pool is outside Operational mode, so fork handling does not silently change the live fee curve.

A child pool begins a new fee epoch when migration and any truth auction finalize. Only allowance already assigned to a vault enters the fee denominator. Auctioned allowance becomes fee-eligible when the winning bid is claimed, after that vault first checkpoints at the current index. Each delayed claim adds only its newly assigned amount to the live eligible total; it does not reconstruct that total from fork-time migration counters, so intervening allowance changes and liquidations remain intact. Per-vault fractional remainders survive public checkpoints, while whole fees move from the pool reserve into totalFeesOwedToVaults; this keeps aggregate debt equal to redeemable vault balances. The totalAccruedFees() view adds that assigned debt to the unallocated reserve when callers need to reconcile all fee-adjusted collateral.

A fork permanently closes the parent's fee epoch. The pool tracks how much eligible allowance still has not checkpointed the final index. After every eligible vault syncs, any whole reserve wei left solely from aggregating individually sub-wei vault remainders cannot become redeemable, so it returns to complete-set collateral. Until the last checkpoint, that reserve remains protected by totalAccruedFees().

Pool accounting conversions REP deposits mint proportional pool ownership, decayed collateral increments the fee index, and vault allowances claim fees from that index. REP Amount vault collateral Pool Ownership proportional claim Redeemable REP inverse conversion Collateral Decay fees produced over time Fee Index global accumulator Vault Fees by allowance

Pool AccountingREP ownership and fee accounting use separate proportional ledgers: ownership tracks REP claims, while the fee index tracks decayed ETH collateral owed to vaults.

poolOwnership = repAmount poolOwnershipDenominator totalRepBalance repAmount = poolOwnership totalRepBalance poolOwnershipDenominator

Pool Ownership ConversionVault REP claims are represented as ownership units against the current pool REP balance and ownership denominator.

Solvency Operations and Liquidation

Withdrawals, allowance changes, and liquidation depend on a fresh REP/ETH price. Liquidation transfers unsafe allowance to a non-liquidatable caller vault and seizes unlocked target REP, so it redistributes backing rather than creating it.

The Liquidation Design owns the queue snapshot, eligibility gates, penalty and rounding math, health improvement rule, and worked examples. The OpenOracle integration owns price staging and callback behavior.

Implementation guardrails for withdrawal locks, external-fork locks, liquidation snapshots, direct ETH receiver restrictions, fee clamps, and complete-set minting order are in Security Pool Guardrails .

5. Shares and Complete Sets

#

ShareToken is an ERC-1155 contract. Each complete set mints one Invalid, one Yes, and one No share. The token id encodes the universe id and outcome, making positions fork-aware.

Complete set lifecycle ETH mints Invalid, Yes, and No shares. Operational pools redeem full sets, and finalized winning shares redeem through outcome settlement. User Sends ETH createCompleteSet Receives Full Set Invalid + Yes + No Before Finalization burn full set recover collateral After Finalization winning leg redeems payout Collateral Settlement

Share LifecycleShares are transferable until redemption or the first fork-claim materialization. Materialization preserves the source balance but locks it against later transfer.

Shares can materialize across forks. ShareToken.migrate preserves a parent-universe token id as a persistent entitlement and mints child-universe token ids for selected valid, non-malformed fork outcomes. The formal transfer-lock and per-child delta rules are owned by FORK-10.

Share migration materializes persistent branch entitlements On first materialization into untouched children, the full current parent balance materializes in each selected child token id while the source remains locked as an entitlement. Later calls mint only the unmaterialized delta for each child. Parent Token Id full holder balance migrate() records target delta checks sorted targets Child Token A first: full balance Child Token B first: full balance

Share MigrationShare migration is not a pro-rata split across selected branches. The source balance remains as a transfer-locked entitlement after first use, and each untouched selected child first materializes the full current balance. Later calls mint only that child's unmaterialized delta.

Adjust a first-materialization share example

This example assumes that none of the selected children has received this source entitlement before. The first call preserves the full parent token balance and materializes that balance in each selected child outcome. On a later call, each child receives only current source balance - amount already materialized; if every selected delta is zero, the call reverts. The contract accepts any non-empty, strictly increasing list of valid non-malformed fork-question outcome indexes. The compact diagram caps the displayed branches at three.

Parent retained + locked 30 30 30 0
Parent balance after call30 shares
First materialized balance per selected branch30 shares
Total displayed child balances60 shares
  • The selected target list must be non-empty.
  • The caller must hold a positive balance of the parent token id.
  • The parent balance remains as a persistent entitlement. After its first child materialization, that source balance cannot transfer.
  • Target outcomes must be valid for the fork question and strictly increasing, which prevents duplicate child mints in one call.

At the implementation level, redeemShares burns the holder’s full materialized balance of the winning token id and pays the burned amount’s pro-rata share of current collateral, using the pool’s remaining economic claim supply as the denominator. The canonical lifecycle is SHARE-04, and unequal materialized supplies are covered by FORK-11. That shareTokenSupply includes materialized ERC-1155 balances and fork-time entitlements that can materialize later. In the healthy case this corresponds to a clean 1:1 redemption outcome against the economic meaning of a complete-set claim, subject to collateral accounting remaining intact through migration and any required auction repair.

cashPayout = floor ( burnedWinningShares completeSetCollateralAmount economicClaimSupplyBeforeBurn )

Share RedemptionThe remaining economic claim supply reserves collateral for both materialized winners and winning fork entitlements that arrive later. Solidity integer division floors any remainder.

The important edge case is fork recovery. If a child pool emerges from migration with incomplete collateral, the truth auction sells REP for ETH so the child pool can restore the collateral base needed for clean redemption. If multiple target outcomes are selected during share migration, the holder’s source entitlement remains and its unmaterialized balance is minted into each selected child.

6. Escalation Resolution

#

Statoblast tries local resolution before asking Zoltar to fork. Vault operators move REP out of their security-pool position and behind Invalid, Yes, or No. The required attrition cost rises from the starting bond to the non-decision threshold over seven weeks after a three-day activation delay.

Local dispute role Escalation is a local dispute game before the protocol escalates to a Zoltar fork.
Contract behavior Deposits compete against a rising cost curve and a fixed non-decision threshold.
Edge cases Ties, fork-threshold scaling, and interrupted unresolved games change payout handling.
Why not fork immediately? A fork asks the whole universe to branch, so Statoblast first gives vault-backed REP a local path to settle the question. The rising cost curve makes late disagreement increasingly expensive, while the non-decision threshold keeps the fork path available when competing outcomes remain strongly funded.
requiredEscalationCost ( elapsedEscalationTime ) = startingBondAmount exp ( ln ( nonDecisionThresholdAmount startingBondAmount ) elapsedEscalationTime fullEscalationInterval )

Escalation Cost CurveThe required dispute bond rises exponentially from the starting bond to the non-decision threshold over the full interval.

  • requiredEscalationCost(0) = startingBondAmount.
  • requiredEscalationCost(fullEscalationInterval) = nonDecisionThresholdAmount.
  • The cost rises monotonically between those endpoints.

The plot below is the same curve used by totalCost() and computeIterativeAttritionCost: early deposits face the starting bond, while sustained late disagreement approaches the non-decision threshold. The plotted curve is the continuous model; Solidity computes it with fixed-point integer approximation using SCALE = 1e6 and clamps the endpoints.

Escalation cost curve The required bond rises from the start bond to the non-decision threshold over the full escalation interval. activation starting bond full interval non-decision threshold rising attrition cost required REP elapsed time

Cost CurveThe curve makes delay expensive without jumping immediately to the fork threshold. Early disagreement can settle locally; sustained late disagreement becomes costly enough to justify the non-decision path.

Escalation deposits enter through SecurityPool.depositToEscalationGame. The first valid deposit after question end deploys the game, sets activationTime = block.timestamp + 3 days, and records the game’s starting parameters. Later deposits can continue only while the pool is operational, the game has resumed if it came from a fork continuation, and the child pool is not still waiting for continuation processing.

The security pool wrapper first previews the accepted amount, then removes the corresponding vault REP ownership with round-up accounting, checks the vault and pool remain solvent, enforces the minimum remaining REP rule, and transfers REP into the escalation game from the caller's existing vault position rather than from free-floating wallet REP. If the pool has any active security-bond allowance, the call also requires a fresh REP/ETH oracle price before the deposit can proceed. Inside the game, the proposed amount must be at least startBond. The recorded amount must be positive and either at least startBond or exactly fill the selected outcome to nonDecisionThreshold.

The game rejects invalid targets, full outcomes, and post-non-decision deposits. Accepted deposits are clipped to the selected outcome's remaining room, and the game applies a small tie-avoidance adjustment when a below-threshold deposit would otherwise make two outcomes share the maximum balance.

Escalation Deposit Trace

Step Contract area State or check Why it matters
Preview amount EscalationGame.previewDepositOnOutcome Computes accepted amount, threshold room, and tie adjustment. The submitted maximum is not always the recorded deposit.
Remove vault ownership SecurityPool.depositToEscalationGame Burns pool ownership with round-up accounting and checks solvency. Escalation uses existing vault-backed REP, not free wallet REP.
Transfer REP SecurityPool to EscalationGame Moves accepted REP into local game custody. The game can later settle, export, or carry that locked REP.
Record deposit EscalationGame.recordDepositFromSecurityPool Updates outcome balances, unresolved totals, and deposit indexes. Those indexes are the handles used by later withdrawal and fork paths.
Adjust escalation deposit clipping and tie avoidance

The accepted deposit is capped by remaining room under the non-decision threshold. If a below-threshold deposit would tie the current leading outcome, the game reduces the accepted amount by one atomic REP unit (1 wei of REP precision).

Before After accepted deposit Invalid Yes No threshold 10 REP
Room on No3 REP
Accepted amount3 REP
Adjustmentclipped to threshold
Post-deposit conditionNo reaches threshold
Adjust escalation resolution edge cases

The resolution helper first checks whether at least two outcomes meet the running cost. If that unresolved test does not apply and all three balances are zero, the helper returns Invalid. Otherwise, a strict Invalid, Yes, or No lead wins. Valid deposits prevent tied maxima below non-decision by reducing the accepted amount by one atomic REP unit, or reverting if that adjusted amount becomes invalid. Continuation snapshots preserve the parent balances exactly, including tied maxima below nonDecisionThreshold, so all selected children begin from the same unresolved state.

Balances compared with running cost Invalid Yes No cost 5 REP
Outcomes at or above cost2
Resolution helper returnsNone
Reasontwo outcomes still contest the cost

The contract exposes both directions of this curve: it can compute the required cost from elapsed time, and it can derive the effective end time from the final binding capital. Before activation, totalCost() is zero. In fork-continuation games, elapsed time can be inherited from a parent game and then resume later instead of restarting from zero.

Escalation thresholds Outcome balances are compared to running total cost for local resolution and to the fixed non-decision threshold for a local game's fork eligibility. A pool with an inherited fixed outcome rejects both new local deposits and fork activation. Tracked Balances Invalid Yes No Running totalCost() two sides at or above threshold means unresolved Local Resolution Non-Decision Threshold two or more outcomes reach fixed threshold Game-Local Eligibility pool guard still applies

Non-Decision FlowA live unresolved contest is not the same as the stronger non-decision condition. This diagram shows a local threshold crossing, which makes canTriggerOwnFork() true. A pool with an inherited fixed outcome rejects the deposits that could create this state and still rejects activateForkMode().

Payouts depend on final binding capital, the median of the three outcome balances. Winning capital inside the reward-eligible window shares a fixed reward pool; excess winning capital receives principal only.

If time expires without non-decision, the resolution ordering in the interactive example above applies. A local deposit that brings a second outcome to nonDecisionThreshold records nonDecisionState = Local and nonDecisionTimestamp, then makes the game-local canTriggerOwnFork() predicate true. That predicate does not override pool finality: a pool with an inherited fixed outcome rejects new depositToEscalationGame calls before they can create a local non-decision and still rejects activateForkMode(). A fork continuation instead records nonDecisionState = InheritedThresholdTie when its preserved balances already meet that condition, without rebasing balances or fabricating a local timestamp. Both explicit states close further deposits. A continuation with a fixed child outcome follows its remaining clock and settles to that outcome; one without a fixed outcome can open its own fork directly.

For the exact resolution ordering, empty-game fallback, deposit clipping, carry batching, and residual sweep rules, see Escalation Resolution and Deposits.

The median matters because the game is trying to detect whether disagreement remains live across multiple sides, not merely whether one side has accumulated the most stake. binding capital measures the level at which the contest is still jointly sustained by competing outcomes, which is why it drives timeout and payout logic.

Escalation payout regions Winning deposits below binding capital and in the safety boundary share rewards, while excess receives principal only. Binding capital Safety boundary Excess principal plus pro-rata reward same reward pool principal only

Payout RegionsWinning deposits below binding capital and in the safety boundary share rewards, while excess receives principal only.

rewardEligibleCap = bindingCapital + floor( bindingCapital excessRewardWindowDivisor ) rewardBonusPool = floor( bindingCapital 3 5 ) burnAmount = floor( rewardEligibleDeposit floor( bindingCapital 2 5 ) rewardEligiblePrincipal ) amountToWithdraw = depositAmount + floor( rewardEligibleDeposit rewardBonusPool rewardEligiblePrincipal ) scaledWithdrawal = floor( amountToWithdraw actualForkThreshold nonDecisionThreshold ) if actualForkThreshold < nonDecisionThreshold

Winning PayoutReward-eligible winning principal shares the 60% bonus pool pro rata, while a 40% haircut is charged exactly once. If the escalation itself triggers the universe fork, Zoltar collects that haircut from the aggregate escalation backing; otherwise settlement burns it directly. Low fork thresholds scale the payout down. All amounts are atomic REP, and every Solidity division floors before the next step.

  • The binding-capital region defines a fixed reward pool.
  • 40% of that binding-capital region is the winner haircut, so the remaining 60% becomes the reward pool.
  • The reward-eligible cap is bindingCapitalAmount + floor(bindingCapitalAmount / EXCESS_REWARD_WINDOW_DIVISOR).
  • The region between binding capital and that cap is the safety boundary.
  • Winning capital above the reward-eligible cap is excess and receives principal only.
Example: if final binding capital is 10 REP, the reward-eligible cap is 15 REP, the safety boundary is (10 REP, 15 REP], and the reward pool is 6 REP. If the winning side contributes 15 REP inside that reward-eligible window, each 5 REP deposit receives 7 REP. Winning depth above 15 REP receives principal back with no share of the reward pool.

If the actual Zoltar fork threshold for the universe is lower than the escalation game’s configured nonDecisionThreshold, the payout is scaled down by actualForkThreshold / nonDecisionThreshold.

Adjust escalation payout regions and threshold scaling

Winning capital up to the reward cap shares the fixed reward pool. Winning capital above the cap receives principal only, and a lower actual fork threshold scales the withdrawal down. Tied leaders do not resolve to a fallback outcome, so the payout path only applies once one outcome has a strict lead or non-decision has already routed the market into the fork path.

0 binding 10 cap 15 winning 18
Reward-eligible cap15 REP
Reward bonus pool6 REP
Reward-eligible part of this deposit5 REP
Excess principal3 REP
This deposit withdrawal7 REP
Scaled withdrawal7 REP
Payout statereachable game state

Settlement uses different calls for local withdrawal, own-fork direct claims, optional parent-lock cleanup, and child proof claims. The distinction matters because local withdrawal settles deposit indexes, while continuation games settle inherited winners by proof and retire inherited losers without a call.

Escalation Settlement Call Matrix

Entry point Used when Claim unit
withdrawFromEscalationGame A local game reaches a final outcome without non-decision and no earlier universe fork interrupted it. This includes the ordinary no-fork lifecycle. Local deposit indexes for one beneficiary vault per call.
claimForkedEscalationDeposits A non-decision fork exists and the vault claims winning parent deposits for a selected outcome. Winning parent deposit indexes; own-fork claims pay the ordinary winning reward in child REP to the vault wallet. The escalation's own fork has already removed the winner haircut from aggregate backing, so the claim does not burn it again.
migrateVaultWithUnresolvedEscalation Optional cleanup after an own or external fork captured unresolved local locks before the game finalized. The affected vault invokes the wrapper for its own parent lock totals. See the operator cleanup reference for ordering, scope, and proof independence.
withdrawForkedEscalationDeposits A child continuation game finalized; only inherited winning deposits are claimable, while inherited losers require no call. Carried-deposit proofs for one beneficiary vault per call.

The pool removes ownership and escrows REP when the user deposits into the local game. Settlement does not re-mint that ownership: a winner receives the ordinary winning payout as wallet REP, while a losing settlement clears escrow without a payout. An own-fork direct claim uses the same winning reward math and releases pre-funded child REP from the selected child's aggregate backing to the vault owner's wallet. It is not a claim-time conversion and does not mint child-pool ownership. Own-fork direct claims must still be submitted before the parent migration window closes.

Caller permissions are intentionally mixed. withdrawFromEscalationGame and withdrawForkedEscalationDeposits are liveness helpers that any caller can submit so long as every proof or deposit index in the batch belongs to the same beneficiary vault. By contrast, claimForkedEscalationDeposits and the optional migrateVaultWithUnresolvedEscalation cleanup require msg.sender to be the affected vault.

Fork interruption changes the claim path, but the canonical continuation lifecycle—snapshotting, aggregate backing, child activation, proof settlement, optional cleanup independence, and recursive re-forking—lives in Forks and Migration. This section only distinguishes the settlement entry points and their caller permissions.

Continuation claims use Merkle Mountain Range proofs and nullifier roots. The canonical leaf fields, peak ordering, proof lengths, and replay rules are documented in Merkle Mountain Range carry proofs.

7. Forks and Migration

#

Fork handling spans both layers. Zoltar creates child universes and handles REP splitting. Statoblast separately moves vault state, proportional collateral, and share positions into child pools, while child creation reproduces unresolved escalation state from one canonical snapshot and aggregate backing transfer.

The child-pool selector comes from the universe’s fork question, not from Statoblast’s binary market shape. A binary Statoblast pool can therefore spawn child pools keyed to scalar encodings or multi-way categorical outcomes when the universe fork used a different Zoltar question.

Accepted global-fork consequence. Zoltar's global-question scope owns admission rules and their security classification. For Statoblast, the consequence is universe-wide: an admitted fork pauses globally guarded parent flows for every pool while migration proceeds.

Fork branch role A Zoltar fork creates branches; Statoblast decides what pool state follows each branch.
Contract behavior Globally guarded parent flows halt while child pools collect migrated vaults, collateral, shares, and escalation state.
Edge cases Only continuations without a fixed outcome can fork again; their game-local trigger may be a local non-decision or an inherited threshold tie.
State Meaning Typical transition out
Operational Stored state for normal operation outside the explicit fork lifecycle. A late universe fork can still disable globally guarded calls without changing this enum value. Local resolution or fork handling activation.
PoolForked Fork handling was activated for this parent pool, which has stopped normal operation. Child pools begin migration setup.
ForkMigration Child pool receives migrated vaults, REP-derived state, and collateral. Migration window ends.
ForkTruthAuction Child pool repairs missing collateral by selling REP. Auction finalization accounts legitimate routed collateral plus accepted bid ETH and activates the child at that level. It rejects explicit caller contribution ETH.

SystemState is primarily a fork-and-migration state machine, not a generic question-resolution state machine. A pool finalized before a late, unrelated universe fork can retain SystemState.Operational. Its isOperational-guarded activity still freezes because that modifier separately checks the universe fork time, while finalized share, REP, and eligible escalation-deposit redemption paths that check only the stored state remain callable.

SecurityPool fork state machine Contract states and conditional transition functions from an operational parent through migration and bounded truth-auction settlement to an operational child. Operational parent pool fork mode PoolForked parent halted create child ForkMigration child pool start auction Fork Truth Auction repair phase auction settled Operational bids accounted no auction needed migration calls vaults, REP, unresolved escalation

Fork State MachineWhen fork handling is activated, the parent moves to PoolForked; child pools do migration and optional auction repair. Only a child whose legitimate routed collateral and accepted bid ETH have been accounted returns to operational mode. Finalization rejects explicit contribution ETH, while forced ETH remains unaccounted surplus. A previously finalized pool affected by a late unrelated fork is the separate stored-state case described above.

  • The parent pool enters fork mode.
  • Anyone can call createChildUniverse to deploy a child pool for a valid fork outcome.
  • Vault owners can call migrateVault to move their own unlocked pool ownership, allowance, fees, and collateral whether or not unresolved external-fork escalation locks exist. Parent-lock cleanup remains a separate optional concern.
  • A forked unresolved game initializes each selected child with one canonical carry snapshot and one aggregate backing transfer. Unrelated forks carry the game REP 1:1; the escalation's own fork carries it after the single winner haircut. No vault migration call is required to fund or authorize claims.
  • After final resolution, anyone may relay winning inherited proofs; the authenticated depositor is paid. Inherited losers need no proof calls, while local losing deposits retain ordinary settlement.
  • Parent-lock cleanup is optional and vault-scoped; the operator cleanup reference owns wrapper ordering and constant-size export details.
  • Vault owners claim winning own-fork escalation deposits through claimForkedEscalationDeposits; the own-fork path is open only during the parent migration window. A successful direct claim invalidates that stable deposit identity in every current and future child continuation.
  • Parent collateral is partially transferred into child pools in proportion to migrated REP.
  • Shares migrate independently through ShareToken.migrate.

REP migration and share migration intentionally reproduce a source position into every selected child branch. A share source remains as a transfer-locked entitlement after first use, and each child balance is colored by a distinct universe, so this is not a same-universe double claim. Integrations must nevertheless keep branch balance sheets separate: summing child REP or shares as though all branches were simultaneously redeemable in one economic universe overstates capital.

The migration API is not a single generic “fork migrator” role. Child creation is permissionless, migrateVault always acts on the caller’s own vault, and the unresolved-escalation and own-fork-claim entry points require the affected vault as caller. This matters for off-chain automation because a keeper can help with liveness, but it cannot migrate or claim another vault’s locked REP.

Fork Migration Contract Trace

Step Function State changed Reason for the boundary
Activate fork mode initiateSecurityPoolFork Parent pool enters fork handling and snapshots auctionable REP. Normal pool accounting stops before child migration begins.
Create child branch createChildUniverse Deploys or locates the child universe and child pool. Child pools are lazy because not every branch may be used.
Migrate vault migrateVault Moves caller vault ownership, allowance, and proportional collateral. Only the vault owner can decide which child branch receives its position.
Repair shortfall startTruthAuction, finalizeTruthAuction Sells child REP for ETH and sets auction ownership rates. Repair is optional and bounded by actual child-pool collateral needs.
Resume child pool Forker-controlled pool setters Returns the child to operational mode after migration and auction settlement. The child accounts only routed collateral and accepted bid ETH; donations are rejected.
Fork migration responsibilities After a Zoltar fork, Statoblast migrates vaults, collateral, and shares. Child creation separately reproduces unresolved escalation state and aggregate backing once. Parent Pool fork mode migrateVault ownership and allowance Escalation Carry snapshot + backing once Share Migration ERC-1155 token ids Child Pool selected universe

Pool Migration FlowThe Zoltar fork and Statoblast pool migration are related, but they are not the same contract step.

Migration Proxy Identity

Statoblast does not send every pool migration call to Zoltar directly from the forker. Instead, SecurityPoolForker lazily deploys one deterministic SecurityPoolMigrationProxy per parent pool. That proxy is the stable msg.sender for Zoltar's migration ledger, so one parent pool has one predictable migration identity across lockRep, forkUniverse, splitToChild, and child REP sweeping.

The address is predictable, but prefunding it does not create pool migration credit. Fork accounting measures REP newly routed through the proxy and Zoltar’s migration ledger; any ERC-20 balance already at the address remains isolated surplus and is not swept into a child on the pool’s behalf.

Pool-specific migration proxy The forker controls one deterministic proxy per parent pool, and the proxy is the caller recorded by Zoltar migration accounting. SecurityPoolForker owner and coordinator Migration Proxy one CREATE2 address per parent pool Zoltar migration ledger ledger key = proxy address, not the vault address

Migration ProxyThe proxy is a thin adapter, but it is economically important because Zoltar accounts migration balances by caller.

Every selected continuation receives the complete parent game snapshot. It also receives the applicable aggregate backing once: complete game REP for an unrelated fork, or post-haircut game REP when the escalation paid for its own fork. A winning carry proof authorizes its own claim and pays the authenticated depositor. The child resumes when its pool becomes operational; it does not wait for vault migration or cleanup calls.

During vault migration, the forker also ensures the child pool is backed by enough child-universe REP for the cumulative migrated REP credited to that child. It tracks how much REP has already been split for each parent-pool/outcome pair and only splits the shortfall before sweeping child REP into the child pool. The migration-proxy and child-backing guardrails are collected in Fork Migration .

Proportional migration accounting Parent pool ownership determines migrated REP, and migrated REP determines the child collateral transferred from the parent collateral baseline. Parent Ownership vault's denominator share Migrated REP parent REP at fork share Child Collateral same REP fraction of ETH Parent REP at fork normalization anchor Parent Collateral transferred by REP share

Proportional MigrationVault migration first converts parent pool ownership into migrated REP, then transfers parent collateral in the same REP-at-fork proportion.

migrationRepDenominatorAtFork = ownFork ? vaultRepAtFork : auctionableRepAtFork migratedRep = floor ( parentPoolOwnership migrationRepDenominatorAtFork parentPoolOwnershipDenominator )

Fork Migration ProportionUnlocked vault migration floors the parent ownership share against the REP bucket that backs collateral for that fork type.

The equation above gives the ownership proportion. Both external and own-fork collateral transfers use one fixed, fee-exclusive fork snapshot plus cumulative ceiling accounting, so repeated vault migrations neither geometrically shrink later transfers nor strand collateral through individual rounding. An external fork is already known when activateForkMode checkpoints and records that snapshot. The own-fork snapshot uses this order: activateForkMode changes the pool state and checkpoints through the pre-fork fee horizon; the proxy creates the universe fork; a fork-time checkpoint covers the newly exposed question-end-to-fork interval at the retained operational rate; then collateralAtFork records the resulting fee-exclusive collateral in the fork-neutral snapshot shared by both paths. Truth-auction repair subtracts the child's actual cumulative routed collateral from that snapshot to set the missing collateral as its ETH raise target; actual repair still depends on auction demand, clearing, and tick-level integer rounding. Before either migration path clears the parent allowance, it also checkpoints the vault's earned fees. Each ETH transfer subtracts from parent complete-set collateral and is limited to the pool balance above totalAccruedFees(), keeping both the unallocated reserve and assigned vault debt funded in the parent.

cumulativeCollateralTargetAfterMigration = ceil ( parentCollateralAtFork cumulativeRepTransferredAfterMigration migrationRepDenominatorAtFork ) requestedCollateralDelta = cumulativeCollateralTargetAfterMigration - cumulativeCollateralAlreadyTransferred actualCollateralDelta =min(requestedCollateralDelta,parentCompleteSetCollateral)

Fork Collateral CeilingThe forker recomputes the cumulative collateral target after the migration, subtracts cumulative collateral already transferred, and sends only the portion that does not back accrued parent fees.

Own-fork migration has one extra accounting split. The pool and its active escalation game both contribute REP to the fork, but the child-universe REP returning from Zoltar is a single auctionable migration balance. Statoblast splits that balance into a vault bucket and an escalation bucket before child migration resumes.

Own-fork REP bucket split Pool REP and unresolved escalation REP enter one Zoltar fork balance. Ordinary pool REP remains in the vault bucket at 1:1, while the fork haircut is deducted from escalation aggregate backing. Pool REP vault ownership source Escalation REP unresolved locks Zoltar Fork Balance auctionable child REP Vault Bucket migrate vaults Escalation Backing game REP minus haircut

Own-Fork REP BucketsIn an own fork, the drained escalation-game REP pays the fork haircut and the remainder becomes aggregate child backing. Ordinary pool REP forms the vault bucket without a haircut.

forkHaircut = floor(forkThresholdforkBurnDivisor) escalationChildRepAtFork = escalationRepToFork - forkHaircut vaultRepAtFork = auctionableRepAtFork - escalationChildRepAtFork selectedChildEscalationBacking = escalationChildRepAtFork

Own-Fork Bucket AllocationThe escalation bucket pays the configured fork haircut exactly once. The vault bucket preserves ordinary pool REP 1:1, and each selected child receives the same post-haircut aggregate escalation backing once.

When a pool forks while its escalation game is unresolved, the parent forker snapshots the unresolved game’s startBond, nonDecisionThreshold, elapsed escalation time, actual per-outcome balances, carry totals, peaks, leaf counts, and nullifier roots. External unrelated forks reserve the drained game's complete REP at 1:1. The escalation's own fork reserves it after the winner haircut has been paid through Zoltar. Each selected child pool can initialize its own paused fork-continuation escalation game from that canonical snapshot and receives the complete aggregate game backing once. The continuation preserves the parent live Invalid, Yes, and No balances, each unresolved deposit’s original outcome, stable parent deposit index, and payout ordering. The authenticated winning proof is the claim authorization; it pays the recorded depositor directly from aggregate backing without a preceding vault migration.

The continuation game remains paused during the migration window so child deployment and vault migration do not silently consume escalation time. While the child pool is waiting for continuation processing, SecurityPool.depositToEscalationGame rejects new live child deposits through the awaitingForkContinuation guard. Operational activation clears that guard without waiting for parent-vault transactions. An inactive vault therefore cannot make other users pay to process its position or delay the child continuation.

  1. The parent pool forks, stores canonical unresolved escalation state, and locks the game's aggregate physical REP once.
  2. Each selected child pool is created in a child universe and marks itself as awaiting fork continuation.
  3. Each created child initializes a paused continuation escalation game from that snapshot and receives the full aggregate escalation backing once.
  4. After the migration window and any required truth auction, the child pool becomes operational, clears the wait marker, and resumes the paused continuation game.

A continuation pool can fork again only when it has no inherited fixed outcome. Within that pool-level boundary, its game can become eligible by inheriting a threshold tie at snapshot initialization or by recording a local non-decision after resuming. The game-local canTriggerOwnFork() predicate returns true for a local non-decision, but it does not bypass the pool's fixed-outcome guard. When both levels are eligible, the forker stores the game's current balances, carry totals, peaks, leaf counts, and current nullifier roots after any prior proof consumption. It drains and locks the remaining aggregate REP once. Each selected grandchild receives that same canonical snapshot and full remaining backing once; winning proofs remain their own authorization. Escalation can therefore branch recursively with Zoltar universes without replaying spent proofs or requiring other vaults to migrate. Before consuming a carried proof, the forker derives a stable global deposit id as keccak256(abi.encode(securityPoolFactory, originId, uint8(outcomeIndex), parentDepositIndex)). A direct claim recorded anywhere in that factory-scoped origin lineage therefore blocks the corresponding descendant proof with one mapping lookup.

Recursive continuation adds another descendant branch to the migration lifecycle described here. The normative recursive-fork gas-bound requirement, current enforcement status, and evidence are maintained in EXT-05. Total historical data and offchain proof coordination can still grow as more forks occur.

For example, suppose Zoltar has two ended questions: “Did London record a temperature of at least 30 °C on August 1, 2030?” and “Did the Atlas protocol upgrade activate by July 1, 2031?” A security pool in the Genesis universe is still escalating the London-temperature question. Before that escalation resolves, a caller invokes Zoltar.forkUniverse with the unrelated, already ended Atlas-upgrade question, causing Zoltar to fork the Genesis universe. Each resulting child universe represents one possible answer to the Atlas-upgrade question. The pool's unresolved London-temperature escalation migrates into selected child universes as separate paused continuation games, one in each selected child universe. After a continuation resumes, it still disputes the London-temperature question. Because the unrelated fork did not give the continuation pool a fixed outcome, an inherited threshold tie or a later local non-decision lets a caller invoke the security-pool fork path, causing Zoltar to fork that child universe again. The first fork therefore creates branches for the possible answers to the unrelated Atlas-upgrade question; the recursive fork creates branches for the possible answers to the pool's original London-temperature question.

Recursive fork-continuation flow A caller invokes Zoltar to fork a universe while one of its security pools has an unresolved escalation. Later, migration into selected child branches deploys child pools and initializes paused continuation games. Canonical snapshot state supplies each selected child's proof baseline, aggregate REP supplies physical backing once, and authenticated winning proofs pay their recorded depositors. Only a continuation pool without a fixed outcome may fork again; within that boundary, a local non-decision or inherited threshold tie makes its game eligible for the security-pool fork path. Parent Escalation unresolved across outcomes Zoltar Fork selected branches created lazily Invalid Child paused continuation game original deposit outcomes preserved Yes Child paused continuation game migration window, then resume No Child paused continuation game same inherited carry baseline Continuation Can Fork Again pool has no fixed outcome local or inherited game trigger

Recursive ContinuationEach child continuation game receives the same canonical proof snapshot and aggregate backing. Winning proofs remain claimable without vault migration. Only a child pool without a fixed outcome can export the carry into another fork generation; within that boundary, a local non-decision or inherited threshold tie makes its game eligible.

Separate recursive continuation accounting lanes Current balances, carry, and nullifiers form the canonical next snapshot. Remaining aggregate REP funds child backing once. Winning proofs authenticate their recorded depositors, while inherited losers retire without proofs. Current Game State balances + carry + nullifiers Canonical Snapshot reused by every selected child Remaining Aggregate REP locked once at the fork Physical Child Backing complete backing per selected child Final Winning Outcome inherited losers retire Winning Carry Proof relayed permissionlessly Authenticated Payout to recorded depositor

Recursive AccountingThe next canonical snapshot and aggregate backing move once per selected child. At final resolution, a winning proof authenticates and pays its depositor; inherited losing principal retires without proof transactions.

The inherited-carry baseline is implemented with Merkle Mountain Range peaks and nullifier roots so a child game can consume proofs without replaying already-spent parent deposits. New child deposits are tracked separately as local carry. Proof settlement is per requested winning leaf, not a depositor-wide loop. Immutable peaks and leaves remain the proof commitment even if separate parent cleanup occurs.

A carried proof's source principal authenticates the leaf and decrements inherited unresolved principal. Normal winning-payout math then pays from the aggregate child backing, so a reward may exceed the proof principal without per-vault scaling. Direct parent claims reduce effective inherited principal by outcome and remain blocked from a second payout in every descendant. Once the final winner's proofs are consumed, inherited losers have retired, local deposits are settled, and no local escrow remains, residual REP can be swept back to the security pool.

Recursive Continuation Example

Step Example state What carries forward
Parent fork In this external-fork example, source REP converts to child REP one-for-one. A parent game has 12 REP unresolved on Yes and 8 REP unresolved on No. The fork snapshot preserves both original outcomes and parent deposit indexes; the one-for-one conversion makes the child backing 20 REP.
Child continuation The preferred child receives the complete 20 REP aggregate backing once and resumes with the inherited carry. Winning inherited proofs authorize themselves and pay their recorded depositors; new deposits are tracked as local child state.
New local activity After resume, users add 5 REP to Invalid and 7 REP to Yes. The next snapshot combines inherited carry, local carry, and spent-proof nullifiers.
Second fork This unrelated continuation has no fixed outcome and did not inherit a threshold tie. When new activity records a local non-decision, canTriggerOwnFork() becomes true. The same predicate also accepts an inherited threshold tie without a fixed outcome. The forker stores the current carry state and nullifier roots and locks the remaining aggregate REP; each selected grandchild receives the full remaining backing once; winning proofs need no vault migration there.

Child Outcome Resolution

Child pools resolve outcomes through the forker. When the parent universe forks on the pool's question, each child stores its selected Zoltar outcome index as the fixed result, whether the fork began through the pool-specific path or a direct Zoltar call. The pool stores and reports that result from child creation, and it is final for that pool. Pool asset redemptions begin after the child becomes operational. When a continuation game exists, it uses the same result for carried-deposit settlement only after its remaining continuation deadline. Later universe forks cannot transition a pool with an inherited fixed result, even when they reuse the pool question. A child created by an unrelated fork without an inherited fixed result uses a local escalation result only if that escalation ended before the universe forked; otherwise it remains unresolved until continuation or later state produces an outcome. The ownFork flag changes accounting, not question semantics.

This finality is also a collateral-accounting boundary. Winning-share redemption burns only the fixed winning token and reduces the pool's remaining economic claim supply. A later fork must not reclassify a surviving sibling outcome token as winning against that reduced denominator.

The same boundary closes new local escalation deposits. A fixed-outcome pool cannot use another fork to export a local non-decision, while local settlement rejects deposits after non-decision. Accepting new REP in that state would therefore lock the depositor's vault ownership and block REP redemption. Carried winning proofs from the parent continuation remain claimable through their separate withdrawal path.

Whole-system decision flow Question, pool, escalation, fork, child migration, auction repair, fixed-outcome settlement, and unresolved recursive continuation flow. Question Zoltar registration Security Pool REP vaults + ETH shares Escalation Game Invalid, Yes, No balances Decision Point local winner or non-decision Local Settlement redeem winning shares Fork + Migration child pools per outcome Truth Auction if collateral is short Operational Child settle; unresolved may continue unresolved recursive continuation

System Decision FlowThe main path is simple, but the unresolved continuation loop matters: a child without a fixed outcome can become the parent of the next fork generation. A fixed-outcome child settles without re-entering the loop.

ethCollateralToBuy = max ( 0 , parentCollateralAtFork - forkCollateralReceived )

Auction ShortfallThe truth auction targets the exact parent snapshot not already received by the child. forkCollateralReceived sums actual routed transfers after cumulative-ceiling calculation and the parent’s available-collateral cap; nominal migrated REP is not substituted.

Adjust child collateral repair

In this example, the parent obligations still require 50 ETH of collateral and the child actually received 47.5 ETH through routed, capped migration transfers. That leaves an exact 2.5 ETH shortfall before any repair auction raises more ETH. After the deadline, the child activates with the collateral actually migrated and raised; finalizer contribution ETH is rejected. Each child branch is sized against its own received collateral and remaining shortfall.

0 ETH target 50 ETH
Routed collateral received47.50 ETH
Initial shortfall2.50 ETH
Unfilled target after auction0.00 ETH
Activation behaviorno contribution required

8. Truth Auction

#

After the parent migration window ends, a child pool may still hold less ETH than users need for clean redemption. The truth auction sells child-universe REP for ETH to repair that gap, subject to both an ETH raise cap and a maximum REP sale cap.

Collateral repair role The auction turns child-universe REP into the ETH needed to repair child collateral.
Contract behavior Bids clear against both an ETH target and a maximum REP inventory.
Edge cases If demand is weak, bids at or above the cap-implied qualification threshold buy the complete REP sale cap at one effective price. Lower bids refund. Value-free finalization activates the child with the collateral actually migrated and raised.

The repair target is the contract’s direct statement of the collateral gap: parentCollateralAtFork minus forkCollateralReceived, floored at zero. The received counter records actual cumulative-ceiling transfers after the parent’s available-collateral cap, so nominal migrated REP cannot overstate child funding. The auction start gate is tied to the parent pool’s fork timestamp plus the eight-week migration window, not to any child-universe fork time. If migrated REP has reached the pool-auctionable baseline, or if that baseline is zero, the child pool can finalize this phase without running the division path or selling REP. For an own fork that baseline is vaultRepAtFork; for an external fork it is auctionableRepAtFork.

The REP sale cap is deliberately not just “all remaining REP.” The forker computes the auctionable pool REP baseline, subtracts a tiny floor-rounded haircut tied to the REP that already migrated, and gives the auction that maximum inventory. For an own fork, poolAuctionableRepAtFork means only the vault REP bucket, so the separate own-fork escalation REP bucket is not sold in the truth auction. For an external fork, it means the parent pool’s full auctionable REP at fork.

migratedRepHaircut = migratedRep MAX_AUCTION_VAULT_HAIRCUT_DIVISOR maxRepBeingSold = 0 if migratedRepHaircut >= poolAuctionableRepAtFork poolAuctionableRepAtFork - migratedRepHaircut otherwise

Auction REP Sale CapThe truth auction sells at most the auctionable REP baseline minus a small migrated-REP haircut, with Solidity integer division applying the floor rounding.

Truth auction repair balance sheet A child pool before auction, after full auction repair, and after weak auction demand settles at the collateral actually raised. Before Auction proportional ETH collateral 190 REP migrated ETH shortfall remains redemptions impaired sell REP Successful Repair 50 ETH collateral less child REP full repair target met cleaner redemption base Weak Demand Settlement auction ETH below cap no caller donation activates at raised collateral

Truth Auction Balance SheetThe auction repairs a child pool by trading part of that child universe’s scarce REP for ETH. The child becomes operational after migration collateral and accepted auction proceeds are accounted, even when weak demand leaves the original target partly unfilled.

The forker tracks collateral received through migration and the auction separately from the child contract's raw ETH balance. ETH forced into the child does not count toward the repair target. After the deadline, value-free finalization activates the child with migrated collateral plus accepted auction ETH and releases every bid to settlement. Nonzero finalizer ETH is rejected.

Clearing and Settlement

The Truth Auction design owns tick pricing, cap selection, underfunded qualification, allocation rounding, settlement modes, and the interactive examples. The security model separately defines the intentional weak-demand loss allocation.

9. REP/ETH Price Oracle

#

The REP/ETH oracle is narrow in purpose. OpenOraclePriceCoordinator uses OpenOracle to obtain a fresh REP/ETH price for liquidation, REP withdrawal, and bond-allowance updates. A stale price also blocks depositToEscalationGame whenever the pool still has active security-bond allowance. It is not the oracle for question truth; that path is handled by escalation and, if necessary, Zoltar forks.

The OpenOracle path is contestable before the coordinator can use it: the sponsor funds the request, the coordinator atomically posts the initial token amounts and becomes the onchain reporter, disputers can replace a bad report with the contract-required replacement amounts strictly before the settlement deadline, and a settler can finalize the report at or after that deadline. OpenOracle credits the final position to the final reporter. Its callback supplies the accepted amounts to the coordinator, which withdraws only its own internal WETH/REP balances back to the sponsor. Request, dispute, settlement, and callback safety checks are detailed in the dedicated OpenOracle integration guide.

If the last price is stale, solvency-sensitive operations can be queued until a valid report is settled. The coordinator then replays the pending operation against the fresh price. Escalation deposits are the exception: they require a fresh price when bond allowance is outstanding, but they revert instead of entering the staged callback queue. This price path is only for security backing against ETH collateral.

ORACLE-A1 — always-available profitable arbitrager. Security depends on profitable corrections receiving timely inclusion; report liquidity does not collateralize downstream operation value. The normative security model owns the full assumption, excluded guarantee, and residual loss boundary.

Queued REP/ETH oracle operation flow A solvency-sensitive operation either executes immediately with a valid cached price or stages behind a pending OpenOracle report. Operation Request withdraw, allowance, liquidation Valid Price? 5-minute freshness window Execute Now no oracle ETH cost Stage Operation one callback replays up to 4 OpenOracle coordinator reports sponsor funds dispute or settle Consume or Replay expired, stale, or failed checks or already executed

REP/ETH Oracle FlowThe coordinator is a queue plus cache. A fresh price can execute immediately; a stale price moves operations into a pending-settlement path whose callback batch is capped at four operations but whose duration is unbounded under paid rolling disputes.

Staging and Recovery

The OpenOracle integration owns request funding, staged-operation ordering, settlement callbacks, rejected-price handling, recovery, report sizing, and current parameters. The application-level distinction is that a fresh price executes an eligible operation immediately, while a stale price routes it through that contestable path.

10. Assumptions and Security Model

#

The Protocol Security Model is normative for ORACLE-A1, AUCTION-A1, their excluded guarantees, and the resulting loss allocation. The summary below provides the broader economic context.

  • REP backing must remain economically meaningful relative to the obligations it secures.
  • Users accept that fork recovery can sell child REP to repair missing ETH collateral.
  • Usable value depends on users and capital coordinating on the branches they continue to support.

Zoltar forks only create child universes, and Statoblast carries underwriting state, collateral state, and outcome shares into them. If users and bidders coordinate on the child universe they expect to keep using, REP migration, proportional collateral migration, and truth-auction repair can rebuild usable market state there.

11. Parameter Sources

#

Current values live with the mechanism that interprets them. This avoids a second catalog drifting away from the focused design pages or deployment configuration.

Parameter familyCanonical source
Universe fork thresholds and haircutsZoltar fork economics
Escalation timing, thresholds, and rewardsEscalation Resolution
Migration and truth-auction timing and boundsTruth Auction lifecycle
Liquidation constants and solvency limitsLiquidation penalty math
REP/ETH coordinator and OpenOracle valuesOpenOracle parameters
Carry-proof peak and nullifier boundsMerkle Mountain Range bounds

12. Implementation Constraints

#
  • Origin security pools currently support the exact categorical market shape Yes / No, with Invalid added as the third trading and resolution outcome.
  • Statoblast issues transferable shares and manages redemption and migration, but does not provide secondary-market trading.
  • Zoltar fork economics are constructor-configured; the Zoltar whitepaper owns the current divisors, rounding, and migration-credit effects.
  • Retention-rate bounds and several oracle and auction parameters are fixed constants in the current implementation.
  • The retention-rate curve in SecurityPoolUtils is heuristic rather than final market design.
  • Some comments in SecurityPool acknowledge open accounting questions around child-pool complete-set behavior.