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.
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.
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.
- A question is registered in Zoltar and a Placeholder 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 Placeholder 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.
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 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
#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, 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, 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.
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.
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 EconomicsBond allowance is not free capacity; it remains constrained by REP backing, REP/ETH price, and pool-level solvency checks.
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.
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. -
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.
-
completeSetCollateralAmountstarts higher and decays gradually. -
totalFeesOwedToVaultsstarts lower and grows over time. feeIndextracks vault fee accounting.-
currentRetentionRatechanges as utilization changes.
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 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 DecayComplete-set collateral decays exponentially into vault fees over elapsed time.
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 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
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.
Liquidation TransferLiquidation moves snapshot-sized debt to the caller vault and seizes unlocked target REP at the configured liquidation bonus.
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 .
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.
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 reject preserved positive tied maxima below
nonDecisionThreshold. Ties already at
nonDecisionThreshold, or all-zero preserved
balances, remain valid carried states.
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 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.
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.
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 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 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 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 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.
| 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.
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
createChildUniverseto deploy a child pool for a valid fork outcome. -
Vault owners call
migrateVaultto 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. |
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.
Migration ProxyThe proxy is a thin adapter, but it is economically important because Zoltar accounts migration balances by caller.
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 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 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.
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 BucketsIn an own fork, child REP is allocated between migrated vault ownership and unresolved escalation escrow according to the pre-burn source REP mix.
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.
- The parent pool forks and snapshots unresolved escalation state.
- 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.
-
Affected vaults migrate unresolved parent locks through
migrateVaultWithUnresolvedEscalation; large carry sets can require multiple calls. - 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 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.
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.
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.
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.
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.
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.
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 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.
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 the ETH needed to restore collateral.
Auction Mechanics
Auction Tick PriceEach tick maps to a scaled ETH-per-REP price on the auction ladder.
Auction Tick IndexThe inverse relation estimates the tick for a desired scaled ETH-per-REP price.
- Tick
0means1 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
.
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.
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.
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:
- Prefix note:
- Underfunded threshold:
- Bidder REP fill:
- REP assigned to other winners:
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.
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.
-
startAuctionsetsethRaiseCap, the ETH repair target. -
startAuctionalso setsmaxRepBeingSold, the maximum REP inventory the child pool will sell. -
The direct
startAuctionandfinalizecalls are owner-only. Anyone can reach those steps throughSecurityPoolForker.startTruthAuctionandSecurityPoolForker.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
, where
underfundedWinningEthis the finalized ETH kept after the winning prefix is fixed and losing bids are refunded. -
When
underfundedWinningEth > 0, the storedclearingTickmarks 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 syntheticunderfundedThresholdprice. If no winning prefix exists, the contract storestype(uint256).max,totalRepPurchasedstays 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;
SecurityPoolForkerwraps that call so anyone can settle a vault's bid pages.
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.
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.
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.
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.
Staged Operation ExpiryQueued oracle-gated operations expire after settlement time plus the caller-selected validity window.
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.
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.
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
#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 StripThe operational clock is mostly measured in days and weeks, except REP/ETH solvency prices, which expire after five minutes.
NUM_OUTCOMES = 3: Invalid, Yes, and No.
1 REP default deployment value
(1000000000000000000 atomic REP units).
3 days after the first accepted escalation
deposit.
totalTheoreticalRepSupply / 40.
EXCESS_REWARD_WINDOW_DIVISOR = 2.
8 weeks.
1 week.
MIN_REP_DEPOSIT = 10 ether.
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 . 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 |
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
|
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 |
Dynamic ETH reward paid to settler, derived from current
block.basefee and report gas configuration.
|
|
OpenOracle settlementTime |
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 . 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
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, withInvalidadded 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 = 20andforkBurnDivisor = 5. -
The escalation-game initial deposit is deployment-configured, with
a default of
1 REP(1000000000000000000atomic REP units). - 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.