Settlement
Settlement is the process of resolving RFQ theses and distributing payouts based on oracle price data.
Settlement Outcomes
| Outcome | Condition | Winner | Payout |
|---|---|---|---|
| User Wins | No breach during observation | User | Full payout |
| Solver Wins | Breach detected | Solver | Full 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.upperSettlement 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.
Why Settler?
| In-Block Settlement | Settler-Based |
|---|---|
| Runs every block | Runs independently |
| Blocks block production | Non-blocking |
| All theses scanned | Event-driven |
| Sync with block time | Real-time detection |
| Single point of failure | Per-pair isolation |
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/USDTTiming
:::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.
:::
- Observation Period (0-60s): Breach can occur
- Challenge Window (optional, 60-80s): Final settlement window
| Phase | Duration | Settler Action |
|---|---|---|
| Before start_time | Variable | Monitor, no settlement |
| Observation | end_time - start_time | Check for breach |
| Challenge window | challenge_deadline - end_time | Wait or submit (if > 0) |
| After deadline | - | Submit UserWins if no breach |
Error Handling
| Scenario | Settler Action | Core Response |
|---|---|---|
| Breach detected | Submit SolverWins | Verify & settle |
| Deadline passed | Submit UserWins | Verify & settle |
| Invalid breach | Core rejects | TX fails |
| Thesis already settled | Skip | TX fails gracefully |
| Settler offline | Theses remain active | Manual settlement possible |
Manual Settlement
If the settler is unavailable, theses can still be settled manually:
# Using Kata CLI
kata rfq settle --thesis-id 42This submits a regular RfqSettle transaction which settles the thesis using Core's verification logic.
