Skip to content

Settlement

Settlement is the process of resolving RFQ theses and distributing payouts based on oracle price data.

Settlement Outcomes

OutcomeConditionWinnerPayout
User WinsNo breach during observationUserFull payout
Solver WinsBreach detectedSolverFull payout

Breach Definition

A breach occurs when the oracle price exits the thesis's price range at any point during the observation period:

Breach = price < priceRange.lower OR price > priceRange.upper

Settlement Architecture

Kaizen uses a settler-based architecture where settlements are handled by an external Settler service via SystemSettle transactions, rather than inline with block production. This keeps block production fast and decoupled. Settlers can be deployed per-pair for fault isolation and scaling.

Settlement Architecture

Why Settler?

In-Block SettlementSettler-Based
Runs every blockRuns independently
Blocks block productionNon-blocking
All theses scannedEvent-driven
Sync with block timeReal-time detection
Single point of failurePer-pair isolation

Settlement Flow

Settlement Flow

SystemSettle Transaction

The settler submits SystemSettle transactions to settle theses:

pub enum TxPayload {
    // ... existing payloads ...
 
    /// System-initiated settlement
    SystemSettle(SystemSettleTx),
}
 
pub struct SystemSettleTx {
    pub thesis_id: u64,
    pub settlement_type: SystemSettlementType,
}
 
pub enum SystemSettlementType {
    /// User wins - no breach found
    UserWins,
    /// Solver wins - breach detected
    SolverWins {
        breach_timestamp: u64,
        breach_price: u64,
    },
}

Permission

Only addresses with the Settler permission can submit SystemSettle transactions:

impl Payload for TxPayload {
    fn required_permission(&self) -> Permission {
        match self {
            TxPayload::SystemSettle(_) => Permission::Settler,
            // ...
        }
    }
}

Breach Verification

When processing SolverWins, Core verifies the breach claim:

fn verify_breach(
    thesis: &RfqThesis,
    breach_timestamp: u64,
    breach_price: u64,
    oracle: &OracleService,
) -> Result<(), SettlementError> {
    // 1. Check timestamp is within observation period
    if breach_timestamp < thesis.start_time || breach_timestamp > thesis.end_time {
        return Err(SettlementError::InvalidBreachTimestamp);
    }
 
    // 2. Verify price exists in oracle history
    let historical_price = oracle.get_historical_price(
        &thesis.oracle_pair,
        breach_timestamp,
    )?;
 
    if historical_price.price != breach_price {
        return Err(SettlementError::PriceMismatch);
    }
 
    // 3. Verify price is outside range
    if thesis.price_range.contains(breach_price) {
        return Err(SettlementError::NotABreach);
    }
 
    Ok(())
}

This prevents:

  • False timestamps: Claiming breach at invalid time
  • Price manipulation: Claiming false breach prices
  • Invalid claims: Prices that are actually within range

Settlement Events

RfqSettled Event

Event::RfqSettled {
    thesis_id: u64,
    user: Address,           // Original user (for OI tracking)
    solver: Address,         // Original solver
    winner: Address,         // User or Solver
    oracle_pair: OraclePair, // For OI tracking
    bet_amount: u64,         // For OI tracking
    payout: u64,
    status: ThesisStatus,    // SettledUserWin or SettledSolverWin
    breach_timestamp: u64,   // 0 if user wins
    breach_price: u64,       // 0 if user wins
}

The event includes full thesis context (user, solver, oracle_pair, bet_amount) to enable proper OI (Open Interest) tracking during event sourcing reconstruction.

Per-Pair Deployment

Settlers can be deployed per trading pair for:

  • Fault isolation: One pair's issues don't affect others
  • Independent scaling: High-volume pairs get dedicated resources
  • Easier debugging: Separate logs and metrics per pair
# BTC/USDT settler
./target/release/settler --pairs BTC/USDT
 
# ETH/USDT settler
./target/release/settler --pairs ETH/USDT

Timing

:::info Default Configuration By default, challenge_window_ms = 0 (instant settlement). This means theses settle immediately at end_time without a challenge window. Configure challenge_window_ms in the global config if you need a challenge period. :::

Thesis Timeline:
  • Observation Period (0-60s): Breach can occur
  • Challenge Window (optional, 60-80s): Final settlement window
PhaseDurationSettler Action
Before start_timeVariableMonitor, no settlement
Observationend_time - start_timeCheck for breach
Challenge windowchallenge_deadline - end_timeWait or submit (if > 0)
After deadline-Submit UserWins if no breach

Error Handling

ScenarioSettler ActionCore Response
Breach detectedSubmit SolverWinsVerify & settle
Deadline passedSubmit UserWinsVerify & settle
Invalid breachCore rejectsTX fails
Thesis already settledSkipTX fails gracefully
Settler offlineTheses remain activeManual settlement possible

Manual Settlement

If the settler is unavailable, theses can still be settled manually:

# Using Kata CLI
kata rfq settle --thesis-id 42

This submits a regular RfqSettle transaction which settles the thesis using Core's verification logic.