Skip to main content

Overview

Elevation Groups (also called E-Mode or Efficiency Mode) are isolated risk tiers within Kamino Lending that allow for higher leverage and capital efficiency when borrowing and depositing correlated assets. Each elevation group has its own risk parameters and borrow limits.

ElevationGroup Structure

Defined in state/lending_market.rs:363:
pub struct ElevationGroup {
    pub max_liquidation_bonus_bps: u16,  // Max liquidation bonus (basis points)
    pub id: u8,                           // Group ID (1-32, 0 = none)
    pub ltv_pct: u8,                      // Loan-to-Value percentage
    pub liquidation_threshold_pct: u8,    // Liquidation threshold percentage
    pub allow_new_loans: u8,              // Whether new loans are allowed
    pub max_reserves_as_collateral: u8,   // Max number of collateral reserves
    pub padding_0: u8,
    pub debt_reserve: Pubkey,             // Primary debt reserve for this group
    pub padding_1: [u64; 4],
}
Key Fields:
  • id: Unique identifier (1-32). ELEVATION_GROUP_NONE (0) means no group
  • ltv_pct: Maximum loan-to-value ratio (e.g., 90 = 90%)
  • liquidation_threshold_pct: Liquidation threshold (e.g., 93 = 93%)
  • debt_reserve: The reserve that must be borrowed in this group
  • max_reserves_as_collateral: Limits collateral diversity
  • allow_new_loans: Enables/disables new borrowing

Lending Market Configuration

Storage (state/lending_market.rs:98):
pub struct LendingMarket {
    // ...
    pub elevation_groups: [ElevationGroup; 32],  // Support up to 32 groups
    pub elevation_group_padding: [u64; 90],
    // ...
}
Each lending market can have up to 32 elevation groups. Default Configuration (state/lending_market.rs:389):
impl Default for ElevationGroup {
    fn default() -> Self {
        let mut default = Self::zeroed();
        default.max_reserves_as_collateral = u8::MAX;  // No limit by default
        default
    }
}

Managing Elevation Groups

Getting an Elevation Group

Implementation (state/lending_market.rs:274):
pub fn get_elevation_group(&self, id: u8) -> Result<Option<&ElevationGroup>> {
    if id == ELEVATION_GROUP_NONE {
        Ok(None)
    } else {
        Ok(Some(
            self.elevation_groups
                .get(id as usize - 1)
                .ok_or(LendingError::InvalidElevationGroup)?,
        ))
    }
}
Note: Group ID is 1-indexed in the user-facing API, but 0-indexed in the array.

Setting an Elevation Group

Implementation (state/lending_market.rs:286):
pub fn set_elevation_group(&mut self, elevation_group: ElevationGroup) -> Result<()> {
    // Cannot set ID 0 (reserved for ELEVATION_GROUP_NONE)
    if elevation_group.id == ELEVATION_GROUP_NONE {
        return err!(LendingError::InvalidElevationGroupConfig);
    }
    
    self.elevation_groups[elevation_group.get_index()] = elevation_group;
    Ok(())
}
Helper (state/lending_market.rs:402):
pub fn get_index(&self) -> usize {
    self.id as usize - 1  // Convert 1-indexed ID to 0-indexed array
}

Checking if New Loans are Allowed

// state/lending_market.rs:398
pub fn new_loans_disabled(&self) -> bool {
    self.allow_new_loans == 0
}

Borrow Limits per Elevation Group

Elevation groups enforce strict borrowing rules:

Same Elevation Group Check

Implementation (lending_market/lending_operations.rs:246):
check_same_elevation_group(obligation, borrow_reserve)?;
When borrowing, the obligation’s elevation group must match the reserve’s elevation group.

Borrowing Enabled Checks

Elevation Group Borrowing (lending_market/lending_operations.rs:248):
check_elevation_group_borrowing_enabled(lending_market, obligation)?;
Non-Elevation Group Borrowing (lending_market/lending_operations.rs:249):
check_non_elevation_group_borrowing_enabled(obligation)?;
These functions verify that borrowing is allowed for the obligation’s elevation group configuration.

Debt Tracking

Reserve Fields (state/reserve.rs:92):
pub struct Reserve {
    // ...
    pub borrowed_amount_outside_elevation_group: u64,
    pub borrowed_amounts_against_this_reserve_in_elevation_groups: [u64; 32],
    // ...
}
Reserves track borrowed amounts both inside and outside elevation groups. Update on Borrow (lending_market/lending_operations.rs:357):
let elevation_group = lending_market.get_elevation_group(obligation.elevation_group)?;
utils::update_elevation_group_debt_trackers_on_borrow(
    borrow_amount,
    obligation,
    borrow_index,
    elevation_group,
    &borrow_reserve_pk,
    borrow_reserve,
    deposit_reserves_iter,
)?;

disable_usage_as_collateral_outside_emode

This reserve-level setting controls whether a reserve can be used as collateral outside of elevation groups. Configuration (state/reserve.rs:1377):
pub struct ReserveConfig {
    // ...
    pub disable_usage_as_coll_outside_emode: u8,
    // ...
}

Enforcement

During Deposit (lending_market/lending_operations.rs:414):
if deposit_reserve.config.disable_usage_as_coll_outside_emode > 0
    && obligation.elevation_group == ELEVATION_GROUP_NONE
    && obligation.borrow_factor_adjusted_debt_value_sf > 0
{
    msg!("Deposit reserve is disabled for usage as collateral outside elevation group");
    return err!(LendingError::DepositDisabledOutsideElevationGroup);
}
Conditions:
  1. Reserve has disable_usage_as_coll_outside_emode enabled
  2. Obligation is not in an elevation group (ELEVATION_GROUP_NONE)
  3. Obligation has existing debt
Result: Cannot deposit this asset as collateral unless you’re in an elevation group.

Use Case

This is typically used for correlated assets like:
  • LSTs (Liquid Staking Tokens) that should only be used with stablecoin borrowing
  • Stablecoins that should only be borrowed against similar stablecoins
  • Wrapped assets that should only be used within their ecosystem
Example Configuration:
// wSOL reserve configuration
const wsolReserve = {
  // ...
  disable_usage_as_coll_outside_emode: 1,  // Must be in elevation group
};

// Elevation Group 1: SOL/mSOL group
const solGroup = {
  id: 1,
  ltv_pct: 90,
  liquidation_threshold_pct: 93,
  debt_reserve: wsolReserve,
  // Users can deposit mSOL and borrow wSOL at high LTV
};

Configuration Examples

Example 1: Stablecoin Elevation Group

const stablecoinGroup: ElevationGroup = {
  id: 1,
  ltv_pct: 98,                          // 98% LTV for correlated stables
  liquidation_threshold_pct: 99,        // 99% liquidation threshold
  max_liquidation_bonus_bps: 100,       // 1% max liquidation bonus
  allow_new_loans: 1,                    // Enabled
  max_reserves_as_collateral: 3,         // Max 3 different stables as collateral
  debt_reserve: usdcReservePubkey,       // Must borrow USDC
  padding_0: 0,
  padding_1: [0, 0, 0, 0],
};

// Allowed scenario:
// - Deposit USDT, USDC, DAI as collateral (3 reserves)
// - Borrow USDC up to 98% LTV
// - Low liquidation risk due to asset correlation

Example 2: LST (Liquid Staking Token) Group

const lstGroup: ElevationGroup = {
  id: 2,
  ltv_pct: 90,                           // 90% LTV
  liquidation_threshold_pct: 93,         // 93% liquidation threshold
  max_liquidation_bonus_bps: 500,        // 5% max liquidation bonus
  allow_new_loans: 1,
  max_reserves_as_collateral: 255,       // No limit
  debt_reserve: solReservePubkey,        // Must borrow SOL
  padding_0: 0,
  padding_1: [0, 0, 0, 0],
};

// Allowed scenario:
// - Deposit mSOL, stSOL, jitoSOL as collateral
// - Borrow SOL up to 90% LTV
// - Efficient capital use for SOL derivatives

Example 3: Isolated Lending Pool

const isolatedGroup: ElevationGroup = {
  id: 3,
  ltv_pct: 50,                           // Conservative 50% LTV
  liquidation_threshold_pct: 60,         // 60% liquidation threshold
  max_liquidation_bonus_bps: 1500,       // 15% max liquidation bonus
  allow_new_loans: 1,
  max_reserves_as_collateral: 1,         // Only 1 collateral type
  debt_reserve: volatileTokenReserve,    // Borrow volatile asset
  padding_0: 0,
  padding_1: [0, 0, 0, 0],
};

// Allowed scenario:
// - Deposit single collateral type
// - Borrow volatile asset
// - Isolated from other groups to limit contagion risk

Setting Elevation Group via Update Config

Update Mode (lending_market/lending_operations.rs:2284):
UpdateConfigMode::UpdateDisableUsageAsCollOutsideEmode => {
    config_items::for_named_field!(&mut reserve.config.disable_usage_as_coll_outside_emode)
        .validating(validations::check_valid_bool_u8)
        .set(value)?;
}
Market owners can update this configuration through the update_reserve_config instruction.

Working with Elevation Groups

Entering an Elevation Group

Users enter elevation groups by:
  1. Depositing collateral from reserves in the group
  2. Borrowing from the group’s debt_reserve
The obligation automatically joins the elevation group.

Constraints While in a Group

Collateral Restrictions (lending_market/lending_operations.rs:422):
check_same_elevation_group(obligation, deposit_reserve)?;
Can only deposit collateral from reserves in the same elevation group. Borrow Restrictions (lending_market/lending_operations.rs:246):
check_same_elevation_group(obligation, borrow_reserve)?;
Can only borrow from reserves in the same elevation group.

Exiting an Elevation Group

To exit:
  1. Repay all borrows
  2. Withdraw all collateral
  3. Obligation returns to ELEVATION_GROUP_NONE

Checking Elevation Group Status

const checkObligationElevationGroup = async (obligation: PublicKey) => {
  const obligationData = await loadObligation(obligation);
  
  if (obligationData.elevationGroup === 0) {
    console.log("Obligation not in any elevation group");
  } else {
    console.log(`Obligation in elevation group: ${obligationData.elevationGroup}`);
    
    const lendingMarketData = await loadLendingMarket(lendingMarket);
    const group = lendingMarketData.elevationGroups[obligationData.elevationGroup - 1];
    
    console.log(`LTV: ${group.ltvPct}%`);
    console.log(`Liquidation Threshold: ${group.liquidationThresholdPct}%`);
    console.log(`Max Collateral Reserves: ${group.maxReservesAsCollateral}`);
  }
};

Best Practices

  1. Asset Correlation: Only group highly correlated assets (e.g., stablecoins, LSTs)
  2. Risk Parameters: Set conservative LTV/liquidation thresholds based on historical price volatility
  3. Collateral Limits: Use max_reserves_as_collateral to limit risk from too many collateral types
  4. Isolation: Use elevation groups to isolate risky assets from the main lending pool
  5. Monitoring: Track debt levels per elevation group to identify concentration risk

Advanced: Debt Tracking Across Groups

Reserves maintain separate debt counters for each elevation group:
// state/reserve.rs:92-96
pub borrowed_amount_outside_elevation_group: u64,
pub borrowed_amounts_against_this_reserve_in_elevation_groups: [u64; 32],
This enables:
  • Per-group borrow limits: Limit exposure to specific elevation groups
  • Risk isolation: Track how much is borrowed in vs out of elevation groups
  • Utilization monitoring: Understand which groups are driving reserve utilization

Errors

InvalidElevationGroup:
  • Elevation group ID doesn’t exist
  • ID must be 1-32
InvalidElevationGroupConfig:
  • Attempting to set ID 0 (reserved)
  • Invalid configuration parameters
DepositDisabledOutsideElevationGroup:
  • Reserve requires elevation group membership
  • Enable disable_usage_as_coll_outside_emode
ElevationGroupBorrowLimitExceeded:
  • Borrowing would exceed group’s limits
  • Check available capacity in the group

Build docs developers (and LLMs) love