Chō Markets

Cho Markets Documentation

Overview

Cho Markets is an isolated lending protocol for Solana. Each market is a separate lending pool with its own loan token, collateral token, oracle, and risk parameters.

Program ID: 5LYM6PdjBQ9Fr6Y3PV5aFBfxVCncup41ZGEQ2q73tm4F

Name

The name Cho is a precise romanization of the Japanese cho, a high-utility signifier for transformation (蟼), transcendence (超), and the summit (頂).

Architecture

┌─────────────────────────────────────────────────────────┐
│                    Cho Markets Program                   │
│  Program ID: 5LYM6PdjBQ9Fr6Y3PV5aFBfxVCncup41ZGEQ2q73tm4F │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │    Market    │  │    Market    │  │    Market    │  │
│  │  USDC/SOL    │  │  USDC/PYUSD  │  │  EURC/SOL    │  │
│  │  LLTV: 80%   │  │  LLTV: 90%   │  │  LLTV: 75%   │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
│         │                  │                  │         │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │   Position   │  │   Position   │  │   Position   │  │
│  │  (per user)  │  │  (per user)  │  │  (per user)  │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
│                                                         │
└─────────────────────────────────────────────────────────┘

Accounts

Market

PDA: ["market", loan_token, collateral_token, collateral_adapter, loan_adapter, irm_id, lltv, nonce]

FieldTypeDescription
nonceu64PDA uniqueness nonce (default 0, increment for new markets)
loan_tokenPubkeyLoan token mint
collateral_tokenPubkeyCollateral token mint
collateral_oracleMarketOracleConfigOracle config for collateral price
loan_oracleMarketOracleConfigOracle config for loan price
oracle_risk_paramsOracleRiskParamsStaleness, confidence, price bounds
irm_idPubkeyIRM identifier (builtin PDA or IrmConfig PDA)
irm_cache_typeu8IRM cache type (0-2 builtin, 255 custom)
lltvu64Liquidation LTV in bps (max 9800)
total_supply_assetsu128Total supplied assets
total_supply_sharesu128Total supply shares
total_borrow_assetsu128Total borrowed assets
total_borrow_sharesu128Total borrow shares
total_collateralu128Total collateral deposited
last_updatei64Unix timestamp of last accrual
feeu64Protocol fee in bps (max 2500)
rate_at_targeti128Current rate for AdaptiveCurve IRM
fee_sharesu128Unclaimed protocol fee shares
health_factor_buffer_wadu64Buffer for liquidation threshold (default 0)
bad_debtu128Accumulated bad debt
loan_vaultPubkeyToken account holding loan assets
collateral_vaultPubkeyToken account holding collateral assets
fee_authorityPubkeyAuthority that can update market fee
bumpu8PDA bump seed

Position

PDA: ["position", market, user]

FieldTypeDescription
marketPubkeyMarket this position belongs to
userPubkeyPosition owner
supply_sharesu128User's supply shares
borrow_sharesu128User's borrow shares
collateralu128Collateral deposited
last_updatei64Last interaction timestamp
bumpu8PDA bump seed

ProtocolConfig

PDA: ["protocol_config"]

Governance-controlled allowlists for LLTV values and IRM IDs.

IrmRegistry

PDA: ["irm_registry"]

Governance-controlled registry tracking IrmConfig accounts and enabled IRM IDs.

FieldTypeDescription
next_indexu64Next IrmConfig index to use
enabled_irm_ids[Pubkey; 64]Allowlist of enabled IRM IDs
enabled_irm_countu8Number of enabled IRMs

IrmConfig

PDA: ["irm_config", index_le_bytes]

Parametric interest rate model configuration for custom IRMs.

FieldTypeDescription
indexu64Sequential config index
curve_typeu80=Linear, 1=Kinked, 2=Adaptive
base_rate_wadu128Base rate at 0% utilization (WAD)
slope1_wadu128Slope below kink (WAD)
slope2_wadu128Slope above kink (WAD)
kink_utilization_wadu128Kink point (0 to WAD)
min_rate_wad / max_rate_wadi128Rate bounds

Liquidation Incentive

Cho Markets uses an LLTV-dependent liquidation incentive formula. Higher LLTV markets have lower liquidation bonuses to protect high-leverage positions.

Formula

incentive = min(1.15, 1 / (1 - 0.3 * (1 - LLTV)))

Incentive by LLTV

LLTVLiquidator Bonus
50%15.0% (capped)
75%~11.4%
80%~6.4%
90%~3.1%
98%~0.6%

This model protects high-LLTV markets from excessive liquidation profits while encouraging liquidations in safer markets.

Instructions

User Operations

InstructionArgsremaining_accountsDescription
supplyassets: u64[IrmConfig?]Supply loan tokens to earn interest
withdrawshares: u128[IrmConfig?]Withdraw supplied assets
deposit_collateralamount: u64-Deposit collateral for borrowing
withdraw_collateralamount: u64[coll_feed, loan_feed, IrmConfig?]Withdraw collateral (if healthy)
borrowassets: u64[coll_feed, loan_feed, IrmConfig?]Borrow against collateral
repayshares: u128[IrmConfig?]Repay by specifying shares
repay_assetsassets: u64[IrmConfig?]Repay by specifying asset amount
liquidaterepay_assets: u64[coll_feed, loan_feed, IrmConfig?]Liquidate unhealthy position
accrue_interest(none)[IrmConfig?]Manually trigger interest accrual on market

Note: IrmConfig is only required in remaining_accounts when the market uses a custom IRM (irm_cache_type = 255). For builtin IRMs (types 0-2), the IrmConfig account is not needed.

Admin/Governance

InstructionDescription
create_marketCreate new isolated market
initialize_protocol_configInitialize governance config
submit_enable_lltvQueue new LLTV (timelocked)
accept_enable_lltvAccept queued LLTV after timelock
submit_enable_irmQueue new IRM type (timelocked)
accept_enable_irmAccept queued IRM after timelock
update_market_feeUpdate market protocol fee
update_oracle_risk_paramsUpdate oracle staleness/confidence
claim_feesClaim accumulated protocol fees
revoke_enable_lltvCancel pending LLTV timelock
revoke_enable_irmCancel pending IRM timelock
set_fee_recipientUpdate fee recipient address
set_fee_authorityUpdate fee authority address
set_default_market_feeSet protocol default fee for new markets
submit_transfer_authoritySubmit authority transfer (timelocked)
accept_transfer_authorityAccept authority transfer after timelock
revoke_transfer_authorityCancel pending authority transfer

Interest Rate Models (IRM)

IRM Types

IRMs are identified by their irm_id (Pubkey). Builtin IRMs use canonical PDAs, while custom IRMs use IrmConfig account PDAs.

TypeCache TypeDescription
None (builtin)0No interest accrual (0%)
Linear (builtin)1Fixed rate from rate_at_target field
AdaptiveCurve (builtin)2Dynamic rate that adjusts based on utilization vs 90% target
Custom (IrmConfig)255Parametric IRM loaded from IrmConfig account

Custom IRM (Type 255)

When a market uses a custom IRM (irm_cache_type = 255), the IrmConfig account must be passed in remaining_accounts for instructions that accrue interest:

  • supply, withdraw, repay, accrueInterest: remaining_accounts[0] = IrmConfig
  • borrow, withdrawCollateral, liquidate: remaining_accounts[2] = IrmConfig (after oracle feeds)

For builtin IRMs (types 0-2), no IrmConfig account is needed in remaining_accounts.

AdaptiveCurve Details

Target utilization: 90%
Rate bounds: 0.1% APR (min) to 200% APR (max)
Initial rate_at_target: 4% APR

Two-slope formula:
• Below 90%: APR = 1% + (utilization × 4% / 90%)
  - At 0% util: 1% APR
  - At 90% util: 5% APR
• Above 90%: APR = 5% + ((utilization - 90%) × 10)
  - At 100% util: 105% APR

Rate adjustment: Exponential based on utilization deviation
If util > 90%: rate increases over time
If util < 90%: rate decreases over time

APR vs APY

The UI displays APY (Annual Percentage Yield) using continuous compounding:

APY = e^APR - 1

Example: 5% APR = 5.127% APY

Oracle System

Cho Markets uses a modular oracle adapter system supporting multiple price feed providers.

Supported Adapters

AdapterProgram IDDescription
MockchoMock...Testing/devnet mock prices
PythchoPyth...Pyth Network price feeds (in development)

Oracle Risk Parameters

ParameterDescription
max_staleness_slotsMax slots since last update (0 = disabled)
max_confidence_bpsMax confidence interval width (0 = disabled)
min_priceMinimum acceptable price (0 = disabled)
max_priceMaximum acceptable price (0 = disabled)

Shares Math

Assets to Shares conversion uses virtual shares (1e6) and virtual assets (1) to prevent first-depositor manipulation:

// Constants
VIRTUAL_SHARES = 1_000_000  // 1e6
VIRTUAL_ASSETS = 1

// To shares (round down for supply, round up for borrow)
shares = (assets * (total_shares + VIRTUAL_SHARES)) / (total_assets + VIRTUAL_ASSETS)

// To assets (round up for withdraw, round down for repay)
assets = (shares * (total_assets + VIRTUAL_ASSETS)) / (total_shares + VIRTUAL_SHARES)

Health Factor

Position health is calculated as:

health_factor = (collateral_value * LLTV) / borrow_value

// Position is liquidatable when:
health_factor < (1.0 - health_factor_buffer)

// Default buffer = 0 (exact 1.0 threshold)

Protocol Fees

Protocol fees are collected as a percentage of interest accrued. Fee shares are minted during interest accrual and can be claimed by the fee authority.

// Fee calculation during interest accrual:
fee_amount = interest * fee_bps / 10000

// Fee shares minted:
fee_shares = fee_amount * total_shares / (total_assets - fee_amount)

// Claiming: fee_authority calls claim_fees to convert shares to assets

User Journeys

1. Lender: Earn Interest on USDC

  1. Connect wallet with USDC balance
  2. Go to User tab, select a USDC/SOL market
  3. Enter amount in "Supply USDC" field
  4. Click Supply - you receive supply shares
  5. As borrowers pay interest, your shares become worth more assets
  6. To exit: enter shares in Withdraw field, click Withdraw

2. Borrower: Borrow USDC Against SOL

  1. Connect wallet with SOL balance
  2. Go to User tab, select USDC/SOL market
  3. Deposit SOL as collateral (will be wrapped to wSOL)
  4. Borrow USDC up to LLTV ratio (e.g., 80% means 0.8 USDC per $1 SOL)
  5. Use borrowed USDC as needed
  6. To close: Repay borrowed USDC + interest, then withdraw collateral

3. Liquidator: Liquidate Unhealthy Positions

  1. Monitor positions for health_factor < 1.0
  2. When position is liquidatable, call liquidate instruction
  3. Repay borrower's debt (100%)
  4. Receive collateral + LLTV-dependent incentive bonus
  5. Profit = collateral received - debt repaid

4. Admin: Create New Market

  1. Initialize protocol config (one-time)
  2. Enable LLTV value via timelock (submit, wait, accept)
  3. Enable IRM type via timelock (submit, wait, accept)
  4. Go to Admin tab, select loan/collateral tokens
  5. Set LLTV, fee, IRM, and oracle parameters
  6. Click Create Market - market is live for users

Example: Complete Borrow Flow

Market: USDC/SOL, LLTV: 80%, SOL price: $100

1. User deposits 1 SOL as collateral
   → collateral_value = 1 SOL × $100 = $100

2. Max borrow = collateral_value × LLTV
   → max_borrow = $100 × 0.80 = $80 USDC

3. User borrows 50 USDC (conservative)
   → health_factor = ($100 × 0.80) / $50 = 1.6 ✓ Healthy

4. If SOL drops to $60:
   → health_factor = ($60 × 0.80) / $50 = 0.96 ✗ Liquidatable

5. Liquidator repays full 50 USDC debt (100% close factor)
   → At 80% LLTV, ~6.4% bonus
   → Collateral seized: $50 × 1.064 = $53.20 worth
   → Liquidator receives: 0.887 SOL ($53.20 / $60)
   → Borrower keeps: 0.113 SOL ($6.80), debt cleared

Bad Debt Handling

Bad debt occurs when liquidation leaves remaining debt with zero collateral. It is socialized atomically during liquidation (no separate instruction) to prevent front-running. Losses are distributed proportionally across all lenders by reducing total_supply_assets.

// Bad debt socialization (automatic during liquidation):
if (position.collateral == 0 && position.borrow_shares > 0):
    bad_debt_amount = to_assets(position.borrow_shares)
    market.bad_debt += bad_debt_amount
    market.total_supply_assets -= bad_debt_amount  // Lenders absorb loss
    market.total_borrow_assets -= bad_debt_amount
    position.borrow_shares = 0

Events

  • MarketCreated - New market deployed
  • SupplyEvent - User supplies assets
  • WithdrawEvent - User withdraws assets
  • DepositCollateralEvent - User deposits collateral
  • WithdrawCollateralEvent - User withdraws collateral
  • BorrowEvent - User borrows assets
  • RepayEvent - User repays debt
  • LiquidateEvent - Position liquidated
  • AccrueInterestEvent - Interest accrued
  • BadDebtSocialized - Bad debt distributed to lenders
  • FeesClaimed - Protocol fees claimed
  • MarketFeeUpdated - Market fee changed
  • OracleRiskParamsUpdated - Oracle params changed

Constants

ConstantValueDescription
MAX_LLTV_BP9800Maximum LLTV (98%)
MAX_FEE_BP2500Maximum protocol fee (25%)
MAX_LIQUIDATION_INCENTIVE_FACTOR1.15Max LLTV-dependent bonus (15%)
TARGET_UTILIZATION90%AdaptiveCurve target
MIN_RATE_AT_TARGET0.1% APRMinimum rate bound
MAX_RATE_AT_TARGET200% APRMaximum rate bound

Devnet Tokens

TokenMintDecimalsPrice
SOLSo111...11129$100
USDC4zMMC...ncDU6$1
PYUSDCXk2A...ynM6$1
EURCHzwqb...Ktr6$1.08

Devnet Faucets

Get test tokens for devnet:

TokenFaucet
SOLfaucet.solana.com
PYUSDGoogle Cloud PYUSD Faucet
USDCfaucet.circle.com
EURCfaucet.circle.com