OpenOracle Integration
Placeholder uses OpenOracle for one narrow job: getting
a fresh REP/ETH price when a pool needs to check solvency. It does
not use OpenOracle to decide market truth; truth still comes from
local escalation and, if necessary, Zoltar forks.
The core lifecycle is simple. A user stages a liquidation, withdrawal, or allowance update with the pool's coordinator. The coordinator requests OpenOracle when needed, and after settlement it either executes the restricted pool action with that fresh price or leaves a recovery path if execution conditions no longer hold.
A stale or missing price sends solvency-sensitive operations into staging because Placeholder would rather delay a withdrawal, liquidation, or allowance change than let an old REP/ETH quote distort pool solvency checks.
The OpenOracle report instance uses REP as token1 and
WETH as token2. The coordinator converts the settled
amounts into a REP-per-ETH price because WETH is the
ETH-denominated leg of the report.
The contract and research context lives in: OpenOracle documentation, the OpenOracle paper, the local OpenOracle contract, the OpenOraclePriceCoordinator integration, and the Placeholder whitepaper's REP/ETH Price Oracle section. Current oracle research lives in the OpenOracle Technical Details sidebar, especially Attack Vectors and Attack Vectors (Inclusion).
OpenOracle Role
The generic oracle lifecycle comes first. Placeholder's coordinator is only a consumer of the report that OpenOracle settles.
OpenOracle creates report instances for a token pair. Reporters submit token amounts, later reporters can dispute by posting a larger report, and a report can settle after its timing window. When a callback contract is configured, settlement calls that contract with the settled amounts.
In this repository the pair is REP as token1 and WETH as
token2. The coordinator converts the callback amounts
into the following REP per ETH price:
REP/ETH PriceThe settled REP amount is scaled by the fixed price precision and divided by the settled WETH amount.
After the coordinator accepts a settlement, that accepted REP/WETH report becomes a REP/ETH coordinator price. The price is reusable only while it remains inside the freshness window; operation volume is not metered by the accepted report.
Four coordinator terms drive the rest of the integration behavior: staged operation, fresh price, liquidation threshold, and report liquidity.
A staged operation is a
liquidation, REP withdrawal, or allowance update queued behind the
coordinator until it can use a
fresh price, meaning a settled
coordinator price still inside PRICE_VALID_FOR_SECONDS,
currently a five-minute cache window.
For liquidations, the relevant boundary is the
liquidation threshold. A
staged liquidation can execute only when the current REP/ETH price is
strictly above that threshold and at least the configured
liquidation distance beyond it,
with that extra margin currently set by
minLiquidationPriceDistanceBps.
OpenOracle disputes scale with report liquidity, the REP/WETH report size posted into the oracle. Larger report liquidity gives honest reporters more economic weight to dispute a bad price. The coordinator sizes the initial report from dispute profitability assumptions, not from the amount of outside value protected by the price.
Placeholder Integration
In Placeholder, every pool gets its own coordinator. That keeps pending operations, freshness windows, and callback state local to one pool instead of sharing one global queue across the protocol.
OpenOraclePriceCoordinator is deployed with an
immutable OpenOracle address. A security pool stages
liquidation, REP-withdrawal, and security-bond-allowance operations
through the coordinator when a fresh REP/ETH price is needed.
Deployment is per pool. SecurityPoolFactory asks
PriceOracleManagerAndOperatorQueuerFactory for a fresh
coordinator for each origin or child security pool, wiring it to the
shared OpenOracle, shared WETH, and that universe's REP
token. SecurityPoolDeployer passes the coordinator into
the pool, and the factory then calls
setSecurityPool exactly once so only that pool can seed
and execute through the coordinator. Child pools inherit the parent's
last price as a starting value, but freshness is still controlled by
the coordinator's settlement timestamp and validity window.
The factory contract still uses its older internal name, but it is the
active deployment path for OpenOraclePriceCoordinator
instances.
Integration FlowThe coordinator is the trust boundary between Placeholder operations and OpenOracle reports. It applies guardrails before request and staging, then again after callback before execution.
If the cached price is valid, the coordinator attempts immediate
execution after staging the operation record. Liquidation-specific
gates, snapshot staleness, zero-effect withdrawals, or downstream pool
checks can still consume the operation as a failed staged operation.
Otherwise the coordinator can create one OpenOracle report and attach
up to four staged operations to the settlement callback. Additional
staged operations remain active but outside the auto-executed callback
batch; if the fresh price remains valid and they have not expired,
they require a later manual executeStagedOperation call.
Staged operation
inputs are constrained before any oracle report is requested.
Liquidation and withdrawal amounts must be non-zero, allowance updates
may set zero, validForSeconds must be positive and no
more than five minutes, liquidation must target a different vault,
non-liquidation operations must target the caller's own vault, and
staging is disabled after the security pool's local escalation has
already resolved. Same-vault liquidation is rejected with
Caller bad before the coordinator requests or uses an
oracle report.
Report funding is caller supplied. The coordinator computes
requestPriceEthCost, the caller-funded
request bounty, from the
current base fee, the callback gas limit, and the gas the coordinator
expects to spend inside openOracleReportPrice:
Oracle Request CostcallbackGasLimit is the gas reserved for the
settlement callback,
gasConsumedOpenOracleReportPrice is the coordinator's
own report-price callback work, and the small
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.
If a cached price is usable, the staged operation pays no oracle
request cost and unused ETH is refunded. If a new report is needed,
only the first pending settlement slot retains the request cost;
requestPrice forwards exactly that bounty to OpenOracle
and refunds any excess. That call creates an OpenOracle report
instance; external reporters still submit the initial REP/WETH
amounts, disputes can replace those amounts, and settlement returns
the final amounts through the callback.
Once a report is pending, only the original
pendingReportSponsor can queue more staged operations
against that in-flight settlement. Those follow-up queues pay no
additional ETH join fee, and other callers must wait for settlement
before staging their own operations.
The money flow is easiest to follow if each role is separated. The triggering caller pays the ETH request bounty, the initial reporter posts the REP/WETH report amounts, and the eventual settler receives the settler reward. Those roles can be different accounts or the same account.
Worked example using a market snapshot taken at
2026-07-09 09:49:35 UTC: at that moment Ethereum base
fee was 69,594,403 wei per gas
(0.069594403 gwei), ETH traded at
$1,750.34, and REP traded at $0.792188 or
0.00045231 ETH per REP. The USD quotes and the
ETH-per-REP quote are
independently rounded snapshot values, so reconstructing one from the
other may differ by a few basis points. With the coordinator defaults
used in this documentation revision,
gasConsumedOpenOracleReportPrice = 100,000,
gasConsumedSettlement = 1,000,000, and
MAX_PENDING_SETTLEMENT_OPERATIONS = 4, the callback gas
limit is:
Worked Callback Gas LimitThe current coordinator parameters reserve four settlement-operation gas budgets inside one callback.
Worked Trigger CostThe caller
who opens a fresh price request sends
0.001141348209200101 ETH, about $2.00 at
that snapshot's ETH/USD price. This bounty is separate from the
normal gas burned by the caller's own transaction.
The initial reporter then posts the configured
exactToken1Report,
259.332023575638507216 REP, plus the matching WETH side
at the chosen report price. Using the same market snapshot:
Worked Initial Report StakeThe
reporter posts about 259.3320 REP and
0.11729847 WETH, roughly $205 of each
token side at that snapshot. The displayed WETH decimal is an
explanatory approximation from the rounded REP/ETH quote rather than
an exact atomic-unit contract input.
If no dispute replaces the report, settlement returns the full REP and WETH amounts to the current reporter. The ETH bounty is split between the initial reporter reward and the settler reward:
Worked No-Dispute PayoutIf the
same account triggers the request, submits the initial report, and
settles it undisputed, that account gets back the full REP stake,
the full WETH stake, and the entire ETH bounty in two pieces:
0.001127429328600101 ETH as reporter reward and
0.0000139188806 ETH as settler reward. If different
accounts fill those roles, the REP/WETH principal goes to the
current reporter, the larger ETH reward goes to the initial
reporter, and the smaller ETH reward goes to the settler.
Escalation-game REP deposits are the exception to the staged callback
path. When the pool has active security-bond allowance,
depositToEscalationGame requires a fresh coordinator
price and reverts if it is stale. After previewing the accepted REP
deposit, the pool uses lastPrice for the post-transfer
vault and pool coverage checks instead of queueing the deposit behind
a report.
Security Guardrails
Placeholder uses the oracle to preserve solvency accounting around ETH obligations backed by REP. The guarantee is conditional: operations that can change REP backing or security-bond exposure must use a fresh, economically contestable REP/ETH price and must pass local coordinator checks before they can affect the pool.
-
PRICE_VALID_FOR_SECONDSlimits accepted prices to a five-minute cache window. - The initial report is sized so an honest disputer can profitably challenge a wrong price at the target error, independent of outside funds protected by the price.
- Pool execution functions reject direct user calls: withdrawals, liquidations, and allowance changes can be performed only by the configured coordinator while its price is still valid.
- Liquidations use a queue-time snapshot and fail if target allowance changes or ownership decreases before execution.
-
Liquidations must remain at least
minLiquidationPriceDistanceBpsbeyond the computed liquidation threshold. - The callback rejects zero reports, computed zero prices, unexpected report ids, and settlements during excessive base fee.
The initial report size is computed from the minimum dispute profitability target. The numerator estimates the REP value of one dispute's gas cost with a profit buffer. The denominator is the price-error room left after OpenOracle reporter/protocol fees and expected adverse REP/ETH movement during settlement. If that denominator is non-positive, the parameter set is unsafe because the target wrong price cannot be disputed profitably under the stated assumptions.
Initial Report SizeThe coordinator's configured REP-side report amount is sized from dispute profitability assumptions, not from protected external value.
The canonical deployment calculation lives in
shared/ts/oracleInitialReport.ts, exported as
@zoltar/shared/oracleInitialReport. The UI deployment
helper and simulator deployment helper both consume that shared value.
With the current inputs, the denominator is positive and the configured
output is 259332023575638507216 atomic REP, or
259.332023575638507216 REP.
| Formula input | Current value | Canonical shared source |
|---|---|---|
requiredDisputerProfitBuffer |
2x |
ORACLE_REQUIRED_DISPUTER_PROFIT_BUFFER |
gasUnitsForOneDispute |
300,000 gas |
ORACLE_GAS_UNITS_FOR_ONE_DISPUTE |
assumedGasPriceInEthPerGas |
30 gwei |
ORACLE_ASSUMED_GAS_PRICE_WEI_PER_GAS |
repPerEthPrice |
1,000 REP / ETH |
ORACLE_ASSUMED_REP_PER_ETH_PRICE |
targetPriceErrorForDispute |
10% |
ORACLE_TARGET_PRICE_ERROR_FOR_DISPUTE |
openOracleProtocolFeeFraction |
1% |
ORACLE_PROTOCOL_FEE / ORACLE_PERCENTAGE_PRECISION |
openOracleReporterFeeFraction |
0.1% |
ORACLE_FEE_PERCENTAGE / ORACLE_PERCENTAGE_PRECISION |
disputeReportSizeMultiplier |
1.15x |
ORACLE_MULTIPLIER / 100 |
expectedRepEthPriceMoveDuringSettlement |
1% |
ORACLE_EXPECTED_REP_ETH_PRICE_MOVE_DURING_SETTLEMENT
|
If the denominator is zero or negative, those assumptions do not leave enough price-error room for a profitable dispute, so deployment must choose a larger target error, lower fee/move assumptions, or a different initial report size model.
The helpers evaluate the displayed equation with
ORACLE_FORMULA_PRECISION = 1e18 fixed-point integers.
openOracleProtocolFeeFraction,
openOracleReporterFeeFraction,
disputeReportSizeMultiplier,
disputeProfitFraction, and
priceMoveFraction use integer division that floors at
that precision. The final exactToken1Report then uses
ceiling division by ORACLE_FORMULA_PRECISION * denominator.
Those helper rounding rules produce the current atomic REP value
above.
Initial report estimator
The estimator stresses the formula inputs. It treats the displayed equation as a continuous model so the sensitivity curves are easy to read; deployment still uses the fixed-point rounding rules described above. The REP/ETH movement input is an adverse movement buffer during settlement, not an average signed drift.
The target-error and adverse-move curves can go vertical near the unsafe denominator boundary. Shaded regions show parameter values where a wrong report at the target error no longer leaves enough room to pay fees, absorb adverse REP/ETH movement, and still make a dispute profitable.
Once a REP/ETH price is fresh, the coordinator relies on local operation guards: withdrawals must have an effect, liquidations must pass their queue-time snapshot checks and liquidation-distance check, and allowance updates execute against the current vault state.
Callback Rejection and Recovery
OpenOracle settles with a low-level callback and emits whether that
callback succeeded. It does not automatically undo settlement when the
callback returns
false. The coordinator accepts callbacks only from the
configured OpenOracle and only for the current pending
report id. If those checks revert inside the low-level call, the
OpenOracle report can still be settled while the coordinator remains
pending. If the settlement transaction cannot leave enough gas
headroom after attempting the callback, OpenOracle reverts settlement
with InvalidGasLimit instead; in that case the report is
not settled yet and coordinator recovery is not available.
Coordinator-level price rejections are separate from low-level
callback failures. After a callback successfully enters the
coordinator, a settlement with base fee above the request-time cap,
zero amounts, or a computed zero price clears the pending id, emits
PriceReportRejected, and does not update the price cache
or replay pending operations. Those pending settlement operations
remain queued for a later valid price report, but a later call to
stage another operation will not automatically fund a retry while that
pending settlement list is still non-empty; an operator or user may
need to call requestPrice directly. When OpenOracle has
already settled but the coordinator is still pending because the
low-level callback did not complete, anyone can call
recoverSettledPendingReport. Recovery clears the pending
report, resets the settlement base-fee cap, and consumes only
pendingOperationSlotId without treating it as a successful
price update. Queue bookkeeping can still leave other staged
operations active; those operations need a later valid price path, for
example a direct requestPrice() call followed by
successful settlement.
Attack Model and Defenses
The security question is not whether OpenOracle can discover market truth. It is whether an attacker can profit by forcing Placeholder to use a bad REP/ETH price long enough for a solvency-sensitive operation to execute. Honest reporters can dispute bad reports, so the model compares the attacker's outside payoff with the liquidity, delay, and censorship cost needed to keep an honest correction out.
OpenOracle's inclusion analysis models an attacker who posts a wrong
price and pays for censorship until the report can settle. For linear
payoffs the settle payoff scales with price error, but Placeholder's
most relevant liquidation payoff is binary: the attacker wins a
fixed payoff only if the manipulated REP/ETH price is strictly above
the liquidation threshold and satisfies the configured liquidation
distance check. OpenOracle's inclusion analysis notes that binary
payoffs therefore replace the linear settle payoff with
externalPayoff, while keeping the same censorship-spend
term.
The minimal attack story is: an attacker posts a manipulated REP/ETH
report, an honest reporter would normally dispute it, and the attacker
must keep that honest dispute or settlement correction out until the
bad report can settle. The payoff is binary because the liquidation
either becomes executable or it does not. The cost side scales with
oracleReportLiquidity, the report's ETH value
liquidity after converting the REP-side report amount at the honest
REP/ETH price; censorshipRate, the modeled per-step
pressure needed to keep honest activity out; and
censorshipDuration, how long the attacker must maintain
that pressure.
In the equations below, price errors are fractions of the honest
price,
externalPayoff is the attacker's outside
ETH-denominated payoff, and oracleLiquidityRatio compares
report liquidity with that payoff. The bound is useful when the
denominator is large enough that delaying honest correction costs more
than the liquidation can pay.
The first boundary is how far the manipulated price must move before the liquidation can execute. The distance guard discounts the honest price, then the execution threshold and reported price error are measured against that honest baseline. The formulas in this attack model are continuous approximations for reasoning about payoff and censorship cost; the contract guard below them shows the integer check that actually gates staged liquidation execution.
Execution Error ThresholdThe manipulated price only creates liquidation payoff when its fractional error meets the guarded liquidation threshold.
Contract Liquidation GuardThe coordinator computes the floored threshold and distance checks
before calling the pool. SecurityPool then rechecks the
scaled liquidation-threshold inequality and rejects chunks that fail
target, caller, or floor checks. Equality passes only at the distance
check; currentPrice must still be strictly above
thresholdPrice.
The attack model separates attacker payoff from delay cost. The payoff
is binary: only a price strictly above the liquidation threshold and
far enough past the guarded distance unlocks
externalPayoff; everything else unlocks nothing.
The censorship cost grows with duration, the dispute barrier, and the
report liquidity honest reporters can use.
Binary Payoff and Censorship CostHere externalPayoff is the attacker's outside
ETH-denominated payoff, and oracleReportLiquidity is
the report's ETH value liquidity after converting the REP-side
report amount at the honest REP/ETH price.
The final bound is how long censorship would need to remain cheap for the attack to be attractive. The safety duration shrinks as the censorship rate and liquidity ratio grow.
Safe Censorship DurationThe bound is strongest when report liquidity is large relative to the payoff and each additional unit of price error is costly to keep undisputed.
Adjust a binary censorship example
The liquidation distance, manipulated price, report liquidity, and payoff determine the attacker's payoff and delay cost.
- Execution error threshold:
- Manipulated price error:
- Liquidation executable:
- Attacker payoff:
- Censorship cost:
- Oracle liquidity ratio:
- Safe censorship duration (steps):
Placeholder defends this model by making wrong reports profitable to dispute at the target error, forcing liquidations to be meaningfully past threshold, and rejecting bad settlement environments. This assumes honest arbitragers are monitoring the REP/ETH price and their dispute or arbitrage transactions can be included; the report size is not scaled by the outside payoff protected by the price.
-
Censorship and delay:
settlementTimegives reporters time to dispute, and the initial report size is chosen so the target wrong price leaves an honest disputer with a positive expected profit after fees, gas, and expected adverse REP/ETH movement. -
Binary threshold attacks: staged liquidations must
satisfy
minLiquidationPriceDistanceBps = 1000, so must be at least10%rather than barely crossing the threshold. -
Block stuffing: the coordinator records a
request-time max settlement base fee and rejects the callback if
settlement base fee exceeds
maxSettlementBaseFeeMultiplierBps = 30000, currently3x. - Callback scope: only up to four operations are auto-executed from one settlement callback. Additional staged operations require a later manual execution while the price remains fresh.
-
Freshness: prices expire after
PRICE_VALID_FOR_SECONDS = 5 minutes, so delayed operations cannot reuse old reports indefinitely.
The
dynamic-programming section
of OpenOracle's inclusion analysis is a template for the inclusion
tradeoff, not a drop-in Placeholder proof. For a simplified binary
censorship bound, replace the linear price-error payoff with a
thresholded payoff that only pays after the manipulated report is
strictly above the liquidation threshold and far enough past the
guarded distance. A full binary-delay model also needs state for
distance to threshold, price evolution, and continuation value.
Locally, OpenOracle escalation is also capped: once
escalationHalt is reached, disputes can continue, but the
required token1 amount advances by 1 atomic token1 unit
instead of multiplying by reportEscalationMultiplier.
Binary Dynamic Programming TerminalThe simplified terminal payoff pays only after the reported error
crosses the threshold distance. The censorship bribe still scales
with report size, but local report-size growth is capped by
escalationHalt before continuing by one atomic unit per
step.
Adjust report escalation and censorship bribe growth
OpenOracle disputes multiply the required report size until the halt
is reached. After that, the next required amount advances by
+1 atomic token1 unit, which is
0.000000000000000001 REP for REP. The REP-scale visual
therefore remains effectively pinned at the halt while the exact
amount and per-step bribe continue to advance by one wei of REP. The
bribe output converts REP-side report size to ETH value through
the selected REP/ETH price.
OpenOracle Parameters Used
The integration depends on the OpenOracle fields below. The canonical exhaustive current-value table remains the Placeholder whitepaper's Current Parameters section.
| Parameter | Current integration value | Use in Placeholder |
|---|---|---|
token1Address / token2Address |
REP / WETH | Defines the REP/ETH price pair used for security-pool solvency checks. |
exactToken1Report |
Computed by the initial report-size formula:
259.332023575638507216 REP
(259332023575638507216 atomic REP units)
|
Sets the initial report size used for dispute profitability. |
escalationHalt |
2,593.32023575638507216 REP
(2593320235756385072160 atomic REP units)
|
Stops multiplicative report-size escalation at 10x;
later disputes add one atomic REP unit at a time.
|
settlerReward |
Pays the account that settles the report and triggers the callback. | |
settlementTime |
480 seconds (8 minutes) |
Sets the timestamp-based report settlement delay. The
40 * 12 derivation assumes twelve-second blocks, but
the configured OpenOracle timeType uses seconds.
Staged operations expire after this delay plus their
operation-specific validity window.
|
disputeDelay |
0 |
Allows disputes immediately after each report; the dispute opportunity still lasts until the settlement delay elapses. |
callbackGasLimit |
Sets the callback gas limit for up to
MAX_PENDING_SETTLEMENT_OPERATIONS staged
operations; settlement still needs enough surrounding gas to
satisfy OpenOracle's callback headroom check.
|
|
feePercentage / protocolFee |
10000 / 100000 |
Configures OpenOracle reporter and protocol fee accounting, and
creates the fee boundary behind the
honestDisputeBarrierFraction dispute barrier in the
attack model.
|
protocolFeeRecipient |
0x000000000000000000000000000000000000dEaD |
Receives OpenOracle protocol fees for the coordinator-created report instance. |
multiplier |
115 |
Requires each dispute report to increase the REP side to
1.15x the prior report until halt.
|
timeType / trackDisputes |
true / true |
Uses timestamps and keeps dispute history visible for report inspection. |
callbackContract |
OpenOraclePriceCoordinator |
Routes settled amounts back into Placeholder's price cache and staged-operation executor. |