Skip to main content

Documentation Index

Fetch the complete documentation index at: https://kamino.com/docs/llms.txt

Use this file to discover all available pages before exploring further.

When something goes wrong, such as a bad oracle, suspicious activity, or an incident, the curator has a graduated set of switches to limit damage while a fix is prepared. This page covers each switch, when to use it, and what’s still possible after it’s flipped. The emergency switches range from softest (pause new borrows) to hardest (freeze the market permanently). Each is a single flag flip on LendingMarket or ReserveConfig, applied via the standard config-update flow.

The graduated controls

SeverityControlEffectReversible
Lowborrow_disabled (market)New borrows revert. Existing borrows, deposits, withdrawals, repayments continueYes
Low-midutilizationLimitBlockBorrowingAbovePct (reserve)Borrows above utilization threshold revertYes
Midblock_price_usage: 1 (reserve tokenInfo)Any operation needing this reserve’s price reverts. Market continues; affected reserve is offlineYes
Midemergency_mode: 1 (reserve ReserveConfig)Per-reserve emergency mode. Reserve operations are halted; other reserves unaffectedYes
Mid-highprice_triggered_liquidation_disabled: 1 (market)All price-triggered liquidations market-wide are blocked. Standard non-price flows continueYes
Highemergency_mode: 1 (market)Market-wide emergency mode. Operations are restricted across all reservesYes
Highestimmutable: 1 (market)All curator-side updates are permanently disabled. The market is frozen at its current stateNo

Flip emergency flags via SDK

All emergency controls are single-field updates on LendingMarket or ReserveConfig. Use the standard market or reserve update flow.

Market-level flags

import { KaminoManager, DEFAULT_RECENT_SLOT_DURATION_MS, PROGRAM_ID, MarketWithAddress } from '@kamino-finance/klend-sdk';
import { LendingMarket } from '@kamino-finance/klend-sdk/dist/@codegen/klend/accounts';
import { address } from '@solana/kit';

const marketState = await LendingMarket.fetch(rpc, marketAddress);
if (!marketState) throw new Error('Market not found');
const marketWithAddress: MarketWithAddress = { address: marketAddress, state: marketState };

const newLendingMarket = { ...marketState };

// Examples — pick the flag that matches the situation:
newLendingMarket.borrowDisabled = 1;                          // pause new borrows
newLendingMarket.priceTriggeredLiquidationDisabled = 1;       // pause price-triggered liquidations
newLendingMarket.emergencyMode = 1;                            // market-wide emergency
newLendingMarket.emergencyCouncil = address('<EMERGENCY_PUBKEY>');
// newLendingMarket.immutable = 1;                              // **IRREVERSIBLE** — only set when winding down

const ixs = kaminoManager.updateLendingMarketIxs(adminSigner, marketWithAddress, newLendingMarket);
// submit each ix in order

Reserve-level flags

import { Reserve } from '@kamino-finance/klend-sdk/dist/@codegen/klend/accounts';

const reserve = await Reserve.fetch(rpc, reserveAddress);
if (!reserve) throw new Error('Reserve not found');

const newConfig = { ...reserve.config };

// Examples — pick the flag that matches the situation:
newConfig.emergencyMode = 1;                                   // halt this reserve
newConfig.tokenInfo = { ...newConfig.tokenInfo, blockPriceUsage: 1 };  // refuse any price-using op
newConfig.utilizationLimitBlockBorrowingAbovePct = 80;          // block borrows above 80% utilization

const ixs = await kaminoManager.updateReserveIxs(
  adminSigner,
  marketWithAddress,
  reserveAddress,
  newConfig,
);
// submit each chunk in order
To re-enable, set the flag back to 0 (or 0 on the relevant numeric field) and re-apply.

What each control does

Soft pause: stop new borrows

borrow_disabled: 1 on the market. The minimal-disruption switch. Existing positions continue accruing interest. Repayments, deposits, withdrawals work. New borrows revert with BorrowingDisabled. Use when: you’ve noticed something off and want to limit new exposure while you investigate.

Reserve-level utilization brake

utilizationLimitBlockBorrowingAbovePct: 80 on the reserve. A softer alternative to borrow_disabled for situations where you want to throttle borrowing only when the reserve is highly utilized. Borrows that would push utilization above the configured percentage revert; borrows at lower utilization continue. 0 disables.

Reserve-level disable: block price usage

tokenInfo.blockPriceUsage: 1 on the reserve. The reserve’s price feed is treated as unusable. Every operation that needs to read the price reverts. The reserve is effectively offline; the rest of the market continues normally. Use when: a specific reserve’s oracle is misbehaving and you want to halt that reserve while replacing the oracle config.

Reserve-level emergency mode

emergency_mode: 1 on the reserve. Stricter than blockPriceUsage. Halts the reserve at the program level — operations targeting the reserve revert.

Pause price-triggered liquidations

price_triggered_liquidation_disabled: 1 on the market. Blocks every liquidation triggered by oracle price movement, including obligation orders that fire on price conditions. Standard liquidations driven by manual triggers (e.g., obligation orders with Always condition) still work. Use when: an oracle is misbehaving market-wide and you want to prevent cascading liquidations on bad prices.

Market-wide emergency mode

emergency_mode: 1 on the market. The strongest reversible switch. Restricts most operations market-wide. Use only when the market needs to be brought to a near-complete stop. Pair with emergency_council to delegate emergency-only powers to a fast multisig.

Permanent freeze: immutable

immutable: 1 on the market. Once flipped to 1, the program rejects every owner-initiated config change. Existing positions continue with current parameters; no flag, threshold, cap, or oracle can be modified by anyone. Use when:
  • The market is being archived
  • You’re providing a trust-minimization commitment to depositors
  • A migration to a new market is complete and you want the old market frozen
immutable: 1 is irreversible. The program has no path to undo it. Confirm twice on a multisig review before signing.

Socialize loss (last-resort)

If a position becomes truly unrecoverable — debt exceeds collateral, even the bad-debt liquidation bonus can’t make liquidators whole — the curator can call socialize-loss to spread the loss across the affected reserve’s depositors. The mechanics:
  1. The lending_market_owner (or emergency_council if configured) calls socialize-loss for the affected obligation.
  2. The unrecoverable debt is written off.
  3. Each depositor’s claim on the reserve is reduced proportionally to their share of total deposits.
This is depositor-affecting and irreversible. Use only when:
  • The position is genuinely unrecoverable
  • Standard liquidation paths (including bad-debt liquidation) have been exhausted
  • The reserve has a credible plan for handling the impact (curator-funded backfill, treasury, etc.)

Configuring the emergency council

emergency_council: <MULTISIG_PUBKEY> on the market. Setting an emergency_council distinct from the main lending_market_owner lets you keep ordinary operations on a slow timelocked multisig while keeping incident response on a fast one. Recommended setup:
AuthorityMultisigTimelockPowers
lending_market_owner3-of-5 multisig12 hoursStandard config updates, parameter changes
emergency_council2-of-3 multisig0 or 1 hourEmergency mode, socialize-loss, fast pauses

Operational playbook

A rough decision tree for incident response:
SituationFirst action
Suspicious borrow activityborrow_disabled: 1
One reserve’s oracle looks wrongThat reserve’s blockPriceUsage: 1
Multiple oracles misbehavingprice_triggered_liquidation_disabled: 1
Smart-contract level issue or partner-asset issueemergency_mode: 1 (market-wide)
Position underwater past liquidator economicsFirst: confirm liquidators have tried. Then: socialize-loss
Winding down a deprecated marketEventually: immutable: 1
Always fix the root cause before re-enabling. Pausing then immediately unpausing without addressing the issue achieves nothing.

Reference