Settler
The Settler is a dedicated service that handles thesis settlements for Kaizen Core. Instead of running settlement logic in the block production loop, settlements are offloaded to this service which monitors theses and submits settlement transactions.
Why Settler?
| Approach | Block Production | Settlement Latency | Scalability |
|---|---|---|---|
| In-block (old) | Blocked during settlement | Coupled to block time | Limited |
| Settler (current) | Unblocked | Independent | Per-pair parallelizable |
Benefits:
- Block producer only processes transactions
- Streams events for breach detection
- Can run separate settlers per trading pair
- One pair's issues don't affect others
Architecture
Running
# Build
cargo build --release -p kaizen-settler
# All pairs (single settler)
./target/release/settler --write-node 127.0.0.1:9000
# Specific pair only
./target/release/settler --pairs BTC/USDT
# Multiple pairs
./target/release/settler --pairs BTC/USDT,ETH/USDT
# Multiple settlers for different pairs
./target/release/settler --pairs BTC/USDT &
./target/release/settler --pairs ETH/USDT &
./target/release/settler --pairs SOL/USDT &CLI Options
| Option | Description | Default |
|---|---|---|
-w, --write-node <addr> | Write node sync address | 127.0.0.1:9000 |
-r, --rpc-url <url> | Kaizen Core RPC URL | http://127.0.0.1:8545 |
-k, --private-key <key> | Settler private key (hex, without 0x) | From env |
-p, --pairs <pairs> | Comma-separated pairs (e.g., BTC/USDT,ETH/USDT). Empty for all | Empty (all) |
--check-interval <ms> | Settlement check interval | 100 |
--batch-size <n> | Max settlements per batch | 100 |
--deadline-buffer <ms> | Buffer added to challenge deadline before UserWins settlement | 200 |
--metrics-port <port> | Metrics HTTP server port (0 to disable) | 9090 |
--data-dir <path> | Data directory for persistent state | From env |
--json-logs | Output logs in JSON format | false |
Environment Variables
| Variable | Description |
|---|---|
SETTLER_PRIVATE_KEY | Settler private key (hex, without 0x) |
SETTLER_PAIRS | Comma-separated pairs to handle (empty = all) |
SETTLER_DATA_DIR | Data directory for persistent state |
Per-Pair Deployment
Run separate settler instances for each trading pair:
# Terminal 1: BTC/USDT settler
SETTLER_PAIRS=BTC/USDT ./target/release/settler
# Terminal 2: ETH/USDT settler
SETTLER_PAIRS=ETH/USDT ./target/release/settler
# Terminal 3: SOL/USDT settler
SETTLER_PAIRS=SOL/USDT ./target/release/settlerAdvantages:
- BTC settler crash doesn't affect ETH settlements
- High-volume pairs can get dedicated resources
- Separate logs per pair
- Update one pair's settler without downtime
Settlement Flow
Settlement Types
User Wins
When the observation period ends without any price breach:
SystemSettleTx {
thesis_id: 42,
settlement_type: SystemSettlementType::UserWins,
}- Submitted after
thesis.challenge_deadlinepasses - User receives full payout
- No breach evidence required
Solver Wins
When a price breach is detected during the observation period:
SystemSettleTx {
thesis_id: 42,
settlement_type: SystemSettlementType::SolverWins {
breach_timestamp: 1700000500000,
breach_price: 94500000000, // Below lower bound
},
}- Submitted immediately upon breach detection
- Solver receives full payout
- Breach evidence (timestamp + price) is verified by Core
Breach Detection
The settler maintains a price cache and checks each active thesis against historical prices using parallel iteration:
// Parallel breach detection with rayon
let decisions: Vec<SettlementDecision> = theses
.par_iter()
.filter_map(|thesis| {
if let Some((breach_ts, breach_price)) = find_breach(thesis, &state, now) {
return Some(SettlementDecision::SolverWins { ... });
}
if now >= thesis.challenge_deadline {
return Some(SettlementDecision::UserWins { ... });
}
None
})
.collect();Key Points:
- Price data is cached for the observation period (5 minutes)
- Binary search for price lookup (O(log n))
- Checks at 100ms intervals (matches oracle granularity)
- Early exit on first breach
Core Integration
SystemSettle Transaction
The settler submits SystemSettle transactions which are processed by Kaizen Core:
pub struct SystemSettleTx {
pub thesis_id: u64,
pub settlement_type: SystemSettlementType,
}
pub enum SystemSettlementType {
UserWins,
SolverWins {
breach_timestamp: u64,
breach_price: u64,
},
}Permission Model
The settler uses a special Settler permission:
| Permission | Who | What |
|---|---|---|
| User | Anyone | Trade, transfer |
| Admin | Operators | Configuration |
| Relayer | Bridge | Deposits/withdrawals |
| Feeder | Oracle | Price feeds |
| Settler | Settler | Settlement only |
The settler address must be configured in Core's global config.
Breach Verification
When processing SolverWins, Core verifies:
- Thesis exists and is active
- Breach timestamp is within observation period
- Breach price exists in oracle history
- Breach price is outside price range
This prevents invalid settlement claims.
Token Addresses
Default token address mappings:
| Symbol | Address |
|---|---|
| BTC | 0x0000000000000000000000000000000000000001 |
| ETH | 0x0000000000000000000000000000000000000002 |
| SOL | 0x0000000000000000000000000000000000000003 |
| USDT | 0x0000000000000000000000000000000000000100 |
Error Handling
| Error | Action |
|---|---|
| Node disconnected | Retry connection, resume from last state |
| Settlement rejected | Log error, continue with next thesis |
| Invalid breach | Log warning, skip thesis |
Deployment
Docker
# All pairs
docker run -d \
--name kaizen-settler \
-e SETTLER_PRIVATE_KEY=... \
kaizen/settler:latest \
--write-node kaizen-node:9000
# Specific pair
docker run -d \
--name kaizen-settler-btc \
-e SETTLER_PRIVATE_KEY=... \
-e SETTLER_PAIRS=BTC/USDT \
kaizen/settler:latest \
--write-node kaizen-node:9000Docker Compose
services:
settler-btc:
image: kaizen/settler:latest
command: --write-node kaizen-node:9000
environment:
- SETTLER_PRIVATE_KEY=${SETTLER_PRIVATE_KEY}
- SETTLER_PAIRS=BTC/USDT
depends_on:
- kaizen-node
restart: unless-stopped
settler-eth:
image: kaizen/settler:latest
command: --write-node kaizen-node:9000
environment:
- SETTLER_PRIVATE_KEY=${SETTLER_PRIVATE_KEY}
- SETTLER_PAIRS=ETH/USDT
depends_on:
- kaizen-node
restart: unless-stopped
settler-sol:
image: kaizen/settler:latest
command: --write-node kaizen-node:9000
environment:
- SETTLER_PRIVATE_KEY=${SETTLER_PRIVATE_KEY}
- SETTLER_PAIRS=SOL/USDT
depends_on:
- kaizen-node
restart: unless-stopped