Truth Auction

When Zoltar forks, Statoblast evaluates each resulting child universe after migration. If a child pool lacks the ETH needed to fully collateralize its open interest, Statoblast initiates a truth auction for that pool. The fork may have been triggered either by a direct Zoltar fork or by the Statoblast Escalation Game.

During the migration period, security vault holders choose which child universe to support by migrating their positions into the corresponding child security vault. This preserves disagreement by splitting the parent market into separate branches. However, ETH is not duplicated across those branches. Each child pool receives only the collateral, migrated REP state, and converted child-universe REP backing that actually move into it.

Because collateral follows migration rather than being copied in full, a child pool can retain its obligations while lacking enough ETH to reopen cleanly. The truth auction sells child-universe REP to raise as much repair ETH as demand supports.

The auction has an ETH raise target and a child-universe REP sale cap. Statoblast runs the resulting dual-cap process through the UniformPriceDualCapBatchAuction contract, then writes winning claims back into vault state rather than paying out simple token transfers, so bidders buy into the child pool and its matching security-bond allowance instead of merely receiving REP.

Operational Lifecycle

Security assumptions: A02 viable operating costs, A11 practical migration, A03 independent bidders, A12 efficient REP conversion, A16 timely inclusion, A06 lifecycle executors, A20 bid-data availability, A25 safe immutable parameters, and A22 native-ETH recipient compatibility.

For any child pool with a collateral shortfall, the truth auction can only begin after fork migration has closed. The migration window lasts through forkActivationTime + SecurityPoolUtils.MIGRATION_TIME, inclusive. forkActivationTime is recorded when this parent pool enters PoolForked; it is pool-local and can be later than Zoltar's universe forkTime. See FORK-05 for the canonical timing invariant. Until that window ends, vault holders can still choose which child universe to support, so the protocol cannot know the final collateral shortfall.

After migration closes, anyone may call startTruthAuction. The forker then starts the underlying auction for the affected child pool, using the pool's ETH repair target and REP sale cap. Bidders submit ETH directly to the auction during the one-week bidding window.

When bidding ends, anyone may call finalizeTruthAuction. Finalization clears the batch auction, sends the raised ETH into the repair path, and computes the accounting rates used to settle bidders. The child activates with legitimate migration collateral plus retained bid ETH, even when that total is below the original fork snapshot. Full repair is not guaranteed, and the remaining impairment is borne by the affected child's open-interest holders. This is the security model's EG02 full-repair exclusion. Finalization rejects caller contribution ETH, and forced ETH remains unaccounted surplus. On the underfunded path, qualifying bidders collectively receive maxRepBeingSold; when no bid qualifies, no REP is sold. Any unsold REP remains behind inherited ownership. A zero-migration full-cap sale is the special case where every auctionable REP is sold and the inherited denominator has no live child owner; see Zero-migration full-cap ownership.

After finalization, winning bids are written into child-pool vault accounting. Winners receive ownership in the child pool plus their proportional share of the auctioned security-bond allowance, rather than a direct REP token transfer.

Auction Lifecycle After migration closes, a child-pool shortfall can be repaired through auction. ETH bids restore collateral, while winning claims are settled into vault accounting. REP doesn't leave the system. Finalization accounts the ETH actually raised and rejects contribution-only ETH, so bidder settlement never depends on a donor.

Phase External call Auction call Who can call it Effect
Start auction startTruthAuction startAuction Anyone may call the wrapper; the forker calls the owner-only auction function. Opens the auction using the child pool's repair target and REP sale cap.
Submit bid Bidder calls the auction directly submitBid Any bidder during the one-week auction window, with msg.value >= minBidSize. Supplies ETH demand for the repair. The minimum bid is max(ethRaiseCap / 100000, 1 wei); final bid treatment is defined by clearing.
Finalize auction finalizeTruthAuction finalize Anyone may call the wrapper after the auction window has passed; the forker calls the owner-only auction function. Clears the auction, routes raised ETH into the repair path, and computes bidder settlement rates.
Refund losing bids settleAuctionBids refundLosingBidsFor Anyone may fetch a bounded bid page with getBidderBidPage or getBidPageAtTick, then filter those results down to one bidder or vault and pass the selected (tick, bidIndex) references into the forker's refund batch. Allows currently losing bids to exit before the auction is finalized.
Settle winning bids claimAuctionProceeds or settleAuctionBids withdrawBids Anyone may fetch bounded bid pages, group the returned bids for one bidder or vault, then settle the selected (tick, bidIndex) entries in one or more bounded batches instead of settling the entire bid set at once. Writes winning claims into child-pool vault accounting as pool ownership plus proportional security-bond allowance.
Pull a deferred refund None withdrawPendingEthRefund The bidder calls the auction after a prior positive refund push was rejected and once its address can accept ETH. Transfers the bidder's complete pendingEthRefunds balance without replaying or changing the settled bid.

The direct auction finalize path allows finalization at auctionStarted + AUCTION_TIME. The public finalizeTruthAuction wrapper is slightly stricter and requires the timestamp to be greater than truthAuctionStarted + SecurityPoolUtils.AUCTION_TIME.

How Clearing Works

Security assumptions: A01 bidder incentives, A12 efficient REP conversion, and A13 REP liquidity.

Bidders submit ETH at discrete price ticks, where each tick encodes an ETH-per-REP limit price. The auction then walks demand from the highest price downward. A bid above the clearing tick wins in full, a bid below it loses in full, and bids at the clearing tick share the last fill. In the uniform-clearing branch that last fill is First in First Out within the tick, so earlier same-tick bids are consumed before later ones.

The tick math follows the contract's tickToPrice function. Tick 0 means 1 ETH/REP. Each step up multiplies price by roughly 1.0001, and each step down divides by the same factor, so the price at tick tickIndex is approximately 1.0001tickIndex ETH/REP. In other words, a higher tick means the bidder is willing to pay more ETH per REP, which is why higher ticks are evaluated first during clearing.

Two caps define the uniform-clearing stopping point. The first cap is ethRaiseCap, the amount of ETH the child pool is trying to restore. The second cap is maxRepBeingSold, the maximum child-universe REP inventory the pool is willing to sell. As soon as cumulative demand is enough to hit either cap at some price, that price becomes the clearing tick and all winning bids settle from it at one price. That is a valid uniform clearing even when the REP cap binds before the ETH repair target is fully met. The contract finds that result by descending aggregate subtree totals, not by looping over every bid one by one.

Underfunded Clearing

Intentional loss allocation. A truth auction is both a collateral-repair mechanism and a penalty for security capital that does not migrate into the child. The cap-implied underfundedThreshold qualifies bids; it is not an execution-price floor.

When demand never reaches a full clearing price, the auction switches into the underfunded branch. Finalization computes underfundedThreshold = ceil(ethRaiseCap · 1e18 / maxRepBeingSold), stores the lowest tick whose price reaches that qualification threshold as clearingTick. Only bids at or above the cap-implied qualification threshold are retained; lower ticks refund. Qualifying bidders collectively purchase maxRepBeingSold for underfundedWinningEth, so the common effective price is their retained ETH divided by the REP cap. That effective price can be lower than the qualification threshold; the threshold is not an execution-price floor. If no ETH qualifies, every bid refunds.

The child activates with the ETH actually routed during migration plus the ETH retained by the auction. It may remain undercollateralized, and open-interest holders are not guaranteed to be made whole. That shortfall is the intended consequence of insufficient demand: non-migrating security positions lose the auctioned economic value, while users of the affected child bear any remaining collateral impairment.

underfundedThreshold = ceil ( ethRaiseCap PRICE_PRECISION maxRepBeingSold )

Underfunded ThresholdPRICE_PRECISION = 1e18. The ceiling tick is the bid-eligibility boundary, not an execution-price floor. Solidity assigns the REP cap by differencing cumulative floor allocations at fixed bid positions.

The interactive example below is where those branches become concrete. The Binding condition output reports one of four outcomes: ETH cap, REP cap, both caps, or underfunded. The first three are uniform-clearing outcomes, while underfunded is the weak-demand path.

The comparison table and examples apply the branch rule above at the binding level and its boundaries. After clearing, SecurityPoolForker.claimAuctionProceeds maps that purchased REP into the corresponding pool-ownership credit and auctioned allowance share for the child branch.

Bid position Uniform-clearing branch Underfunded branch
Above the binding level Wins in full at the uniform clearing price. Qualifies only if its tick is at or above the cap-implied qualification clearingTick; qualifying bids share the complete REP sale cap.
At the binding level Shares the marginal fill FIFO inside that tick. Qualifies at the threshold boundary, then shares the complete REP sale cap by retained ETH.
Below the binding level Loses and is refunded. Loses and is refunded.

Worked Underfunded Examples

The examples below use maxRepBeingSold = 10 REP and ethRaiseCap = 20 ETH, so the formula above gives 2 ETH/REP. Figures use human-unit approximations; the contract floors per-bid allocations in atomic units.

Example Bid ladder Threshold qualification Outcome
Two-bidder underfunded prefix Bob: 6 ETH @ 4 ETH/REP
Alice: 4 ETH @ 2 ETH/REP
At Bob's higher tick, cumulative demand is 6 / 4 = 1.5 REP, so the REP cap is not reached.
At Alice's lower tick, cumulative demand is 10 / 2 = 5 REP, so funded clearing still fails.
Both limits meet the 2 ETH/REP qualification threshold.
The system keeps 10 ETH.
Bob and Alice purchase 10 REP pro rata by winning ETH.
Bob receives 6 REP and Alice receives 4 REP, both at an effective price of 1 ETH/REP.
Three-bidder underfunded prefix Alice: 5 ETH @ 5 ETH/REP
Bob: 3 ETH @ 3 ETH/REP
Carol: 2 ETH @ 2 ETH/REP
At the active ticks, cumulative demand is 5 / 5 = 1 REP, then 8 / 3 ≈ 2.67 REP, then 10 / 2 = 5 REP.
All of those remain below the 10 REP cap, so the funded branch never triggers.
All three bid limits meet the 2 ETH/REP qualification threshold.
The system keeps 10 ETH.
Alice, Bob, and Carol share 10 REP pro rata by winning ETH.
Alice receives 5 REP, Bob 3 REP, and Carol 2 REP, all at 1 ETH/REP.
Tiny-demand edge case Alice: 1 ETH @ 0.11 ETH/REP
No other positive-price bids.
At Alice's tick, cumulative demand is 1 / 0.11 ≈ 9.09 REP, so funded clearing remains below the 10 REP cap and fails.
Alice's 0.11 ETH/REP limit is below the 2 ETH/REP qualification threshold.
The system refunds 1 ETH.
Alice receives no REP; the inventory remains unsold.

Boundary note: if a lower positive bid would make cumulative demand reach the REP cap at that lower tick, the auction no longer stays in the underfunded branch. It becomes a normal funded clearing at that tick, with the marginal lower-price tick filled only up to the REP cap. If that tick contributes more ETH than the cap accepts, the unfilled remainder is refunded.

Gas and Spam Bounds

Security assumption: A02 viable operating costs.

The main spam risk is a bidder trying to fill bid slots or tick levels cheaply enough to block later demand. The auction avoids that failure mode by not capping total bids or active price levels at all.

Finalization remains bounded without those caps because it does not iterate over raw bids. Same-tick bids append to per-tick arrays for later batched withdrawal. Read helpers expose bounded offset/limit slices of bids at one tick or for one bidder, and settlement later uses selected TickIndex references from those reads to process bids in bounded batches. Clearing itself uses aggregate ETH totals stored in an AVL tree keyed by tick. The tree exists so the contract can keep all valid price levels open during bidding, yet still find the highest winning region by descending aggregate subtree totals instead of scanning every submitted bid. Finalization therefore pays for tree descent across the finite tick domain rather than for the total number of bids in the auction. The valid tick range is finite: [-524288, 524288], or 1,048,577 possible price levels. An AVL tree containing every possible tick has maximum height 28, so finalize() descends bounded tree paths and pruned subtrees instead of risking an out-of-gas loop over every submitted bid.

The gas tests cover both funded and underfunded finalization over a synthetic max-depth tree for the full tick domain. They assert each path remains below 20,000,000 gas and print measured gas values in the test output, so the documented worst case is a max-depth AVL-tree finalization path that still stays under that bound.

Settlement Modes

Security assumption: A22 native-ETH recipient compatibility.

Currently losing bids can be refunded before finalization once current demand is enough to find a clearing tick. Only ticks below that found clearing tick can be withdrawn through the pre-finalization refund path; binding or potentially winning bids stay in the auction.

Refund delivery is independent from bid settlement. The auction first marks each selected bid and fixes its REP and allowance result, then attempts to push any positive ETH refund with an explicit gas: 30,000 call argument. Because the call transfers nonzero value, the EVM adds its 2,300-gas value-transfer stipend, giving recipient code an effective maximum of 32,300 gas. If the bidder rejects, reverts, or exhausts that bounded callback budget, settlement remains complete and the ETH is added to pendingEthRefunds. The bidder can later pull the complete balance with withdrawPendingEthRefund; that caller-initiated transfer is not subject to the push cap, and a rejected pull leaves the balance unchanged. The auction emits the balance clear before that callback, so a reentrant settlement's new deferred refund follows the clear in receipt order.

Finalized case Bid treatment Why it works this way
Losing bids Receive ETH refunds. They did not contribute to the accepted clearing demand.
Normal winning bids above clearing tick Convert all accepted ETH into purchased REP at the uniform clearing price. Higher bids should receive the same execution price as the marginal winner.
Normal marginal clearing tick May be partially filled and partially refunded; same-tick bids fill in First in First Out submission order. First in First Out gives deterministic marginal allocation without iterating over all same-tick bids during finalization.
Underfunded winning ticks Settle the fixed outcome produced by clearing; non-winners refund. Settlement does not recompute branch selection or qualification.

That settlement is why a winning bid buys more than a token transfer. The forker withdraws finalized bid results, converts purchased REP into pool ownership with auctionPoolOwnershipPerRep, and adds the matching slice of auctionedSecurityBondAllowance to the bidder's vault. Any auction that purchases REP assigns the complete unmigrated allowance among its winners; a zero-REP auction assigns none. Allowance uses the same fixed cumulative retained-ETH ordering, so its integer remainder also has a deterministic recipient independent of claim order. Because REP and allowance use independent integer numerators, a winning dust bid whose REP share rounds to zero can still receive a positive allowance share; settlement credits that allowance without requiring positive REP in the same claim. Finalization rejects explicit repair contributions. In other words, the bidder is buying a share of the repaired child-pool vault state, including the debt-bearing allowance that still has to be backed.

Before a winner claims, its reserved auction allowance remains part of the child's fork balance sheet but is not assigned to a responsible vault. It therefore does not accrue fees, affect retention utilization, or secure newly minted complete sets. Claim settlement adds that allowance to the bidder vault and to feeEligibleSecurityBondAllowance atomically.

Zero-migration full-cap ownership. When no vault REP migrated into the child and the auction sells every auctionable REP, there is no live child owner behind the parent's inherited denominator. The forker therefore sets auctionPoolOwnershipPerRep to PRICE_PRECISION and sets the final denominator to repAvailable * PRICE_PRECISION. Each winner's ownership then round-trips through poolOwnershipToRep to its full purchased REP, and later direct REP deposits join the same live scale. When REP remains unsold behind an inherited denominator, the forker instead derives the ownership rate from that residual REP.

Zero-effective-price ticks are rejected when bids are submitted, so underfunded settlement cannot assign REP to bids whose rounded ETH/REP price is zero.

Both finalized allocation paths derive each bid's REP from its fixed cumulative winning-ETH position. The bid receives the difference between rounded cumulative REP after and before that position. The companion allowance allocation differences cumulative floors over the same position. This reconciles paged withdrawals to both aggregate allocations while making rounding independent of claim order.

Interactive Clearing Example

The slider example shows the uniform-clearing path, the underfunded path, and how the same bid ladder reacts when the ETH cap, REP cap, and bid sizes change.

Try a simple auction clearing run

The simplified calculator visualizes both finalized branches with three fixed price levels and real-number arithmetic. The clearing section specifies the contract's exact tick and cumulative-floor behavior.

Interactive Demand CurveThe stepped curve accumulates bids from the highest limit price to the lowest. The vertical rule is available REP, the horizontal rule is the current clearing or qualification price, and colored bid points distinguish accepted demand from demand below the boundary. The chart updates from the auction controls directly above it.

Modeuniform clearing near 3 ETH/REP
Binding conditionboth caps
ETH retained12 ETH
Winning ETH keptnot underfunded
Thresholdnot underfunded
Approximate Alice REP1.00 REP
Approximate Bob REP1.33 REP
Approximate Carol REP1.67 REP
Total REP allocated4 REP
Refunds1.00 ETH