SecurityPool

Holds ETH collateral and REP underwriting, accounts for vaults and fees, mints shares, and routes local escalation. SecurityPool source contract

Read surface

Immutable relationship and configuration getters are questionId, universeId, initialEscalationGameDepositAttoRep, zoltar, parent, shareToken, repToken, priceOracleManagerAndOperatorQueuer, openOracle, escalationGameFactory, eventEmitter, questionData, securityPoolForker, truthAuction, securityPoolFactory, and statoblastSecurityMultiplierBps; the current game is escalationGame. The escalation deposit is derived at construction from live universe theoretical REP supply rather than copied from factory configuration. Accounting getters include totalCapacityOwnershipAttoRep, settlementCollateralAttoEth, totalRepBackingUnits, shareTokenSupplyAttoShares, securityVaults, minimumSecurityBondDebtAttoEth, minimumVaultRepDepositAttoRep, totalBadDebtAttoEth, and vaultBadDebtAttoEth. Aggregate and per-vault bad debt describe only the current collateral-claim generation; getPoolAccountingSnapshot exposes its badDebtGeneration. Exhausting the remaining claim supply clears aggregate debt and advances the generation before later collateral can be minted. Use vaultTargetBackingFactorBps for the saved vault target, initialized by the first deposit and inherited when a parent vault is configured in a child. Later deposits must use this target. Safe vault fee checkpoints reapply it when settlement collateral and dispute stake are zero and vault admission remains open. The saved target is distinct from the actual backing ratios and current vault health. Use getVaultCapacityBackingFactorsBps for current associated and pool-held REP-per-capacity ratios, getCurrentMintingCapacityAttoEth for price-converted aggregate capacity, and getVaultOpenInterestAttoEth for a vault’s live proportional obligation. Other derived and paged reads are getVaultCount, getVaults, attoSharesToAttoEth, attoEthToAttoShares, attoRepToBackingUnits, backingUnitsToAttoRep, getTotalPoolHeldAttoRep, totalAccruedFeesAttoEth, getPoolAccountingSnapshot, getFeeEpochEndTime, getVaultFeeRemainder, and isEscalationResolved. The backing-factor ratios are not current vault health: associated REP includes dispute-staked principal as at-risk security, while current health also depends on OI, REP/ETH price, the security multiplier, and both protocol constraints. The vault registry is append-only and newest-registered first. Registration requires only a nonzero address and can occur without economic state; consumers filter current positions from securityVaults, escalation stake, and bad debt. isEscalationResolved() is true when the pool inherits a fixed fork outcome, or when a local escalation game is configured and the forker routes a non-None outcome. An operational fixed-outcome child remains available for settlement and redemption but rejects new collateralized operations. getFeeEpochEndTime() returns the effective fee cutoff, or type(uint256).max for an open child epoch. A resolved child without a continuation game fixes its cutoff at activation. Lifecycle and fee getters are totalClaimableVaultFeesAttoEth, lastUpdatedFeeAccumulator, feeIndex, currentRetentionRate, awaitingForkContinuation, and systemState.

Price-sensitive withdrawal, dynamic-capacity, and liquidation calls depend on A16 timely inclusion, A21 genesis REP and WETH behavior, A19 observable correctable price, and A06 lifecycle executors. User-initiated pool calls additionally depend on A28 account authority.

State-changing interactions

Vault owners change their saved target through coordinator operation AdjustVaultBackingFactor (2), which executes immediately with a fresh price or remains in the existing on-chain queue. The pool fallback exposes adjustVaultBackingFactor(vault, backingFactorBps) through ISecurityPool only to its coordinator. Capacity becomes pool-held vault REP × statoblastSecurityMultiplierBps / backingFactorBps, rounded down, without transferring REP. The target is an absolute backing ratio in BPS and must be at least statoblastSecurityMultiplierBps (equality allowed), leaving positive capacity and a fully collateralized vault at execution. The pool must be operational, unforked, unresolved, and open to vault admission; dispute-staked REP must be zero. Capacity reductions require zero settlement collateral. Successful execution saves the target, checkpoints fees, updates retention, and emits VaultBackingFactorAdjusted plus accounting checkpoints. A queued or failed change leaves the saved target unchanged.

Transaction Caller Main prerequisites State or asset effect Primary signals
burnEscalationWinnerHaircut(amountAttoRep) This pool's EscalationGame only Caller is the configured escalation game; amount is positive and the game has already transferred enough REP to the pool. Burns the winning-deposit haircut from REP already escrowed in the game. RepBurned and ERC-20 Transfer; child REP also emits Burn
depositRepToVault(attoRepAmount, targetHealthFactorBps) Vault owner Operational and unforked; isEscalationResolved() is false; the transaction timestamp is strictly before the question end time unless the pool has an inherited fork-continuation game; deposit amount is positive; target backing ratio is at least statoblastSecurityMultiplierBps and matches any saved vault target; resulting vault REP meets the configured supply-scaled minimum and resulting capacity is positive. Prices proportional REP backing units before receiving the transfer, including authorization deposits, then credits those units. The first deposit saves the target; later deposits must match it. With no settlement collateral, capacity is recalculated from all pool-held vault backing. Otherwise, only the new deposit adds capacity at the saved factor. RepDepositedToVault, VaultDepositTargetHealthFactorRecorded, and accounting checkpoints
redeemFees(vault) Anyone; any nonzero ETH payment is always sent to vault A nonzero payment path requires vault to accept ETH. First accrues the vault's fees. If resulting claimable fees are zero, returns without payment; otherwise clears and pays the full amount. Accrual checkpoints only when accrual state changes; both VaultAccountingCheckpoint and PoolAccountingCheckpoint for a nonzero redemption; no event when fees and accrual state are unchanged
createCompleteSet() with ETH Trader Operational and unforked; isEscalationResolved() is false; not awaiting continuation; positive ETH converts to at least one complete-set unit; live oracle-priced minting capacity covers the resulting settlement collateral, not merely this deposit; for resulting collateral net of recorded bad debt, combined pool-held and dispute-staked REP satisfies the associated-REP constraint while pool-held REP alone satisfies the migration-safety constraint; any explicit unassigned auction position remains healthy after the mint; under A22 asset-recipient compatibility, a contract trader accepts onERC1155BatchReceived. Adds collateral and mints one Invalid, Yes, and No share per complete-set unit, then invokes the ERC-1155 batch-receiver callback for a contract trader. Callback rejection rolls back the ETH, pool accounting, events, and share mint. CompleteSetCreated, PoolAccountingCheckpoint, then ERC-1155 TransferBatch on a successful callback
redeemCompleteSet(amountAttoShares) Anyone; positive redemption requires the caller to hold the complete set Operational and unforked; caller holds every outcome amount requested; caller accepts the resulting ETH call, including zero value. Zero is accepted without a token balance. Burns equal balances of all three outcomes and pays amountAttoShares * settlementCollateralAttoEth / shareTokenSupplyAttoShares using the pool's remaining economic claim supply as its collateral denominator. Complete-set issuance adds to that denominator, while complete-set and winning-share redemption consume it; fork-time source entitlements materialize without changing it because their claims are already reserved. When the remaining supply reaches zero, aggregate bad debt is cleared and per-vault bad-debt reads advance to an empty collateral generation. Zero passes the token and accounting checks and follows the normal zero-value event, checkpoint, and ETH-send path; rejection of that ETH call reverts the transaction. CompleteSetRedeemed and PoolAccountingCheckpoint
redeemShares() Anyone; a positive payout requires the caller to hold winning shares Operational pool with a final outcome; caller accepts the resulting ETH call, including zero value. Burns the caller's full winning balance and pays its pro-rata remaining collateral. When the remaining supply reaches zero, aggregate bad debt is cleared and per-vault bad-debt reads advance to an empty collateral generation. A zero winning balance passes token and accounting checks and follows the normal zero-value event, checkpoint, and ETH-send path; rejection of that ETH call reverts the transaction. SharesRedeemed and PoolAccountingCheckpoint
redeemRepFromVault(vault) Vault owner; caller must equal vault, and REP is sent to vault Operational pool with a final outcome; the specified vault has no escalation escrow and has redeemable REP. Burns the vault's REP backing units and returns its proportional vault REP backing. RepRedeemedFromVault
depositToEscalationGame(outcome, maxAmount) Vault owner Question end has passed; pool operational in an unforked universe, without an inherited fixed outcome, and not awaiting continuation. On the first deposit, the live non-decision threshold must exceed one attoREP; outcome and amount accepted; the remaining vault and aggregate pool totals each preserve both live open-interest health branches; a fresh price is required when total capacity ownership is nonzero. Deploys the local game on the first deposit. The game factory uses the configured start bond while it is below the live non-decision threshold; if tracked REP supply later makes it too large, the factory uses nonDecisionThresholdAttoRep - 1 instead. Repeat deposits use the existing game's stored startBondAttoRep and nonDecisionThresholdAttoRep. Every accepted deposit removes enough REP backing units and escrows dispute-staked REP on the selected outcome. EscalationGameSet on first deposit; DepositToEscalationGame
withdrawFromEscalationGame(outcome, depositIndexes) Anyone; a nonempty list must select deposits belonging to one original depositor Game configured; operational pool; valid final outcome. If an external fork interrupted the game, parent withdrawal stays unavailable: winners settle in the child by carried proof, inherited losers require no transaction, and unresolved parent escalation-deposit accounting cleanup is optional. A nonempty list additionally requires valid local indexes and one common depositor. A nonempty list settles local deposits and pays winning REP to the immutable depositor recorded by each deposit. Liquidation cannot change that payout address. An empty list returns after the outer lifecycle checks without settlement, state change, or event. Per processed deposit, escalation-game CarryDepositConsumed; additionally ClaimDeposit for a winning payout. No event for an empty list
withdrawForkedEscalationDeposits(outcome, proofs) Anyone; a nonempty list must name one original depositor across all proofs Game configured; operational child pool; valid final outcome. A nonempty list additionally requires an initialized and fully resumed continuation game, valid unconsumed winning proofs, and one common depositor. A nonempty list verifies and consumes carried proofs, then pays winning child REP to the immutable depositor committed in each leaf. Stable continuation identities retain the creating game. Allocations follow the source-game chain and apply each auction haircut through cumulative interval rounding; read cost grows with fork depth. An empty list returns after the outer lifecycle checks without proof verification, state change, or event. Per processed proof, escalation-game CarryDepositConsumed and ClaimDeposit. No event for an empty list
updateSettlementCollateral() Anyone No caller or lifecycle restriction. It returns unchanged when the accumulator is already at or beyond the clamped timestamp. For an initial pool, accrues elapsed fees through question end while its universe remains unforked; after that universe forks, its fork timestamp replaces question end as the cutoff, including a later question-end-to-fork interval. Finalizing an activated child starts a separate epoch at that finalization timestamp, which runs until the child game resolves or the child universe subsequently forks. An inherited fixed-outcome child without a continuation game fixes its cutoff at finalization and accrues no later fees. Accumulator timestamps prevent charging an interval twice. It moves whole credited fees from settlement collateral into the unallocated accrued-fee reserve and advances the accumulator. With positive elapsed time but zero fee-eligible capacity ownership it clears denominator-specific remainder and advances the timestamp without charging fees. PoolAccountingCheckpoint whenever positive elapsed time is processed, including the zero-capacity-ownership branch; no event for an unchanged timestamp
updateRetentionRate() Anyone No caller restriction. It returns unchanged when the pool is not Operational or the calculated rate equals the stored rate. Zero live minting capacity selects the maximum retention rate. Recalculates the retention rate from current collateral and live oracle-priced minting capacity. PoolAccountingCheckpoint only when the stored retention rate changes; no event for a no-op
updateVaultFees(vault) Anyone for any address No caller, nonzero-vault, or lifecycle restriction. A vault accounting checkpoint: first updates pool accrual, then advances the vault fee index and fractional remainder, moves whole assigned fees from reserve to the vault, registers any previously unseen nonzero vault address regardless of economic state, and returns leftover reserve to settlement collateral once a forked pool has checkpointed all fee-eligible capacity ownership. It then reapplies a saved target when the pool is operational, unforked, unresolved, and open to admission, with zero settlement collateral and no vault dispute stake. Reapplying the target can change capacity and recalculate retention. Accrual PoolAccountingCheckpoint when due; VaultAccountingCheckpoint when the vault index, remainder, or claimable fee balance changes; an additional PoolAccountingCheckpoint when pool accounting changes; target synchronization also emits VaultBackingFactorAdjusted and capacity accounting checkpoints; no event when neither accrual nor vault or pool accounting changes
withdrawRepFromVault(vault, attoRepAmount) This pool's OpenOraclePriceCoordinator only Fresh coordinator price; operational pool in an unforked universe; isEscalationResolved() is false; no vault REP escrow. A withdrawal that would reduce capacity ownership requires zero settlement collateral; a backing-only withdrawal does not. The remaining vault and aggregate pool totals each meet the upward-rounded associated-REP and free-REP backing requirements, with equality healthy. Removes the requested proportional REP backing units, or all backing units when the requested remainder would fall below the REP minimum; proportionally reduces vault and pool capacity ownership when the vault has positive capacity ownership; recalculates retention; and transfers the resulting withdrawable REP to vault. REP Transfer; RepWithdrawnFromVault; VaultAccountingCheckpoint; and applicable fee-accrual or retention PoolAccountingCheckpoint events
performLiquidation(request) This pool's OpenOraclePriceCoordinator only In ABI order, request contains operationId, operator, receiverVault, targetVault, requestedDebtAttoEth, snapshot, minimumReceiverHealthFactorBps, and minLiquidationPriceDistanceBps. The nested snapshot contains only targetBackingUnits and targetCapacityOwnershipAttoRep. Fresh settled coordinator price; operational pool in an unforked universe; isEscalationResolved() is false; receiver differs from target; neither participant has current-generation bad debt. A bad-debt-bearing vault becomes eligible again after the current collateral claims are exhausted, which clears aggregate debt and advances the bad-debt generation. The two target snapshot fields must match, while execution reads current pool totals. After target and receiver fee checkpoints, the liquidation delegate requires live target backing, dispute-staked REP, and open interest to remain at least minLiquidationPriceDistanceBps beyond the liquidation threshold and requires the live target state to remain unhealthy. When debt moves, the receiver must satisfy the protocol backing checks multiplied by its approved minimum health factor, using live post-liquidation state and upward-rounded requirements; its resulting debt must meet the configured debt floor and its REP must meet the vault floor. The target resulting debt must be zero or meet the debt floor; when debt remains, target REP must meet the vault floor. Capped by the target vault's open interest and fundable REP award, a nominal debt quote selects proportional capacity ownership rounded downward and moves that ownership to the explicitly selected receiver vault. Moved security-bond debt is the receiver's exact live open-interest increase and cannot exceed the nominal quote or request. On a delegated route, the coordinator additionally bounds it by the staged approval reservation; the self-receiving route has no approval reservation. The operator only submits the transaction. Dispute-staked REP claims, accrued claimable fees, surplus vault REP backing, and unmatched ownership remain with the target. On a full-target request, target open interest minus exact moved debt is recorded as attoETH-denominated bad debt; that residual can include both an award-unfunded slice and integer-allocation residue. Receiver or target dust cannot turn otherwise funded debt into bad debt. Fee-accrual and target or receiver VaultAccountingCheckpoint events as needed; VaultLiquidated identifies operation, operator, receiver, target, moved debt, moved ownership, and bad debt; VaultBadDebtRecorded records residual target debt on a full-target request; final pool accounting checkpoint
setStartingParams(...) SecurityPoolFactory only Factory caller. The pool has no internal one-shot or lifecycle guard; the factory exposes it only through atomic deployment wiring. Sets the fee timestamp and initial question-end epoch cutoff, retention, and collateral, seeds the coordinator with zero for an origin or the parent's last price for a child, then checkpoints initialization. Coordinator RepEthPriceSet and CoordinatorStateCheckpoint, then pool PoolAccountingCheckpoint, even for zero or repeated values if the factory were to call again
activateForkMode() SecurityPoolForker only The pool has no inherited fixed outcome, so a fixed child cannot reopen for a later universe fork. There is no current-state guard otherwise. A configured game's drain must succeed or the entire activation reverts without propagating its reason data. Sets PoolForked, accrues through the fork clamp, transfers the pool's entire REP balance to the forker, then makes the pool drain its configured escalation game's entire REP balance to the forker. Repeated calls are not lifecycle-guarded and transfer any balances replenished since the prior call before repeating the checkpoints. Pool-held REP Transfer always, including at zero; configured-game REP Transfer only for a positive game balance; accrual checkpoint when due; always PoolForkModeActivated and fork-activation PoolAccountingCheckpoint
initializeForkedEscalationGame(...) SecurityPoolForker only No game is configured; downstream startFromFork parameters are valid. Deploys and starts the pool's paused fork-continuation game with inherited timing and optional fixed outcome. Escalation GameContinuedFromFork, then pool EscalationGameSet
initializeForkCarrySnapshotWithResolutionBalances(...) SecurityPoolForker only A game is configured; it is a fork continuation with no prior snapshot; leaf counts fit the MMR; supplied or computed snapshot ID matches the data. Installs the continuation game's immutable carry peaks, counts, totals, resolution balances, and normalized nullifier roots. ForkCarryCheckpoint
resumeForkedEscalationGame() Anyone Pool is operational, awaiting a configured fork continuation, and the game has not resumed. Checks the already-installed immutable carry commitment and aggregate REP funding, clears the pool wait flag, records the resume timestamp, and starts the continuation's remaining escalation clock in one bounded call. ForkContinuationResumed and AwaitingForkContinuationSet(false)
setAwaitingForkContinuation(shouldAwait) SecurityPoolForker only No lifecycle or value-change guard. Stores whether complete-set minting must wait for continuation initialization. AwaitingForkContinuationSet, including for a repeated value
setSystemState(newState) SecurityPoolForker only No transition or value-change guard. Replaces the pool lifecycle state directly. SystemStateSet, including for a repeated state
configureVault(vault, repBackingUnits, capacityOwnershipAttoRep, vaultFeeIndex, newVaultBadDebtAttoEth, newTotalBadDebtAttoEth) SecurityPoolForker only vault is nonzero; no lifecycle or value-change guard. Replaces the vault REP backing units, price-independent capacity ownership, fee index, vault bad debt, and aggregate pool bad debt, clears pooled fee-index remainder when capacity ownership changes, and registers the nonzero vault address regardless of the supplied state. Always VaultAccountingCheckpoint and PoolAccountingCheckpoint, including when all supplied values repeat current state
configureFinalizedAuctionVault(vault, repBackingUnits, capacityOwnershipAttoRep, vaultFeeIndex, newVaultBadDebtAttoEth, newTotalBadDebtAttoEth) SecurityPoolForker only vault is nonzero; the forker has already included the sold capacity ownership in the pool fee denominator at auction finalization. Assigns finalized-auction REP backing and already-fee-eligible capacity ownership to the winning vault without clearing the pool-wide fee-index remainder, then replaces the vault fee index, bad debt, and aggregate pool bad debt and registers the vault. Always VaultAccountingCheckpoint and PoolAccountingCheckpoint, including when all supplied values repeat current state
setTotalRepBackingUnits(newDenominator) SecurityPoolForker only No lifecycle or value-change guard. Replaces the REP backing units denominator. TotalRepBackingUnitsSet, including for zero or a repeated value
setTotalSharesAttoShares(newTotalSharesAttoShares) SecurityPoolForker only No lifecycle or value-change guard. Replaces stored shareTokenSupplyAttoShares, the denominator used by attoSharesToAttoEth and complete-set redemption. ShareTokenSupplySet, including for zero or a repeated value
setPoolFinancials(newSettlementCollateralAttoEth, newTotalCapacityOwnershipAttoRep, newFeeEligibleCapacityOwnershipAttoRep, newTotalBadDebtAttoEth) SecurityPoolForker only Fee-eligible capacity ownership does not exceed total capacity ownership. Actual ETH covers the supplied collateral plus all accrued fee liabilities. Installing inherited liabilities does not require current REP solvency. Replaces settlement collateral, both price-independent capacity-ownership totals, and aggregate pool bad debt, resets the fee timestamp to the current block, opens the finalized child fee epoch unless the child has a fixed outcome and no continuation game, and clears fee-index rounding carry. PoolAccountingCheckpoint, including for repeated financial values
authorizeChildPool(pool) SecurityPoolForker only This parent pool is already authorized; candidate reports this share token; candidate universe has no different canonical pool. No pool-lifecycle guard. Asks the lineage share token to establish pool as the canonical authorized pool for its universe; reauthorizing the same pool is a no-op. AuthorizationUpdated only on first authorization; no event when already authorized
transferEth(receiver, amountAttoEth) SecurityPoolForker only Fee liabilities are covered; amount fits both unreserved pool ETH and tracked settlement collateral; receiver accepts the ETH call, including zero value. Reduces tracked settlement collateral by amount, checkpoints the reconciliation, and calls receiver with that ETH. At zero amount it reduces no settlement collateral but still emits the checkpoint and performs a zero-value call; callback rejection rolls back the transaction and checkpoint. PoolAccountingCheckpoint, including at zero amount; no dedicated ETH-transfer event
assignFinalizedAuctionFees(vault, amountAttoRep, auctionFeeIndexAtFinalization) SecurityPoolForker only The supplied finalization index does not exceed the current fee index; the forker credit workflow has already checkpointed the vault and added the claimed capacity ownership. Assigns to the claiming vault the fees its auctioned capacity ownership accrued from truth-auction finalization through the current fee index, combines fractional carry with the vault remainder, and removes whole assigned fees from the unallocated reserve. The ownership was already included in the fee denominator and total ownership at finalization. This call preserves aggregate fees and settlement collateral; after every eligible ownership is reconciled on a forked pool, a subsequent permissionless updateVaultFees checkpoint returns any reserve that no vault can individually claim to settlement collateral. VaultAccountingCheckpoint and auction-claim PoolAccountingCheckpoint; the calling forker emits ClaimAuctionProceeds only after the broader credit workflow completes
Direct ETH transfer to receive() Forker, this pool's Truth Auction, or parent pool only Sender is one of the three authorized protocol addresses. Forced ETH bypasses this ordinary-call guard. Accepts protocol-routed ETH used by migration and auction settlement. Forced ETH remains raw, unaccounted surplus rather than settlement collateral or fees. No dedicated receive event; the calling protocol step emits its own event

Accounting examples

Retention rate and annualized fee

SecurityPoolUtils.calculateRetentionRate returns a per-second retention factor scaled by PRICE_PRECISION (1e18). It equals MAX_RETENTION_RATE at zero utilization, declines linearly until utilization reaches RETENTION_RATE_DIP (80% of live minting capacity), and stays at MIN_RETENTION_RATE above that. Zero live minting capacity selects MAX_RETENTION_RATE.

Clients annualize a retention rate for display as follows; the contract never computes this value.

annualFee=1(retentionRatePRICE_PRECISION)SECONDS_PER_YEAR

Liquidation transfer rounding

A liquidation request is an ETH-denominated debt amount. Execution caps it at the target vault's live debt and at the largest amount whose complete REP award (LIQUIDATION_REP_BONUS_BPS = 500, so 5%) the target can fund. The proportional capacity ownership that leaves the target is rounded down; the REP backing units that leave it are rounded up. The receiver incurs exactly the reported debt increase and receives those ownership and backing units. Target claims, fees, surplus, and unmatched ownership remain with the target.

Only a request covering the target's full position records the untransferable remainder as target-local bad debt. Partial requests leave the remainder as ordinary target debt. After the transfer the receiver must remain healthy and any remaining target position must still meet minimumSecurityBondDebtAttoEth and minimumVaultRepDepositAttoRep, so a liquidation cannot leave unusable dust.