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.
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.
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.
- A question is registered in Zoltar and a Statoblast security pool is deployed for that question in one universe.
- REP vaults fund underwriting capacity and users mint ETH-backed complete sets.
- After question end, disputes first try to resolve locally through the escalation game.
- If local resolution fails and the system reaches non-decision, Zoltar forks and Statoblast migrates pool state into child universes.
- 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.
- 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 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
#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, orNoafter 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.
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.
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.
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.
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.
Bond SolvencyVault and pool bond checks require remaining REP value to cover the ETH-denominated security-bond allowance.
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.
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
redeemCompleteSetto 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
cashToSharesto at least one complete-set unit. If the current exchange rate would round the mint to zero shares,createCompleteSetreverts withExchange rate undefinedinstead 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.
-
completeSetCollateralAmountstarts higher and decays gradually. -
totalFeesOwedToVaultsgrows only when a vault checkpoints whole-wei fees that it can redeem; accrued but not yet checkpointed fees remain in a separate pool reserve. feeIndextracks vault fee accounting.-
currentRetentionRatechanges as utilization changes.
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 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 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.
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.
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.
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.
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 AccountingREP ownership and fee accounting use separate proportional ledgers: ownership tracks REP claims, while the fee index tracks decayed ETH collateral owed to vaults.
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 .
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.
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.
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).
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.
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.
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.
Payout RegionsWinning deposits below binding capital and in the safety boundary share rewards, while excess receives principal only.
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 remaining60%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.
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.
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.
| 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.
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
createChildUniverseto deploy a child pool for a valid fork outcome. -
Vault owners can call
migrateVaultto 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. |
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.
Migration ProxyThe proxy is a thin adapter, but it is economically important because Zoltar accounts migration balances by caller.
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 MigrationVault migration first converts parent pool ownership into migrated REP, then transfers parent collateral in the same REP-at-fork proportion.
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.
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 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.
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.
- The parent pool forks, stores canonical unresolved escalation state, and locks the game's aggregate physical REP once.
- Each selected child pool is created in a child universe and marks itself as awaiting fork continuation.
- Each created child initializes a paused continuation escalation game from that snapshot and receives the full aggregate escalation backing once.
- 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 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.
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.
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.
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.
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.
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.
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 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.
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 family | Canonical source |
|---|---|
| Universe fork thresholds and haircuts | Zoltar fork economics |
| Escalation timing, thresholds, and rewards | Escalation Resolution |
| Migration and truth-auction timing and bounds | Truth Auction lifecycle |
| Liquidation constants and solvency limits | Liquidation penalty math |
| REP/ETH coordinator and OpenOracle values | OpenOracle parameters |
| Carry-proof peak and nullifier bounds | Merkle Mountain Range bounds |
12. Implementation Constraints
#-
Origin security pools currently support the exact categorical
market shape
Yes / No, withInvalidadded 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
SecurityPoolUtilsis heuristic rather than final market design. -
Some comments in
SecurityPoolacknowledge open accounting questions around child-pool complete-set behavior.