Augur / Zoltar Paired whitepaper set
Companion paper Branching Oracle Layer

Prediction Market Layer

Protocol draft • Application layer built on Zoltar

Augur Placeholder 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 Placeholder is the market layer built on top of Zoltar. It creates one security pool per question, 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, Placeholder 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 Placeholder is the application layer that adds market collateral, underwriting, local disputes, migration, and collateral repair. For the base oracle layer, see whitepaper_zoltar.html.

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

Placeholder 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 bounded REP/ETH solvency price for withdrawals, allowance updates, and liquidations.

  1. A question is registered in Zoltar and a Placeholder 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 Placeholder 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.
Placeholder lifecycle from question to child pool Operational pool moves through escalation, possible Zoltar fork, migration, optional truth auction, and child operation. Operational parent pool Question Ends escalation opens Escalation local dispute game Local Finalization unique winner before fork Zoltar Fork non-decision path Pool Migration vaults, collateral, shares Truth Auction only if collateral is short Child Pool operational again

Lifecycle FlowPlaceholder tries to settle locally first. A fork is the recovery path when local escalation reaches non-decision.

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, collateral accounting, and eligible escalation positions move into child pools.
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.

Placeholder 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.
Zoltar and Placeholder stack Zoltar supplies universes and forks; Placeholder adds pools, shares, escalation, migration, and truth auction. Zoltar universes, questions, forks REP branching Augur Placeholder security pools and shares local escalation migration and truth auction

Layer BoundaryThe two layers are related but separate: Zoltar forks universes; Placeholder moves market state across those universes.

3. Core Economic Roles

#
  • Question creators register questions in Zoltar question data, creating the objects around which Placeholder 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, eligible escalation positions, and shares across child universes after a Zoltar fork; some settlement helpers are permissionless.
  • 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 the central Placeholder contract for one question in one universe. Vault operators deposit REP, choose bond allowance, and underwrite ETH-backed complete sets.

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 Placeholder 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; Placeholder's factory decides which question shape is eligible for the current security-pool implementation.

Pool-related addresses are also deterministic. Origin-pool deployment salts include the zero parent address, universe id, question id, and securityMultiplier; child-pool salts include the parent pool address as well. The share token uses a separate salt based on securityMultiplier and question id so the share-token address stays stable across forked child pools for the same market shape.

Security pool economics REP deposits create ownership and bond allowance, which gates open interest and solvency-sensitive actions. Vault Operator deposits REP SecurityPool pooled REP ownership Allowance open-interest capacity Solvency Checks withdraw, allowance, liquidation

Security Pool EconomicsBond allowance is not free capacity; it remains constrained by REP backing, REP/ETH price, and pool-level solvency checks.

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 REP/WETH 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.
  • 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.

Fee Extraction and Retention Rate

Complete-set collateral gradually decays into fees owed to vaults. 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 starts lower and grows over time.
  • feeIndex tracks vault fee accounting.
  • currentRetentionRate changes as utilization changes.
Collateral retention and fee flow Complete-set collateral decays over time into vault fees as utilization changes retention rate. Complete-Set Collateral starts higher, decays over time retention shrinks Vault Fees Owed grows as collateral decays higher utilization lowers retention and accelerates fee extraction

Fee FlowComplete-set collateral decays into vault fees as utilization changes the retention rate.

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 the 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.

collateralAtLaterTime = collateralAtCurrentTime retentionRate elapsedTime

Retention DecayComplete-set collateral decays exponentially into vault fees over elapsed time.

feeIndexNext = feeIndex + collateralDecay pricePrecision totalSecurityBondAllowance vaultFees = securityBondAllowance ( feeIndex - vaultFeeIndex ) pricePrecision

Fee Index AccrualCollateral decay becomes a global fee-index increment, then each vault claims the increment weighted by its security-bond allowance.

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.

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

REP withdrawal, liquidation, and bond-allowance updates depend on a valid REP/ETH price. Withdrawal and allowance changes check backing against allowance, while liquidation applies the stronger securityMultiplier-adjusted liquidability condition.

Liquidation is a punitive vault-to-vault transfer: debt moves to the caller vault, and unlocked target REP moves to that caller at the liquidation bonus. When a vault becomes undercollateralized, another vault can call performLiquidation through the queued-oracle path. The contract snapshots the target vault’s state at queue time and uses that snapshot when the operation executes, so later target-vault manipulation cannot trivially invalidate the liquidation attempt. The queued liquidation executes only if the fresh current REP/ETH price is strictly above the computed threshold and far enough beyond it to satisfy the configured minLiquidationPriceDistanceBps liquidation distance check.

Economically, liquidation is punitive: the liquidator vault absorbs target debt and receives unlocked target REP at a fixed five-percent bonus over the debt chunk’s REP market value. That punishes the target and rewards the liquidator, while the securityMultiplier still makes the target healthier as long as required backing falls faster than seized REP.

The purpose is therefore threefold: punish the liquidated vault, reward the liquidator, and move the system into a healthier state. The first two effects are direct REP transfers; the third comes from the strict health-improvement guard that rejects chunks whose rounding would fail to reduce the target shortfall. Healthier means the target shortfall shrinks and the moved debt lands only on a caller vault that remains non-liquidatable under the securityMultiplier-adjusted collateral threshold; liquidation redistributes REP and allowance rather than increasing aggregate backing.

Snapshot sizing, REP-seizure math, the strict health-improvement guard debtToMove * securityMultiplier * currentRepPerEthPrice > repToMove * PRICE_PRECISION, target/caller dust floors, and repeated-liquidation edge cases define the liquidation execution envelope; Liquidation Design derives that flow in full.

Punitive liquidation transfer Liquidation moves debt to the caller vault and moves unlocked REP from the target vault to the caller at a fixed liquidation bonus, subject to target and caller minimum-balance constraints. Target Vault unsafe allowance loses unlocked REP debtToMove + repToMove snapshot sizing + floor checks Caller Vault takes debt earns seized REP REP moves with a fixed 5% liquidation bonus full rules and worked examples live in Liquidation Design

Liquidation TransferLiquidation moves snapshot-sized debt to the caller vault and seizes unlocked target REP at the configured liquidation bonus.

repToMove = ceil ( debtToMovecurrentPrice(BPS_DENOMINATOR+liquidationRepBonusBps) PRICE_PRECISIONBPS_DENOMINATOR )

Liquidation TransferThe caller receives REP priced from the executed debt chunk plus the fixed liquidation bonus.

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 before either redemption path is exercised.

Shares can migrate across forks. ShareToken.migrate burns a parent-universe token id and mints child-universe token ids for selected valid, non-malformed fork outcomes.

Share migration is full-balance reproduction A full parent token balance is burned and reproduced into each selected child token id. Parent Token Id full holder balance migrate() burns source checks sorted targets Child Token A same full balance Child Token B same full balance

Share MigrationShare migration is not a pro-rata split across selected branches. The source balance is burned once and reproduced in each valid selected child token id.

Adjust a share migration reproduction example

The migration call burns the full parent token balance and reproduces that same balance into each selected child outcome. 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, which shows why migration is not a pro-rata split.

Parent burned 30 30 30 0
Parent balance after call0 shares
Child 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 entire parent token balance is burned; callers cannot migrate only part of that token id.
  • 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 balance of the winning token id and pays sharesToCash(amount) out of the pool’s current collateral accounting. 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 ( completeSetAmount completeSetCollateralAmount shareTokenSupply )

Share RedemptionsharesToCash pays a pro-rata share of the pool's current collateral accounting, with Solidity integer division flooring 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 full burned balance is reproduced into each selected child universe.

6. Escalation Resolution

#

Placeholder 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 Placeholder 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 reject preserved positive tied maxima below nonDecisionThreshold. Ties already at nonDecisionThreshold, or all-zero preserved balances, remain valid carried states.

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 timing Question end, first escalation deposit, three-day activation delay, seven-week cost curve, and final local or fork result. Question End deposits open First Deposit game starts 3 days activation delay Cost Accrues startBond upward 7 weeks attrition curve Outcome local finality or fork

Escalation TimingThe clock is lazy: the first accepted escalation deposit starts the game, then cost stays at zero until the three-day activation delay has passed.

Escalation thresholds Outcome balances are compared to running total cost for local resolution and fixed non-decision threshold for fork path. 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 Fork path opens

Non-Decision FlowA live unresolved contest is not the same as the stronger non-decision condition that opens the fork path.

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 the stronger non-decision condition, the local resolution helper first checks whether two or more outcomes still meet the running cost. If not and all three balances are zero, it falls back to Invalid. Otherwise, a strict leading outcome 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 reject preserved positive tied maxima below nonDecisionThreshold. Ties already at the fixed nonDecisionThreshold, or all-zero preserved balances, remain valid carried states. A local deposit that brings a second outcome to nonDecisionThreshold records nonDecisionTimestamp and opens the fork path. A fork continuation can instead inherit a threshold tie without rebasing balances or recording a new timestamp during snapshot initialization.

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 amount is burned or retained by the game accounting; 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 burned or retained, 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 games, own-fork claims, external-fork migration, and child continuation games. The distinction matters because some paths settle local deposit indexes, while continuation games settle inherited parent deposits by proof.

Escalation Settlement Call Matrix

Entry point Used when Claim unit
withdrawFromEscalationGame The original pool's local game finalized before a fork interrupted it. 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 converted child REP to the vault wallet.
migrateVaultWithUnresolvedEscalation An external fork interrupted unresolved local locks before the game finalized. One vault's unresolved locks, possibly across multiple calls.
withdrawForkedEscalationDeposits A child continuation game finalized and inherited deposits must be settled. Carried-deposit proofs for one beneficiary vault per call.

Local withdrawals adjust the vault's parent-pool ownership by the gain or loss relative to the original locked REP. Own-fork claims instead convert source REP into child-universe REP and pay it to the vault owner’s wallet, rather than minting child-pool ownership. In the own-fork path, those 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 migrateVaultWithUnresolvedEscalation require msg.sender to be the affected vault.

Continuation withdrawals use Merkle Mountain Range proofs against the inherited carry snapshot. Each accepted proof advances the nullifier root, so a carried parent deposit cannot be replayed in the child.

If an unrelated external Zoltar fork interrupts the local escalation game before it resolves, unresolved escalation deposits stop being withdrawable on the parent pool. The vault owner must migrate all remaining unresolved parent locks for the chosen child universe through repeated migrateVaultWithUnresolvedEscalation calls until the function reports no more work. Each call pages only a bounded chunk of unresolved export data, so large carry sets can require multiple transactions. If the vault does not migrate in time, the unresolved position remains locked in the parent escalation game and is no longer withdrawable from the parent pool.

Unresolved escalation migration External fork interruption moves unresolved parent escalation locks through paged export into a child continuation game. Parent Game unresolved locks Paged Export up to 64 refs Child Carry MMR snapshot Resume child game

Unresolved MigrationUnresolved parent locks are exported in bounded pages into child carry so the continuation game can settle them later with proofs.

Unresolved Escalation Migration Trace

Step Function or contract area State or proof material Why it matters
Snapshot interruption SecurityPoolForker Records elapsed escalation time and unresolved outcome state. The child continuation starts from the interrupted parent clock.
Export parent refs migrateVaultWithUnresolvedEscalation Exports bounded batches of unresolved deposit references. Large vault histories may require several transactions.
Initialize child carry SecurityPool.initializeForkCarrySnapshotWithResolutionBalances, which forwards to EscalationGame.initializeForkCarrySnapshotWithResolutionBalances. Materializes inherited carry peaks and totals. Child games can verify parent deposits without copying every leaf immediately.
Withdraw after child resolution withdrawForkedEscalationDeposits Consumes proofs and advances nullifier roots. Proof consumption prevents replaying the same inherited deposit.

7. Forks and Migration

#

Fork handling spans both layers. Zoltar creates child universes and handles REP splitting. Placeholder separately moves vault state, eligible escalation-game positions, proportional collateral, and share positions into child pools.

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

Fork branch role A Zoltar fork creates branches; Placeholder decides what pool state follows each branch.
Contract behavior Parent pools halt while child pools collect migrated vaults, collateral, shares, and escalation state.
Edge cases Unresolved games become paused continuations and can fork again after they resume.
State Meaning Typical transition out
Operational Normal pool operation outside the explicit fork lifecycle. Local resolution or fork handling activation.
PoolForked Parent pool has stopped normal operation after fork. 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 finalizes and pool returns to operational mode.

SystemState is primarily a fork-and-migration state machine, not a generic question-resolution state machine.

SecurityPool fork state machine Contract states and transition functions from operational parent to operational child. Operational parent pool fork mode PoolForked parent halted create child ForkMigration child pool start auction Fork Truth Auction repair phase finalize Operational child no auction needed migration calls vaults, REP, unresolved escalation

Fork State MachineThe parent stops mutating once forked; child pools do migration, optional auction repair, and then return to operational mode.

  • The parent pool enters fork mode.
  • Anyone can call createChildUniverse to deploy a child pool for a valid fork outcome.
  • Vault owners call migrateVault to move their own pool ownership and bond allowance when they do not have unresolved external-fork escalation locks.
  • Vault owners with unresolved external-fork escalation locks call migrateVaultWithUnresolvedEscalation, which can require multiple calls before it returns that migration is complete.
  • Vault owners claim winning own-fork escalation deposits through claimForkedEscalationDeposits; the own-fork path is open only during the parent migration window.
  • Parent collateral is partially transferred into child pools in proportion to migrated REP.
  • Shares migrate independently through ShareToken.migrate.

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 Finalizes child accounting and returns to operational mode. Users should interact with a consistent child state after migration and repair.
Fork migration responsibilities After a Zoltar fork, Placeholder migrates vaults, collateral, escalation positions, and shares into child pools. Parent Pool fork mode migrateVault ownership and allowance Escalation Claims winning or unresolved paths Share Migration ERC-1155 token ids Child Pool selected universe

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

Migration Proxy Identity

Placeholder 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.

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.

Unresolved external-fork escalation positions migrate with the vault through migrateVaultWithUnresolvedEscalation. The child continuation game is paused during the migration window, then resumes once the child pool becomes operational.

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.

migratedRep = parentPoolOwnership parentRepAtFork parentPoolOwnershipDenominator collateralToTransfer = parentCollateral migratedRep parentRepAtFork

Fork Migration ProportionUnlocked vault migration preserves the parent ownership share, then transfers ETH collateral according to the migrated REP share of REP at fork.

The equation above is the external-fork unlocked transfer path. In an own-fork migration, child collateral uses cumulative ceiling accounting so repeated vault migrations do not strand collateral because each individual transfer rounded down.

cumulativeCollateralRequiredAfterMigration = ceil ( parentCollateralAtFork cumulativeRepTransferredAfterMigration vaultRepAtFork ) collateralDeltaToTransfer = cumulativeCollateralRequiredAfterMigration - cumulativeCollateralAlreadyTransferred

Own-Fork Collateral CeilingThe forker recomputes the cumulative collateral required after the migration, subtracts cumulative collateral already transferred, and sends only that newly required difference to the child pool.

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. Placeholder 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, then split into vault and escalation child-REP buckets. Pool REP vault ownership source Escalation REP unresolved locks Zoltar Fork Balance auctionable child REP Vault Bucket migrate vaults Escrow Bucket continue locks

Own-Fork REP BucketsIn an own fork, child REP is allocated between migrated vault ownership and unresolved escalation escrow according to the pre-burn source REP mix.

totalRepBeforeBurn = poolRepToFork + escalationRepToFork vaultRepAtFork = 0 if totalRepBeforeBurn = 0 poolRepToFork auctionableRepAtFork totalRepBeforeBurn otherwise escalationChildRepAtFork = auctionableRepAtFork - vaultRepAtFork previewEscalationChildRep = sourceRepAmount escalationChildRepAtFork escalationSourceRepAtFork escalationChildRepToTransfer = min ( previewEscalationChildRep , remainingOutcomeEscrowChildRep )

Own-Fork Bucket AllocationThe vault bucket follows the pool REP share of total source REP before the fork burn; unresolved escalation migration then converts source escrow through the escalation bucket’s child-REP ratio and caps each outcome by its remaining escrow bucket.

When a pool forks while its escalation game is unresolved, the parent forker snapshots the unresolved game’s startBond, nonDecisionThreshold, elapsed escalation time, and per-outcome carry state. Each relevant child pool can initialize its own paused fork-continuation escalation game from that snapshot. The continuation preserves the parent live Invalid, Yes, and No balances, each unresolved deposit’s original outcome, stable parent deposit index, payout ordering, and the remaining REP backing that still needs to arrive through forked escrow. Forked escrow then tracks how much child REP backing has arrived for those inherited unresolved deposits without rebasing the preserved live balances.

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.

  1. The parent pool forks and snapshots unresolved escalation state.
  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.
  4. Affected vaults migrate unresolved parent locks through migrateVaultWithUnresolvedEscalation; large carry sets can require multiple calls.
  5. 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 game can later reach non-decision too. When that happens, it exports a materialized carry snapshot made from inherited unresolved deposits plus new local deposits placed after resume. That snapshot can seed another set of child continuation games, so escalation does not end at a single fork; it can branch recursively along with Zoltar universes.

Recursive fork-continuation flow An unresolved escalation game can fork, initialize several child continuation games, and those child games can fork again. Parent Escalation unresolved across outcomes Zoltar Fork carry snapshot + forked escrow 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 new snapshot combines inherited carry + local carry

Recursive ContinuationEach child continuation game inherits the unresolved carry baseline. If a child game later reaches non-decision, the carry can be exported again into the next fork generation.

Continuation carry ingredients Inherited carry, local carry, and forked escrow combine into the next fork snapshot. Inherited Carry parent proof baseline Local Carry new child deposits Forked Escrow child REP backing Next Snapshot materialized carry tree nullifiers preserve spent proofs

Carry SnapshotThe continuation game carries a proof baseline, tracks new local deposits, and records escrow backing by original outcome.

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, and local unresolved export work is paged in batches of at most 64 refs per vault. This is why large unresolved carry sets can require multiple migration calls.

Forked escrow records both source-principal amounts and converted child REP by original outcome. The scaling path applies only when a claim has positive source principal and a forked-escrow record exists. If inherited carry requires forked escrow, missing escrow blocks settlement. A zero-source, zero-child escrow record is a no-op; recorded escrow with zero child REP can settle a positive proof for zero child REP. Once a game is final, all unresolved principal is cleared, and no escrow remains, any residual REP still sitting in the escalation game can be swept back to the security pool.

nextChildRepClaimed = ceil ( nextSourcePrincipalClaimed forkedEscrowChildRep forkedEscrowPrincipal ) childRepToRelease = nextChildRepClaimed - childRepClaimed scaledWithdrawal = ceil ( sourceAmount forkedEscrowChildRep forkedEscrowPrincipal )

Forked Escrow ScalingForked escrow claims scale source principal into child REP with ceiling division, and cumulative claimed state prevents each partial proof from restarting the rounding calculation.

Adjust forked escrow cumulative rounding

Each claim computes the cumulative child REP that should have been released, then subtracts what was already claimed. This prevents each partial proof from restarting the ceiling division. Rounding is at atomic REP precision, so each ceiling step rounds up by at most one atomic unit.

Cumulative release minus already claimed cumulative 12.176471 REP If this claim restarted rounding alone restart 5.411765 REP
Child REP already released6.764706 REP
Cumulative child REP after claim12.176471 REP
Child REP released now5.411765 REP
If rounding restarted5.411765 REP if each claim rounded alone
Claim boundswithin escrow principal

Recursive Continuation Example

Step Example state What carries forward
Parent fork 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.
Child continuation The preferred child receives forked escrow backing and resumes with the inherited 20 REP carry after migration. Inherited carry is settled by proof, while 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 If the child game reaches non-decision, it exports a new carry tree for the next generation of child pools. The recursive child does not replay settled proofs and does not lose still-unsettled carried deposits.

Child Outcome Resolution

Child pools resolve outcomes through the forker. In an own-fork child, the child's outcome is fixed by the child's Zoltar outcome index. In an external-fork child, a local escalation result is used only if that escalation ended before the universe forked; otherwise the child remains unresolved until its own continuation or later state produces an outcome.

Whole-system decision flow Question, pool, escalation, fork, child migration, auction repair, and 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 or resume continuation recursive continuation

System Decision FlowThe main path is simple, but the continuation loop matters: a child escalation game can become the parent of the next fork generation.

ethCollateralToBuy = { 0 if repairDenominatorRepAtForkAmount = 0 or migratedRepAmount repairDenominatorRepAtForkAmount parentCollateralAmount - parentCollateralAmount migratedRepAmount repairDenominatorRepAtForkAmount otherwise

Auction ShortfallThe truth auction targets the parent collateral not already covered by migrated REP. The denominator is the contract’s repair denominator: own-fork repair uses vaultRepAtFork, while external-fork repair uses auctionableRepAtFork.

Adjust child collateral repair

In this example, the parent obligations still require 50 ETH of collateral, the repair denominator is 200 REP, and 190 REP migrate into the child branch. That branch therefore receives 190/200 of the parent collateral, or 47.5 ETH, leaving a 2.5 ETH shortfall before any repair auction raises more ETH. Each child branch is sized against its own migrated collateral and its own remaining shortfall.

0 ETH target 50 ETH
Migrated collateral47.50 ETH
Initial shortfall2.50 ETH
Remaining shortfall0.00 ETH
Repair statusfull repair
Migration boundswithin repair denominator REP

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 and finalization finds a non-empty winning prefix, qualifying ticks still settle at one synthetic uniform price and share the full REP cap. If no winning prefix exists, every bid refunds instead.

The repair target is the contract’s direct statement of the collateral gap after proportional REP migration. 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 successful repair, and after underfunded repair. 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 Underfunded partial ETH repair collateral gap remains child still continues

Truth Auction Balance SheetThe auction repairs a child pool by trading part of that child universe’s scarce REP for the ETH needed to restore collateral.

Auction Mechanics

priceAtTick = 1018 1.0001 tick

Auction Tick PriceEach tick maps to a scaled ETH-per-REP price on the auction ladder.

tick ln ( priceInWeiPerRep 1018 ) ln ( 1.0001 )

Auction Tick IndexThe inverse relation estimates the tick for a desired scaled ETH-per-REP price.

  • Tick 0 means 1 ETH/REP.
  • Positive ticks mean a higher ETH-per-REP bid.
  • Negative ticks mean a lower ETH-per-REP bid.

The auction intentionally has no total bid cap and no active-tick cap. Instead, it stores aggregate ETH by tick in an AVL tree. The tick range is finite (-524288 through 524288), so even a tree containing every possible tick has bounded height. Finalization descends aggregate tree paths and does not scan every bid; individual bid settlement is paged later. Bidding, refund, finalization, and settlement guardrails are collected in Truth Auction Operations .

Why sell REP instead of choosing truth? The child branch is already defined by Zoltar's fork. The auction's job is narrower: convert some child-universe REP into ETH so the migrated child pool can repair collateral and remain usable. The auction implementation follows owner authority, clearing, refund, and underfunded paths in order.
Auction operation and settlement paths Bidders submit before auction end, losing bids can be refunded before finalization, and the forker settles finalized bids into vault accounting. Bidding before auction end Aggregate Tree ticks, not raw bid scan Forker Finalizes public wrapper after one week Pre-finalization losing-bid refunds only ticks below the current binding tick Forker Settlement REP credited to vaults

Auction OperationsBidders can recover clearly losing bids before finalization. After finalization, the forker-owned auction is settled in pages so purchased REP can become child-pool vault ownership.

Truth auction clearing Auction starts with ETH and REP caps, sorts demand high to low, and clears normally or through the underfunded path. Child Pool Short needs ETH repair Auction Caps ETH target and REP inventory Bid Ladder higher ticks first First Binding Cap ETH target or REP inventory Normal Clear uniform price, marginal fills Underfunded synthetic uniform

Auction ClearingThe auction is dual-capped: it tries to raise enough ETH without selling more REP than allowed.

For the underfunded threshold row below, underfundedWinningEth is the finalized winning ETH kept after the winning prefix is fixed and ticks below clearingTick are excluded for refund. If underfundedWinningEth > 0, the contract computes the synthetic threshold from that winning ETH and allocates the full REP cap across ticks >= clearingTick. If no winning prefix exists, underfundedThreshold is stored as type(uint256).max, totalRepPurchased remains zero, and every bid refunds. The contract still records aggregate submitted ethRaised, but the underfunded threshold and uniform winner allocation both use underfundedWinningEth when it is non-zero.

filledRep = floor ( ethUsed pricePrecision clearingPrice ) underfundedThreshold = { ceil ( underfundedWinningEth pricePrecision maxRepBeingSold ) if underfundedWinningEth > 0 type(uint256).max otherwise underfundedRepShare = { floor ( bidEth maxRepBeingSold + clearingRemainderIn underfundedWinningEth ) if underfundedWinningEth > 0 not applicable otherwise clearingRemainderOut = { ( bidEth maxRepBeingSold + clearingRemainderIn ) mod underfundedWinningEth if underfundedWinningEth > 0 not applicable otherwise

Auction Fill MathUniform clearing converts filled ETH at the uniform clearing price; underfunded clearing sets a threshold price from winning ETH and distributes the full REP cap across those winners pro rata by ETH. The stored clearingTick marks the lowest winning tick, and bids at ticks >= clearingTick share the synthetic uniform underfunded price. If underfundedWinningEth == 0, the threshold row takes type(uint256).max, the share and remainder rows are not applicable, and every bid refunds.

Adjust an underfunded synthetic uniform example

The widget assumes the bid is already inside the finalized winning prefix. The winning ETH, REP sale cap, and bidder ETH then determine the synthetic execution price, the bidder's REP fill, and the REP assigned to the rest of the winning prefix. The tick price input is only a consistency check: if the synthetic price is above the tick limit, these inputs cannot describe a winning-prefix bid.

  • Input note: inputs are consistent
  • Prefix note: inputs are consistent with a winning-prefix bid
  • Underfunded threshold: 0.1000 ETH/REP
  • Bidder REP fill: 40.0000 REP
  • REP assigned to other winners: 60.0000 REP

In normal clearing, bids above the clearing tick win in full, bids below it lose, and bids at the clearing tick are filled in submission order. In the underfunded path, ticks at or above the finalized clearingTick boundary share the full REP sale cap pro rata by winning ETH contribution.

Both finalized paths carry division dust across paged withdrawals. Normal clearing carries clearingRemainder through the uniform-price ETH-to-REP division, and underfunded clearing reuses that same remainder while dividing the full REP cap across winning ETH, so bid-level withdrawals reconcile to the aggregate finalized REP allocation.

Settlement then turns purchased REP into child-pool vault accounting. The filled REP amount becomes pool ownership at the auction-specific auctionPoolOwnershipPerRep rate set during auction finalization. Any security-bond allowance left behind by parent vaults is assigned to auction buyers pro rata by purchased REP, with the final settlement claim taking the remainder left by integer division.

poolOwnershipAmount = purchasedRepAmount auctionPoolOwnershipPerRep newSecurityBondAllowance = auctionedSecurityBondAllowance - claimedAuctionedSecurityBondAllowance if this is the final REP claim auctionedSecurityBondAllowance purchasedRepAmount totalRepPurchased otherwise

Auction Settlement AllocationWinning auction settlement credits purchased REP into pool ownership and carries inherited security-bond allowance to buyers in the same purchased-REP proportion, while the final claim absorbs rounding dust.

  • startAuction sets ethRaiseCap, the ETH repair target.
  • startAuction also sets maxRepBeingSold, the maximum REP inventory the child pool will sell.
  • The direct startAuction and finalize calls are owner-only. Anyone can reach those steps through SecurityPoolForker.startTruthAuction and SecurityPoolForker.finalizeTruthAuction.
  • Bids are accepted only after start, before finalization, and before auctionStarted + 1 week.
  • Finalization walks aggregate AVL tick totals from the highest ETH/REP tick to the lowest; raw bids are settled later through paged withdrawals.
  • The sweep stops when the ETH raise cap binds or when the ETH collected so far, at that price, would buy at or beyond the REP sale cap. Because ticks and Solidity division are discrete, the final purchased REP can land at or just below that cap.
  • In an underfunded auction, the contract computes underfundedThreshold={ceil(underfundedWinningEthPRICE_PRECISIONmaxRepBeingSold)if underfundedWinningEth > 0type(uint256).maxotherwise, where underfundedWinningEth is the finalized ETH kept after the winning prefix is fixed and losing bids are refunded.
  • When underfundedWinningEth > 0, the stored clearingTick marks the lowest winning tick. Underfunded winners are bids at ticks >= clearingTick, and those winners share the full REP cap pro rata by ETH at the synthetic underfundedThreshold price. If no winning prefix exists, the contract stores type(uint256).max, totalRepPurchased stays zero, and every bid refunds.
  • Normal clearing carries division dust through clearingRemainder; underfunded clearing reuses that remainder while dividing the full REP cap across winning ETH.
  • Before finalization, losing bids below the currently found clearing tick can be refunded in paged batches.
  • After finalization, only the owner withdraws bid outcomes from the auction; SecurityPoolForker wraps that call so anyone can settle a vault's bid pages.
Underfunded intuition: winning ticks still clear at one synthetic price, and the winner set is the finalized tick prefix rather than a standalone threshold-price cutoff. The adjustable example above shows how weak demand can still sell the full REP cap once the winner set is fixed.

The Binding condition output reports one of four outcomes: ETH cap, REP cap, both caps, or underfunded. The same bid ladder shows how ETH-cap uniform clearing, REP-cap uniform clearing, both-cap clearing, and weak-demand outcomes clear.

Adjust uniform-clearing and underfunded auction clearing

The auction sells REP at a uniform clearing price when demand is strong enough. That uniform result can come from the ETH cap, the REP cap, or the last valid tick before the next lower price would overshoot the sale constraint. The contract input is an integer tick. This example uses a three-level tick-derived ladder displayed near 5, 4, and 3 ETH/REP to keep the arithmetic readable. If qualifying demand is below the ETH raise cap, the underfunded winner set still shares the full REP cap at one synthetic uniform price. The calculator shows human-unit allocations; exact withdrawals use integer REP-wei division with carried remainder across paged withdrawals.

REP allocation ETH raised toward cap cap 20 ETH
Clearing modeuniform clearing near 3 ETH/REP
Binding conditionREP cap
ETH retained12 ETH
Winning ETH keptnot underfunded
Derived underfunded thresholdnot underfunded
Alice receives1.00 REP
Bob receives1.33 REP
Carol receives1.67 REP
Refunds1.00 ETH

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 coordinator requests a REP/WETH report, a reporter posts token amounts, disputers can replace a bad report with a larger one, a settler finalizes the report after the timing window, and the settlement callback returns the final amounts to the coordinator. 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.

When the coordinator accepts a settled REP/ETH price, that price is reusable only inside its freshness window. It does not authorize a fixed amount of operation exposure. The initial OpenOracle report is instead sized from dispute profitability assumptions so a wrong report at the target error is profitable to challenge.

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 reporter posts disputer may replace settler callback Consume or Replay expired, stale, failed local checks, or executed

REP/ETH Oracle FlowThe coordinator is a queue plus cache. A fresh price can execute immediately; a stale price moves operations into a bounded pending-settlement path.

Oracle-Staged Operation Trace

Step Function or callback State changed Why it matters
Request operation requestPriceIfNeededAndStageOperation Executes immediately if price is fresh, otherwise records a staged operation. Fresh price cache avoids unnecessary oracle requests.
Create report requestPrice Funds one pending OpenOracle report and records callback gas/basefee bounds. The requester pays for the contestable price path when the cache is stale.
Accept callback openOracleCallback Updates lastPrice only for valid non-zero settled amounts. Bad or zero report data cannot silently refresh the solvency price.
Replay staged work executeStagedOperation Runs withdrawal, allowance update, or liquidation checks against the fresh price. Queued operations still must pass local safety checks at execution time.
Recover settled report recoverSettledPendingReport Clears the pending report and consumes only pendingOperationSlotId; other active staged operations can remain queued. Remaining operations need a later valid price path, such as a direct requestPrice() followed by settlement.
Consume failures Coordinator failure paths Expired, stale, zero-effect, or too-close operations are consumed. Failed staged work does not retry forever and block later operations.

When no fresh price is available, the caller funds the OpenOracle request bounty. The coordinator sizes that ETH cost from the current base fee, the callback gas limit, and the configured gas budget for reporting the price.

While a report remains pending, only the original pending report sponsor can queue more staged operations against that settlement. Those follow-up queues pay no extra ETH join fee; other callers must wait until the pending report settles before they can stage their own operations.

requestPriceEthCost = block.basefee 4 ( callbackGasLimit + gasConsumedOpenOracleReportPrice ) + 101

Oracle Request CostThe coordinator sizes the ETH bounty from current base fee and the configured report and callback gas limits. The 101 offset keeps the forwarded bounty strictly above the computed gas product so the OpenOracle-funded reward path has a positive buffer instead of landing exactly on the boundary.

Staged operations also need an expiry because they are caller-authorized actions, not permanent permissions. A delayed callback should not execute an old withdrawal, allowance change, or liquidation after the caller-selected validity window has passed.

stagedExpiry = queuedAt + settlementTime + validForSeconds

Staged Operation ExpiryQueued oracle-gated operations expire after settlement time plus the caller-selected validity window.

Oracle execution gates A staged operation needs a valid price and local operation checks before execution. Liquidations also need sufficient distance from the threshold. Fresh Price 5-minute cache Operation Guards snapshot, effect, pool checks Execute Operation operation volume is unmetered Liquidation Distance Gate current price must be far enough past threshold

Oracle Execution GatesOracle-gated operations execute only when the price is fresh and local operation checks pass; liquidations must also be sufficiently far beyond their threshold.

The initial report equation below names the configured REP-side exactToken1Report. It is a dispute-profitability parameter, not an exposure cap. The numerator estimates one dispute gas cost converted into REP with a profit buffer; the denominator is the target price-error room left after OpenOracle fees and expected adverse REP/ETH movement during settlement.

exactToken1Report = ceil ( requiredDisputerProfitBuffer gasUnitsForOneDispute assumedGasPriceInEthPerGas repPerEthPrice targetPriceErrorForDispute - ( openOracleProtocolFeeFraction + openOracleReporterFeeFraction ) 1 + targetPriceErrorForDispute - disputeReportSizeMultiplier expectedRepEthPriceMoveDuringSettlement )

Initial Report SizeThe report size is independent of outside funds at risk; it only needs to make a wrong report profitable to dispute under the stated assumptions.

The canonical formula inputs live in shared/ts/oracleInitialReport.ts, exported as @zoltar/shared/oracleInitialReport. The UI deployment helper and simulator deployment helper consume that shared value. The current inputs are requiredDisputerProfitBuffer = 2x, gasUnitsForOneDispute = 300,000 gas, assumedGasPriceInEthPerGas = 30 gwei, repPerEthPrice = 1,000 REP / ETH, targetPriceErrorForDispute = 10%, openOracleProtocolFeeFraction = 1%, openOracleReporterFeeFraction = 0.1%, disputeReportSizeMultiplier = 1.15x, and expectedRepEthPriceMoveDuringSettlement = 1%. Those values produce a positive denominator and exactToken1Report = 259.332023575638507216 REP (259332023575638507216 atomic REP). The helpers evaluate the displayed equation with ORACLE_FORMULA_PRECISION = 1e18 fixed-point integers: fee fractions, disputeReportSizeMultiplier, disputeProfitFraction, and priceMoveFraction use integer division that floors at that precision, then the final report amount uses ceiling division by ORACLE_FORMULA_PRECISION * denominator. If the denominator is zero or negative, the assumptions do not leave enough price-error room for profitable disputes.

thresholdPrice = floor ( vaultRep pricePrecision snapshotTargetAllowance securityMultiplier ) currentPrice > thresholdPrice distanceBps = floor ( ( currentPrice - thresholdPrice ) basisPointDenominator currentPrice ) distanceBps minLiquidationPriceDistanceBps

Liquidation Price DistanceA staged liquidation must be strictly above the computed threshold, then remain at least the configured floored basis-point distance beyond it before it can execute.

The detailed immediate-execution path, staging constraints, pending report bound, callback no-op cases, recovery path, consumed failure modes, liquidation snapshot staleness, and liquidation-distance check are listed with source links in the REP/ETH Oracle Operations reference.

10. Assumptions and Security Model

#
REP value securing the system > value of the obligations secured by that REP

Security AssumptionThe protocol assumes REP backing remains economically larger than the obligations it secures.

Protocol Assumptions

  • The economic value of REP backing remains larger than the value of the obligations that REP is securing.
  • Users accept that, after a fork, the protocol may sell part of a child universe’s REP backing in the truth auction to refill missing ETH collateral.

Market and Liquidity Assumptions

  • The child-universe truth auction can attract enough demand for child-universe REP to repair missing ETH collateral when repair is needed.
  • REP and ETH can be exchanged with sufficient liquidity and price discovery that collateral repair and solvency operations do not fail purely because markets are too thin.

Coordination Assumption

  • Users who want clean resolution continue in the child universe they expect other users to keep valuing.

Operational Failure Modes

  • If truth-auction demand is weak, a child pool can resume with only partial collateral repair.
  • If REP/ETH liquidity is thin or price discovery is poor, solvency-sensitive operations become less trustworthy.
  • If users and capital split across multiple child universes, no single child universe may recover the dominant economic value assumed by the simpler fork story.

Zoltar forks only create child universes, and Placeholder 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. Current Parameters

#
Parameter timing strip Compact row of core current parameters: outcomes, activation delay, escalation length, migration time, auction time, and price validity. 3 outcomes 3 days activation 7 weeks escalation 8 weeks migration 1 week auction 5 minutes price validity

Parameter StripThe operational clock is mostly measured in days and weeks, except REP/ETH solvency prices, which expire after five minutes.

Outcomes NUM_OUTCOMES = 3: Invalid, Yes, and No.
Initial Escalation Deposit 1 REP default deployment value (1000000000000000000 atomic REP units).
Activation Delay 3 days after the first accepted escalation deposit.
Non-Decision Threshold totalTheoreticalRepSupply / 40.
Reward Window EXCESS_REWARD_WINDOW_DIVISOR = 2.
Migration Time 8 weeks.
Auction Time 1 week.
Minimum Vault REP MIN_REP_DEPOSIT = 10 ether.
Price Validity PRICE_VALID_FOR_SECONDS = 5 minutes.

Escalation Parameters

Rows marked as deployment-configured are constructor or factory inputs for a deployment. Rows marked as constants come from the named Solidity constant and require a contract change to modify.

Parameter Current value Meaning and source / mutability
NUM_OUTCOMES 3 Placeholder complete sets mint Invalid, Yes, and No shares. Protocol constant for the current Placeholder market shape.
initialEscalationGameDeposit 1 REP default Deployment-configured starting bond passed through SecurityPoolFactory; the default is 1000000000000000000 atomic REP units, and deployments can override it.
activationDelay 3 days Delay between game start and the escalation clock beginning to accrue cost. Deployment-configured EscalationGame parameter.
Escalation nonDecisionThreshold totalTheoreticalRepSupply / 40 Deployed as repToken.getTotalTheoreticalSupply()forkThresholdDivisor2. Deployment-derived threshold using the REP token's theoretical supply and the configured fork threshold divisor.
EXCESS_REWARD_WINDOW_DIVISOR 2 Extends the escalation reward-eligible cap by an extra 50% region above binding capital.
MERKLE_MOUNTAIN_RANGE_MAX_PEAKS 64 Maximum inherited-carry MMR peaks accepted by fork-continuation proof state.
NULLIFIER_DEPTH 64 Nullifier depth used to track consumed inherited-carry proofs.
MAX_UNRESOLVED_EXPORT_REFS 64 Maximum local unresolved deposit refs exported per vault per migration batch.

Migration, Auction, Solvency, and Retention Parameters

Parameter Current value Meaning and source / mutability
MIGRATION_TIME 8 weeks Fork-migration window before truth auction can start. Solidity constant in SecurityPoolUtils.
AUCTION_TIME 1 week Truth-auction duration. The wrapper gate uses SecurityPoolUtils.AUCTION_TIME, and bid acceptance plus finalization in UniformPriceDualCapBatchAuction use that contract's own AUCTION_TIME; the values must stay aligned.
MAX_AUCTION_VAULT_HAIRCUT_DIVISOR 1000000 Small haircut divisor used when reserving a tiny amount of REP from auction sale. Solidity constant in SecurityPoolUtils.
Auction tick bounds -524288 to 524288 Inclusive discrete ETH/REP price-tick range accepted by the truth auction. Solidity constants in UniformPriceDualCapBatchAuction.
minBidSize max(ethRaiseCap / 100000, 1 wei) Minimum bid size computed at auction start from that auction's ETH raise cap.
Maximum AVL height over full tick domain 28 Derived gas bound for an AVL tree containing every possible auction tick. Derived from the finite tick-domain constants, not a stored contract parameter.
PRICE_PRECISION 1018 Fixed-point precision for REP/ETH and retention-rate math. Solidity constant in SecurityPoolUtils.
MAX_RETENTION_RATE 999999996848000000 Upper retention-rate bound, annotated in code as approximately 90% yearly retention. Solidity constant in SecurityPoolUtils.
MIN_RETENTION_RATE 999999977880000000 Lower retention-rate bound, annotated in code as approximately 50% yearly retention. Solidity constant in SecurityPoolUtils.
RETENTION_RATE_DIP 80% / 800000000000000000 Scaled utilization point where the retention curve reaches its minimum. Solidity constant in SecurityPoolUtils.
LIQUIDATION_REP_BONUS_BPS 500 Fixed liquidator reward bonus in basis points. Higher values increase liquidator reward but reduce the margin by which a liquidation improves target health. Solidity constant in SecurityPoolUtils.
MIN_SECURITY_BOND_DEBT 1 ether Minimum non-zero bond allowance a vault can carry. Solidity constant in SecurityPoolUtils.
MIN_REP_DEPOSIT 10 ether Minimum REP backing for a non-empty vault. Solidity constant in SecurityPoolUtils.

Oracle Parameters

Parameter Current value Meaning and source / mutability
PRICE_VALID_FOR_SECONDS 5 minutes How long a settled REP/ETH price remains valid. Solidity constant in OpenOraclePriceCoordinator.
gasConsumedOpenOracleReportPrice 100000 Coordinator gas estimate for report submission. Deployment-configured coordinator parameter.
gasConsumedSettlement 1000000 Per-operation settlement gas estimate used by the coordinator. Deployment-configured coordinator parameter.
MAX_PENDING_SETTLEMENT_OPERATIONS 4 Maximum staged operations executed from one OpenOracle settlement callback. Solidity constant in OpenOraclePriceCoordinator.
MAX_OPERATION_VALID_FOR_SECONDS 5 minutes Maximum self-service validity window for staged oracle-gated operations. Solidity constant in OpenOraclePriceCoordinator.
OpenOracle exactToken1Report 259.332023575638507216 REP (259332023575638507216 atomic REP), computed from the initial report size equation. Initial report liquidity parameter, stored as an ERC-20 atomic REP amount and sized from dispute profitability assumptions. Derived deployment parameter.
OpenOracle escalationHalt 2,593.32023575638507216 REP (2593320235756385072160 atomic REP), computed as exactToken1ReportescalationHaltMultiplierBps/10000 Dynamic halt threshold for multiplicative report escalation; later reports add one atomic REP unit at a time. Derived from the deployment report size and escalation-halt multiplier.
OpenOracle settlerReward block.basefee2gasConsumedOpenOracleReportPrice Dynamic ETH reward paid to settler, derived from current block.basefee and report gas configuration.
OpenOracle settlementTime 4012 Timestamp-based settlement delay encoded as 480 seconds (8 minutes). The 40 * 12 derivation assumes twelve-second blocks, but the configured OpenOracle timeType uses seconds. Deployment-configured OpenOracle parameter.
OpenOracle disputeDelay 0 Deployment-configured OpenOracle parameter; no extra waiting period after each report.
OpenOracle protocolFee 100000 Protocol fee sent to the configured fee sink, equal to 1%. Deployment-configured OpenOracle parameter.
OpenOracle callbackGasLimit 4000000 Settlement callback gas limit computed as gasConsumedSettlementMAX_PENDING_SETTLEMENT_OPERATIONS. Derived deployment parameter.
OpenOracle feePercentage 10000 Fee paid to previous reporter, annotated in code as 0.1%. Deployment-configured OpenOracle parameter.
OpenOracle multiplier 115 New report amount must be 1.15x the prior amount until the escalation halt. Deployment-configured OpenOracle parameter.
OpenOracle timeType true Deployment-configured OpenOracle parameter; OpenOracle uses block timestamps rather than block numbers.
OpenOracle trackDisputes true Deployment-configured OpenOracle parameter; dispute history is stored for these reports.
OpenOracle protocolFeeRecipient 0x000000000000000000000000000000000000dEaD Deployment-configured OpenOracle parameter; fee sink configured for protocol fees.
escalationHaltMultiplierBps 100000 Multiplier used to derive the OpenOracle escalation halt from exactToken1Report, equal to 10x. Deployment-configured coordinator parameter.
maxSettlementBaseFeeMultiplierBps 30000 Maximum settlement base-fee multiplier accepted by the coordinator, equal to 3x. Deployment-configured coordinator parameter.
minLiquidationPriceDistanceBps 1000 Minimum liquidation distance required before executing a staged liquidation. The current REP/ETH price must first be strictly above the floored threshold price; then distanceBps=floor((currentPrice-thresholdPrice)BPS_DENOMINATORcurrentPrice) must be at least 10%, so distanceBps >= minLiquidationPriceDistanceBps. Deployment-configured coordinator parameter.
Coordinator WETH 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 Mainnet immutable token address used in REP/ETH price reports.

12. Lifecycle Recap

#

The lifecycle has four checkpoints: pool creation, local escalation, fork migration, and optional collateral repair. The Lifecycle Flow figure shows the happy path; fork migration and auction repair cover the exceptional cases.

  • Local resolution keeps redemption in the original pool.
  • Non-decision moves the relevant state into child pools.
  • Short child collateral triggers truth-auction repair before normal child operation resumes.
  • Solvency-sensitive operations run through the bounded REP/ETH price-oracle path, not through the truth/forking path.

13. Implementation Constraints

#
  • Origin security pools currently support the exact categorical market shape Yes / No, with Invalid added as the third trading and resolution outcome.
  • Placeholder issues transferable shares and manages redemption and migration, but does not provide secondary-market trading.
  • Zoltar fork thresholds and fork burn parameters are constructor-configured economics; the default deployment config uses forkThresholdDivisor = 20 and forkBurnDivisor = 5.
  • The escalation-game initial deposit is deployment-configured, with a default of 1 REP (1000000000000000000 atomic REP units).
  • 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.