From 31e5d3cb48289148e5e22391fb148297601d6c0f Mon Sep 17 00:00:00 2001 From: JKrishnaD Date: Sun, 23 Nov 2025 23:09:06 +0530 Subject: [PATCH 1/5] feat: add kamino lending protocol scenario --- .../scenarios/protocols/kamino/v1/idl.json | 2268 +++++++++++++++++ .../protocols/kamino/v1/overrides.yaml | 55 + crates/core/src/scenarios/registry.rs | 38 +- 3 files changed, 2339 insertions(+), 22 deletions(-) create mode 100644 crates/core/src/scenarios/protocols/kamino/v1/idl.json create mode 100644 crates/core/src/scenarios/protocols/kamino/v1/overrides.yaml diff --git a/crates/core/src/scenarios/protocols/kamino/v1/idl.json b/crates/core/src/scenarios/protocols/kamino/v1/idl.json new file mode 100644 index 00000000..38ff8c8d --- /dev/null +++ b/crates/core/src/scenarios/protocols/kamino/v1/idl.json @@ -0,0 +1,2268 @@ +{ + "address": "KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD", + "metadata": { + "name": "kamino_lending", + "version": "1.12.6", + "spec": "0.1.0" + }, + "instructions": [], + "accounts": [ + { + "name": "UserState", + "discriminator": [72, 177, 85, 249, 76, 167, 186, 126] + }, + { + "name": "GlobalConfig", + "discriminator": [149, 8, 156, 202, 160, 252, 176, 217] + }, + { + "name": "LendingMarket", + "discriminator": [246, 114, 50, 98, 72, 157, 28, 120] + }, + { + "name": "Obligation", + "discriminator": [168, 206, 141, 106, 88, 76, 172, 167] + }, + { + "name": "ReferrerState", + "discriminator": [194, 81, 217, 103, 12, 19, 12, 66] + }, + { + "name": "ReferrerTokenState", + "discriminator": [39, 15, 208, 77, 32, 195, 105, 56] + }, + { + "name": "ShortUrl", + "discriminator": [28, 89, 174, 25, 226, 124, 126, 212] + }, + { + "name": "UserMetadata", + "discriminator": [157, 214, 220, 235, 98, 135, 171, 28] + }, + { + "name": "Reserve", + "discriminator": [43, 242, 204, 202, 26, 247, 59, 127] + } + ], + "types": [ + { + "name": "UpdateConfigMode", + "type": { + "kind": "enum", + "variants": [ + { + "name": "UpdateLoanToValuePct" + }, + { + "name": "UpdateMaxLiquidationBonusBps" + }, + { + "name": "UpdateLiquidationThresholdPct" + }, + { + "name": "UpdateProtocolLiquidationFee" + }, + { + "name": "UpdateProtocolTakeRate" + }, + { + "name": "UpdateFeesOriginationFee" + }, + { + "name": "UpdateFeesFlashLoanFee" + }, + { + "name": "DeprecatedUpdateFeesReferralFeeBps" + }, + { + "name": "UpdateDepositLimit" + }, + { + "name": "UpdateBorrowLimit" + }, + { + "name": "UpdateTokenInfoLowerHeuristic" + }, + { + "name": "UpdateTokenInfoUpperHeuristic" + }, + { + "name": "UpdateTokenInfoExpHeuristic" + }, + { + "name": "UpdateTokenInfoTwapDivergence" + }, + { + "name": "UpdateTokenInfoScopeTwap" + }, + { + "name": "UpdateTokenInfoScopeChain" + }, + { + "name": "UpdateTokenInfoName" + }, + { + "name": "UpdateTokenInfoPriceMaxAge" + }, + { + "name": "UpdateTokenInfoTwapMaxAge" + }, + { + "name": "UpdateScopePriceFeed" + }, + { + "name": "UpdatePythPrice" + }, + { + "name": "UpdateSwitchboardFeed" + }, + { + "name": "UpdateSwitchboardTwapFeed" + }, + { + "name": "UpdateBorrowRateCurve" + }, + { + "name": "UpdateEntireReserveConfig" + }, + { + "name": "UpdateDebtWithdrawalCap" + }, + { + "name": "UpdateDepositWithdrawalCap" + }, + { + "name": "DeprecatedUpdateDebtWithdrawalCapCurrentTotal" + }, + { + "name": "DeprecatedUpdateDepositWithdrawalCapCurrentTotal" + }, + { + "name": "UpdateBadDebtLiquidationBonusBps" + }, + { + "name": "UpdateMinLiquidationBonusBps" + }, + { + "name": "UpdateDeleveragingMarginCallPeriod" + }, + { + "name": "UpdateBorrowFactor" + }, + { + "name": "UpdateAssetTier" + }, + { + "name": "UpdateElevationGroup" + }, + { + "name": "UpdateDeleveragingThresholdDecreaseBpsPerDay" + }, + { + "name": "DeprecatedUpdateMultiplierSideBoost" + }, + { + "name": "DeprecatedUpdateMultiplierTagBoost" + }, + { + "name": "UpdateReserveStatus" + }, + { + "name": "UpdateFarmCollateral" + }, + { + "name": "UpdateFarmDebt" + }, + { + "name": "UpdateDisableUsageAsCollateralOutsideEmode" + }, + { + "name": "UpdateBlockBorrowingAboveUtilizationPct" + }, + { + "name": "UpdateBlockPriceUsage" + }, + { + "name": "UpdateBorrowLimitOutsideElevationGroup" + }, + { + "name": "UpdateBorrowLimitsInElevationGroupAgainstThisReserve" + }, + { + "name": "UpdateHostFixedInterestRateBps" + }, + { + "name": "UpdateAutodeleverageEnabled" + }, + { + "name": "UpdateDeleveragingBonusIncreaseBpsPerDay" + }, + { + "name": "UpdateProtocolOrderExecutionFee" + }, + { + "name": "UpdateProposerAuthorityLock" + }, + { + "name": "UpdateMinDeleveragingBonusBps" + }, + { + "name": "UpdateBlockCTokenUsage" + } + ] + } + }, + { + "name": "UpdateLendingMarketConfigValue", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Bool", + "fields": ["bool"] + }, + { + "name": "U8", + "fields": ["u8"] + }, + { + "name": "U8Array", + "fields": [ + { + "array": ["u8", 8] + } + ] + }, + { + "name": "U16", + "fields": ["u16"] + }, + { + "name": "U64", + "fields": ["u64"] + }, + { + "name": "U128", + "fields": ["u128"] + }, + { + "name": "Pubkey", + "fields": ["pubkey"] + }, + { + "name": "ElevationGroup", + "fields": [ + { + "defined": { + "name": "ElevationGroup" + } + } + ] + }, + { + "name": "Name", + "fields": [ + { + "array": ["u8", 32] + } + ] + } + ] + } + }, + { + "name": "UpdateLendingMarketMode", + "type": { + "kind": "enum", + "variants": [ + { + "name": "UpdateOwner" + }, + { + "name": "UpdateEmergencyMode" + }, + { + "name": "UpdateLiquidationCloseFactor" + }, + { + "name": "UpdateLiquidationMaxValue" + }, + { + "name": "DeprecatedUpdateGlobalUnhealthyBorrow" + }, + { + "name": "UpdateGlobalAllowedBorrow" + }, + { + "name": "UpdateRiskCouncil" + }, + { + "name": "UpdateMinFullLiquidationThreshold" + }, + { + "name": "UpdateInsolvencyRiskLtv" + }, + { + "name": "UpdateElevationGroup" + }, + { + "name": "UpdateReferralFeeBps" + }, + { + "name": "DeprecatedUpdateMultiplierPoints" + }, + { + "name": "UpdatePriceRefreshTriggerToMaxAgePct" + }, + { + "name": "UpdateAutodeleverageEnabled" + }, + { + "name": "UpdateBorrowingDisabled" + }, + { + "name": "UpdateMinNetValueObligationPostAction" + }, + { + "name": "UpdateMinValueLtvSkipPriorityLiqCheck" + }, + { + "name": "UpdateMinValueBfSkipPriorityLiqCheck" + }, + { + "name": "UpdatePaddingFields" + }, + { + "name": "UpdateName" + }, + { + "name": "UpdateIndividualAutodeleverageMarginCallPeriodSecs" + }, + { + "name": "UpdateInitialDepositAmount" + }, + { + "name": "UpdateObligationOrderExecutionEnabled" + }, + { + "name": "UpdateImmutableFlag" + }, + { + "name": "UpdateObligationOrderCreationEnabled" + }, + { + "name": "UpdateProposerAuthority" + }, + { + "name": "UpdatePriceTriggeredLiquidationDisabled" + } + ] + } + }, + { + "name": "UpdateGlobalConfigMode", + "type": { + "kind": "enum", + "variants": [ + { + "name": "PendingAdmin" + }, + { + "name": "FeeCollector" + } + ] + } + }, + { + "name": "LastUpdate", + "docs": ["Last update state"], + "type": { + "kind": "struct", + "fields": [ + { + "name": "slot", + "docs": ["Last slot when updated"], + "type": "u64" + }, + { + "name": "stale", + "docs": ["True when marked stale, false when slot updated"], + "type": "u8" + }, + { + "name": "price_status", + "docs": ["Status of the prices used to calculate the last update"], + "type": "u8" + }, + { + "name": "placeholder", + "type": { + "array": ["u8", 6] + } + } + ] + } + }, + { + "name": "ElevationGroup", + "type": { + "kind": "struct", + "fields": [ + { + "name": "max_liquidation_bonus_bps", + "type": "u16" + }, + { + "name": "id", + "type": "u8" + }, + { + "name": "ltv_pct", + "type": "u8" + }, + { + "name": "liquidation_threshold_pct", + "type": "u8" + }, + { + "name": "allow_new_loans", + "type": "u8" + }, + { + "name": "max_reserves_as_collateral", + "type": "u8" + }, + { + "name": "padding0", + "type": "u8" + }, + { + "name": "debt_reserve", + "docs": ["Mandatory debt reserve for this elevation group"], + "type": "pubkey" + }, + { + "name": "padding1", + "type": { + "array": ["u64", 4] + } + } + ] + } + }, + { + "name": "InitObligationArgs", + "type": { + "kind": "struct", + "fields": [ + { + "name": "tag", + "type": "u8" + }, + { + "name": "id", + "type": "u8" + } + ] + } + }, + { + "name": "ObligationCollateral", + "docs": ["Obligation collateral state"], + "type": { + "kind": "struct", + "fields": [ + { + "name": "deposit_reserve", + "docs": ["Reserve collateral is deposited to"], + "type": "pubkey" + }, + { + "name": "deposited_amount", + "docs": ["Amount of collateral deposited"], + "type": "u64" + }, + { + "name": "market_value_sf", + "docs": [ + "Collateral market value in quote currency (scaled fraction)" + ], + "type": "u128" + }, + { + "name": "borrowed_amount_against_this_collateral_in_elevation_group", + "docs": [ + "Debt amount (lamport) taken against this collateral.", + "(only meaningful if this obligation is part of an elevation group, otherwise 0)", + "This is only indicative of the debt computed on the last refresh obligation.", + "If the obligation have multiple collateral this value is the same for all of them." + ], + "type": "u64" + }, + { + "name": "padding", + "type": { + "array": ["u64", 9] + } + } + ] + } + }, + { + "name": "ObligationLiquidity", + "docs": ["Obligation liquidity state"], + "type": { + "kind": "struct", + "fields": [ + { + "name": "borrow_reserve", + "docs": ["Reserve liquidity is borrowed from"], + "type": "pubkey" + }, + { + "name": "cumulative_borrow_rate_bsf", + "docs": [ + "Borrow rate used for calculating interest (big scaled fraction)" + ], + "type": { + "defined": { + "name": "BigFractionBytes" + } + } + }, + { + "name": "padding", + "type": "u64" + }, + { + "name": "borrowed_amount_sf", + "docs": [ + "Amount of liquidity borrowed plus interest (scaled fraction)" + ], + "type": "u128" + }, + { + "name": "market_value_sf", + "docs": [ + "Liquidity market value in quote currency (scaled fraction)" + ], + "type": "u128" + }, + { + "name": "borrow_factor_adjusted_market_value_sf", + "docs": [ + "Risk adjusted liquidity market value in quote currency - DEBUG ONLY - use market_value instead" + ], + "type": "u128" + }, + { + "name": "borrowed_amount_outside_elevation_groups", + "docs": [ + "Amount of liquidity borrowed outside of an elevation group" + ], + "type": "u64" + }, + { + "name": "padding2", + "type": { + "array": ["u64", 7] + } + } + ] + } + }, + { + "name": "ObligationOrder", + "docs": ["A single obligation order.", "See [Obligation::orders]."], + "type": { + "kind": "struct", + "fields": [ + { + "name": "condition_threshold_sf", + "docs": [ + "A threshold value used by the condition (scaled [Fraction]).", + "The exact meaning depends on the specific [Self::condition_type].", + "", + "Examples:", + "- when `condition_type == 2 (UserLtvBelow)`:", + "then a value of `0.455` here means that the order is active only when the obligation's", + "user LTV is less than `0.455` (i.e. < 45.5%).", + "- when `condition_type == 3 (DebtCollPriceRatioAbove)`:", + "assuming the obligation uses BTC collateral for SOL debt, then a value of `491.3` here", + "means that the order is active only when the BTC-SOL price is greater than `491.3` (i.e.", + "> 491.3 SOL per BTC)." + ], + "type": "u128" + }, + { + "name": "opportunity_parameter_sf", + "docs": [ + "A configuration parameter used by the opportunity (scaled [Fraction]).", + "The exact meaning depends on the specific [Self::opportunity_type].", + "", + "Examples:", + "- when `opportunity_type == 0 (DeleverageSingleDebtAmount)`:", + "Assuming the obligation uses BTC collateral for SOL debt, then a value of `1_234_000_000`", + "here means that a liquidator may repay up to 1234000000 lamports (i.e. 1.234 SOL) on this", + "obligation.", + "Note: the special value of [Fraction::MAX] is *not* allowed in this case.", + "- when `opportunity_type == 1 (DeleverageAllDebtAmount)`:", + "The only allowed value in this case is [Fraction::MAX] (to emphasize that *all* debt", + "should be repaid)." + ], + "type": "u128" + }, + { + "name": "min_execution_bonus_bps", + "docs": [ + "A *minimum* additional fraction of collateral transferred to the liquidator, in bps.", + "", + "The minimum bonus is applied exactly when the [Self::condition_threshold_sf] is met, and", + "grows linearly towards the [Self::max_execution_bonus_bps].", + "", + "Example: a value of `50` here means 50bps == 0.5% bonus for an \"LTV > 65%\" order, when", + "executed precisely at the moment LTV exceeds 65%." + ], + "type": "u16" + }, + { + "name": "max_execution_bonus_bps", + "docs": [ + "A *maximum* additional fraction of collateral transferred to the liquidator, in bps.", + "", + "The maximum bonus is applied at the relevant \"extreme\" state of the obligation, i.e.:", + "- for a stop-loss condition, it is a point at which the obligation becomes liquidatable;", + "- for a take-profit condition, it is a point at which obligation has 0% LTV.", + "", + "In non-extreme states, the actual bonus value is interpolated linearly, starting from", + "[Self::min_execution_bonus_bps] (at the point specified by the order's condition).", + "", + "Example: a value of `300` here means 300bps == 3.0% bonus for a \"debt/coll price > 140\"", + "order, when executed at a higher price = 200, at which the obligation's LTV happens to", + "be equal to its liquidation LTV." + ], + "type": "u16" + }, + { + "name": "condition_type", + "docs": [ + "Serialized [ConditionType].", + "The entire order is void when this is zeroed (i.e. representing [ConditionType::Never]).", + "", + "Example: a value of `2` here denotes `UserLtvBelow` condition type. Of course, to", + "interpret this condition, we also need to take the [Self::condition_threshold_sf] into", + "account." + ], + "type": "u8" + }, + { + "name": "opportunity_type", + "docs": [ + "Serialized [OpportunityType].", + "", + "Example: a value of `0` here denotes `DeleverageSingleDebtAmount` opportunity. Of course, to", + "interpret this opportunity, we also need to take the [Self::opportunity_parameter_sf] into", + "account." + ], + "type": "u8" + }, + { + "name": "padding1", + "docs": [ + "Internal padding.", + "The fields above take up 2+2+1+1 bytes = 48 bits, which means we need 80 bits = 10 bytes to", + "align with `u128`s." + ], + "type": { + "array": ["u8", 10] + } + }, + { + "name": "padding2", + "docs": [ + "End padding.", + "The total size of a single instance is 8*u128 = 128 bytes." + ], + "type": { + "array": ["u128", 5] + } + } + ] + } + }, + { + "name": "AssetTier", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Regular" + }, + { + "name": "IsolatedCollateral" + }, + { + "name": "IsolatedDebt" + } + ] + } + }, + { + "name": "BigFractionBytes", + "type": { + "kind": "struct", + "fields": [ + { + "name": "value", + "type": { + "array": ["u64", 4] + } + }, + { + "name": "padding", + "type": { + "array": ["u64", 2] + } + } + ] + } + }, + { + "name": "FeeCalculation", + "docs": ["Calculate fees exlusive or inclusive of an amount"], + "type": { + "kind": "enum", + "variants": [ + { + "name": "Exclusive" + }, + { + "name": "Inclusive" + } + ] + } + }, + { + "name": "ReserveCollateral", + "docs": ["Reserve collateral"], + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint_pubkey", + "docs": ["Reserve collateral mint address"], + "type": "pubkey" + }, + { + "name": "mint_total_supply", + "docs": ["Reserve collateral mint supply, used for exchange rate"], + "type": "u64" + }, + { + "name": "supply_vault", + "docs": ["Reserve collateral supply address"], + "type": "pubkey" + }, + { + "name": "padding1", + "type": { + "array": ["u128", 32] + } + }, + { + "name": "padding2", + "type": { + "array": ["u128", 32] + } + } + ] + } + }, + { + "name": "ReserveConfig", + "docs": ["Reserve configuration values"], + "type": { + "kind": "struct", + "fields": [ + { + "name": "status", + "docs": ["Status of the reserve Active/Obsolete/Hidden"], + "type": "u8" + }, + { + "name": "asset_tier", + "docs": [ + "Asset tier -> 0 - regular (collateral & debt), 1 - isolated collateral, 2 - isolated debt" + ], + "type": "u8" + }, + { + "name": "host_fixed_interest_rate_bps", + "docs": ["Flat rate that goes to the host"], + "type": "u16" + }, + { + "name": "min_deleveraging_bonus_bps", + "docs": [ + "Starting bonus for deleveraging-related liquidations, in bps." + ], + "type": "u16" + }, + { + "name": "block_ctoken_usage", + "docs": [ + "Boolean flag to block minting/redeeming of ctokens", + "Blocks usage of ctokens (minting or withdrawing from obligation)", + "Effectively blocks deposit_reserve_liquidity and withdraw_obligation_collateral" + ], + "type": "u8" + }, + { + "name": "reserved1", + "docs": ["Past reserved space - feel free to reuse."], + "type": { + "array": ["u8", 6] + } + }, + { + "name": "protocol_order_execution_fee_pct", + "docs": [ + "Cut of the order execution bonus that the protocol receives, as a percentage" + ], + "type": "u8" + }, + { + "name": "protocol_take_rate_pct", + "docs": [ + "Protocol take rate is the amount borrowed interest protocol receives, as a percentage" + ], + "type": "u8" + }, + { + "name": "protocol_liquidation_fee_pct", + "docs": [ + "Cut of the liquidation bonus that the protocol receives, as a percentage" + ], + "type": "u8" + }, + { + "name": "loan_to_value_pct", + "docs": [ + "Target ratio of the value of borrows to deposits, as a percentage", + "0 if use as collateral is disabled" + ], + "type": "u8" + }, + { + "name": "liquidation_threshold_pct", + "docs": [ + "Loan to value ratio at which an obligation can be liquidated, as percentage" + ], + "type": "u8" + }, + { + "name": "min_liquidation_bonus_bps", + "docs": [ + "Minimum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps" + ], + "type": "u16" + }, + { + "name": "max_liquidation_bonus_bps", + "docs": [ + "Maximum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps" + ], + "type": "u16" + }, + { + "name": "bad_debt_liquidation_bonus_bps", + "docs": [ + "Bad debt liquidation bonus for an undercollateralized obligation, as bps" + ], + "type": "u16" + }, + { + "name": "deleveraging_margin_call_period_secs", + "docs": [ + "Time in seconds that must pass before redemptions are enabled after the deposit limit is", + "crossed.", + "Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case." + ], + "type": "u64" + }, + { + "name": "deleveraging_threshold_decrease_bps_per_day", + "docs": [ + "The rate at which the deleveraging threshold decreases, in bps per day.", + "Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case." + ], + "type": "u64" + }, + { + "name": "fees", + "docs": [ + "Program owner fees assessed, separate from gains due to interest accrual" + ], + "type": { + "defined": { + "name": "ReserveFees" + } + } + }, + { + "name": "borrow_rate_curve", + "docs": ["Borrow rate curve based on utilization"], + "type": { + "defined": { + "name": "BorrowRateCurve" + } + } + }, + { + "name": "borrow_factor_pct", + "docs": ["Borrow factor in percentage - used for risk adjustment"], + "type": "u64" + }, + { + "name": "deposit_limit", + "docs": [ + "Maximum deposit limit of liquidity in native units, u64::MAX for inf" + ], + "type": "u64" + }, + { + "name": "borrow_limit", + "docs": [ + "Maximum amount borrowed, u64::MAX for inf, 0 to disable borrows (protected deposits)" + ], + "type": "u64" + }, + { + "name": "token_info", + "docs": ["Token id from TokenInfos struct"], + "type": { + "defined": { + "name": "TokenInfo" + } + } + }, + { + "name": "deposit_withdrawal_cap", + "docs": ["Deposit withdrawal caps - deposit & redeem"], + "type": { + "defined": { + "name": "WithdrawalCaps" + } + } + }, + { + "name": "debt_withdrawal_cap", + "docs": ["Debt withdrawal caps - borrow & repay"], + "type": { + "defined": { + "name": "WithdrawalCaps" + } + } + }, + { + "name": "elevation_groups", + "type": { + "array": ["u8", 20] + } + }, + { + "name": "disable_usage_as_coll_outside_emode", + "type": "u8" + }, + { + "name": "utilization_limit_block_borrowing_above_pct", + "docs": [ + "Utilization (in percentage) above which borrowing is blocked. 0 to disable." + ], + "type": "u8" + }, + { + "name": "autodeleverage_enabled", + "docs": [ + "Whether this reserve should be subject to auto-deleveraging after deposit or borrow limit is", + "crossed.", + "Besides this flag, the lending market's flag also needs to be enabled (logical `AND`).", + "**NOTE:** the manual \"target LTV\" deleveraging (enabled by the risk council for individual", + "obligations) is NOT affected by this flag." + ], + "type": "u8" + }, + { + "name": "proposer_authority_locked", + "docs": [ + "Boolean flag indicating whether the reserve is locked for the proposer authority.", + "", + "Once the proposer have finished preparing the reserve, it must be locked to prevent", + "further changes to the reserve configuration allowing review and voting on the proposal", + "without alteration during the voting period." + ], + "type": "u8" + }, + { + "name": "borrow_limit_outside_elevation_group", + "docs": [ + "Maximum amount liquidity of this reserve borrowed outside all elevation groups", + "- u64::MAX for inf", + "- 0 to disable borrows outside elevation groups" + ], + "type": "u64" + }, + { + "name": "borrow_limit_against_this_collateral_in_elevation_group", + "docs": [ + "Defines the maximum amount (in lamports of elevation group debt asset)", + "that can be borrowed when this reserve is used as collateral.", + "- u64::MAX for inf", + "- 0 to disable borrows in this elevation group (expected value for the debt asset)" + ], + "type": { + "array": ["u64", 32] + } + }, + { + "name": "deleveraging_bonus_increase_bps_per_day", + "docs": [ + "The rate at which the deleveraging-related liquidation bonus increases, in bps per day.", + "Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case." + ], + "type": "u64" + } + ] + } + }, + { + "name": "ReserveFarmKind", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Collateral" + }, + { + "name": "Debt" + } + ] + } + }, + { + "name": "ReserveFees", + "docs": [ + "Additional fee information on a reserve", + "", + "These exist separately from interest accrual fees, and are specifically for the program owner", + "and referral fee. The fees are paid out as a percentage of liquidity token amounts during", + "repayments and liquidations." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "origination_fee_sf", + "docs": [ + "Fee assessed on `BorrowObligationLiquidity`, as scaled fraction (60 bits fractional part)", + "Must be between `0` and `2^60`, such that `2^60 = 1`. A few examples for", + "clarity:", + "1% = (1 << 60) / 100 = 11529215046068470", + "0.01% (1 basis point) = 115292150460685", + "0.00001% (Aave origination fee) = 115292150461" + ], + "type": "u64" + }, + { + "name": "flash_loan_fee_sf", + "docs": [ + "Fee for flash loan, expressed as scaled fraction.", + "0.3% (Aave flash loan fee) = 0.003 * 2^60 = 3458764513820541" + ], + "type": "u64" + }, + { + "name": "padding", + "docs": ["Used for allignment"], + "type": { + "array": ["u8", 8] + } + } + ] + } + }, + { + "name": "ReserveLiquidity", + "docs": ["Reserve liquidity"], + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint_pubkey", + "docs": ["Reserve liquidity mint address"], + "type": "pubkey" + }, + { + "name": "supply_vault", + "docs": ["Reserve liquidity supply address"], + "type": "pubkey" + }, + { + "name": "fee_vault", + "docs": ["Reserve liquidity fee collection address"], + "type": "pubkey" + }, + { + "name": "available_amount", + "docs": ["Reserve liquidity available"], + "type": "u64" + }, + { + "name": "borrowed_amount_sf", + "docs": ["Reserve liquidity borrowed (scaled fraction)"], + "type": "u128" + }, + { + "name": "market_price_sf", + "docs": [ + "Reserve liquidity market price in quote currency (scaled fraction)" + ], + "type": "u128" + }, + { + "name": "market_price_last_updated_ts", + "docs": ["Unix timestamp of the market price (from the oracle)"], + "type": "u64" + }, + { + "name": "mint_decimals", + "docs": ["Reserve liquidity mint decimals"], + "type": "u64" + }, + { + "name": "deposit_limit_crossed_timestamp", + "docs": [ + "Timestamp when the last refresh reserve detected that the liquidity amount is above the deposit cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.", + "If the threshold is not crossed, then the timestamp is set to 0" + ], + "type": "u64" + }, + { + "name": "borrow_limit_crossed_timestamp", + "docs": [ + "Timestamp when the last refresh reserve detected that the borrowed amount is above the borrow cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.", + "If the threshold is not crossed, then the timestamp is set to 0" + ], + "type": "u64" + }, + { + "name": "cumulative_borrow_rate_bsf", + "docs": [ + "Reserve liquidity cumulative borrow rate (scaled fraction)" + ], + "type": { + "defined": { + "name": "BigFractionBytes" + } + } + }, + { + "name": "accumulated_protocol_fees_sf", + "docs": ["Reserve cumulative protocol fees (scaled fraction)"], + "type": "u128" + }, + { + "name": "accumulated_referrer_fees_sf", + "docs": ["Reserve cumulative referrer fees (scaled fraction)"], + "type": "u128" + }, + { + "name": "pending_referrer_fees_sf", + "docs": [ + "Reserve pending referrer fees, to be claimed in refresh_obligation by referrer or protocol (scaled fraction)" + ], + "type": "u128" + }, + { + "name": "absolute_referral_rate_sf", + "docs": [ + "Reserve referrer fee absolute rate calculated at each refresh_reserve operation (scaled fraction)" + ], + "type": "u128" + }, + { + "name": "token_program", + "docs": ["Token program of the liquidity mint"], + "type": "pubkey" + }, + { + "name": "padding2", + "type": { + "array": ["u64", 51] + } + }, + { + "name": "padding3", + "type": { + "array": ["u128", 32] + } + } + ] + } + }, + { + "name": "ReserveStatus", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Active" + }, + { + "name": "Obsolete" + }, + { + "name": "Hidden" + } + ] + } + }, + { + "name": "WithdrawalCaps", + "docs": ["Reserve Withdrawal Caps State"], + "type": { + "kind": "struct", + "fields": [ + { + "name": "config_capacity", + "type": "i64" + }, + { + "name": "current_total", + "type": "i64" + }, + { + "name": "last_interval_start_timestamp", + "type": "u64" + }, + { + "name": "config_interval_length_seconds", + "type": "u64" + } + ] + } + }, + { + "name": "PriceHeuristic", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lower", + "docs": ["Lower value of acceptable price"], + "type": "u64" + }, + { + "name": "upper", + "docs": ["Upper value of acceptable price"], + "type": "u64" + }, + { + "name": "exp", + "docs": ["Number of decimals of the previously defined values"], + "type": "u64" + } + ] + } + }, + { + "name": "PythConfiguration", + "type": { + "kind": "struct", + "fields": [ + { + "name": "price", + "docs": [ + "Pubkey of the base price feed (disabled if `null` or `default`)" + ], + "type": "pubkey" + } + ] + } + }, + { + "name": "ScopeConfiguration", + "type": { + "kind": "struct", + "fields": [ + { + "name": "price_feed", + "docs": [ + "Pubkey of the scope price feed (disabled if `null` or `default`)" + ], + "type": "pubkey" + }, + { + "name": "price_chain", + "docs": [ + "This is the scope_id price chain that results in a price for the token" + ], + "type": { + "array": ["u16", 4] + } + }, + { + "name": "twap_chain", + "docs": ["This is the scope_id price chain for the twap"], + "type": { + "array": ["u16", 4] + } + } + ] + } + }, + { + "name": "SwitchboardConfiguration", + "type": { + "kind": "struct", + "fields": [ + { + "name": "price_aggregator", + "docs": [ + "Pubkey of the base price feed (disabled if `null` or `default`)" + ], + "type": "pubkey" + }, + { + "name": "twap_aggregator", + "type": "pubkey" + } + ] + } + }, + { + "name": "TokenInfo", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "docs": ["UTF-8 encoded name of the token (null-terminated)"], + "type": { + "array": ["u8", 32] + } + }, + { + "name": "heuristic", + "docs": ["Heuristics limits of acceptable price"], + "type": { + "defined": { + "name": "PriceHeuristic" + } + } + }, + { + "name": "max_twap_divergence_bps", + "docs": ["Max divergence between twap and price in bps"], + "type": "u64" + }, + { + "name": "max_age_price_seconds", + "type": "u64" + }, + { + "name": "max_age_twap_seconds", + "type": "u64" + }, + { + "name": "scope_configuration", + "docs": ["Scope price configuration"], + "type": { + "defined": { + "name": "ScopeConfiguration" + } + } + }, + { + "name": "switchboard_configuration", + "docs": ["Switchboard configuration"], + "type": { + "defined": { + "name": "SwitchboardConfiguration" + } + } + }, + { + "name": "pyth_configuration", + "docs": ["Pyth configuration"], + "type": { + "defined": { + "name": "PythConfiguration" + } + } + }, + { + "name": "block_price_usage", + "type": "u8" + }, + { + "name": "reserved", + "type": { + "array": ["u8", 7] + } + }, + { + "name": "padding", + "type": { + "array": ["u64", 19] + } + } + ] + } + }, + { + "name": "BorrowRateCurve", + "type": { + "kind": "struct", + "fields": [ + { + "name": "points", + "type": { + "array": [ + { + "defined": { + "name": "CurvePoint" + } + }, + 11 + ] + } + } + ] + } + }, + { + "name": "CurvePoint", + "type": { + "kind": "struct", + "fields": [ + { + "name": "utilization_rate_bps", + "type": "u32" + }, + { + "name": "borrow_rate_bps", + "type": "u32" + } + ] + } + }, + { + "name": "UserState", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user_id", + "type": "u64" + }, + { + "name": "farm_state", + "type": "pubkey" + }, + { + "name": "owner", + "type": "pubkey" + }, + { + "name": "is_farm_delegated", + "type": "u8" + }, + { + "name": "padding0", + "type": { + "array": ["u8", 7] + } + }, + { + "name": "rewards_tally_scaled", + "type": { + "array": ["u128", 10] + } + }, + { + "name": "rewards_issued_unclaimed", + "type": { + "array": ["u64", 10] + } + }, + { + "name": "last_claim_ts", + "type": { + "array": ["u64", 10] + } + }, + { + "name": "active_stake_scaled", + "type": "u128" + }, + { + "name": "pending_deposit_stake_scaled", + "type": "u128" + }, + { + "name": "pending_deposit_stake_ts", + "type": "u64" + }, + { + "name": "pending_withdrawal_unstake_scaled", + "type": "u128" + }, + { + "name": "pending_withdrawal_unstake_ts", + "type": "u64" + }, + { + "name": "bump", + "type": "u64" + }, + { + "name": "delegatee", + "type": "pubkey" + }, + { + "name": "last_stake_ts", + "type": "u64" + }, + { + "name": "padding1", + "type": { + "array": ["u64", 50] + } + } + ] + } + }, + { + "name": "GlobalConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "global_admin", + "docs": ["Global admin of the program"], + "type": "pubkey" + }, + { + "name": "pending_admin", + "docs": [ + "Pending admin must sign a specific transaction to become the global admin" + ], + "type": "pubkey" + }, + { + "name": "fee_collector", + "docs": [ + "Fee collector is the only allowed owner of token accounts receiving protocol fees" + ], + "type": "pubkey" + }, + { + "name": "padding", + "docs": ["Padding to make the struct size 1024 bytes"], + "type": { + "array": ["u8", 928] + } + } + ] + } + }, + { + "name": "LendingMarket", + "type": { + "kind": "struct", + "fields": [ + { + "name": "version", + "docs": ["Version of lending market"], + "type": "u64" + }, + { + "name": "bump_seed", + "docs": ["Bump seed for derived authority address"], + "type": "u64" + }, + { + "name": "lending_market_owner", + "docs": ["Owner authority which can add new reserves"], + "type": "pubkey" + }, + { + "name": "lending_market_owner_cached", + "docs": [ + "Temporary cache of the lending market owner, used in update_lending_market_owner" + ], + "type": "pubkey" + }, + { + "name": "quote_currency", + "docs": [ + "Currency market prices are quoted in", + "e.g. \"USD\" null padded (`*b\"USD\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"`) or a SPL token mint pubkey" + ], + "type": { + "array": ["u8", 32] + } + }, + { + "name": "referral_fee_bps", + "docs": [ + "Referral fee for the lending market, as bps out of the total protocol fee" + ], + "type": "u16" + }, + { + "name": "emergency_mode", + "type": "u8" + }, + { + "name": "autodeleverage_enabled", + "docs": [ + "Whether the obligations on this market should be subject to auto-deleveraging after deposit", + "or borrow limit is crossed.", + "Besides this flag, the particular reserve's flag also needs to be enabled (logical `AND`).", + "**NOTE:** this also affects the individual \"target LTV\" deleveraging." + ], + "type": "u8" + }, + { + "name": "borrow_disabled", + "type": "u8" + }, + { + "name": "price_refresh_trigger_to_max_age_pct", + "docs": [ + "Refresh price from oracle only if it's older than this percentage of the price max age.", + "e.g. if the max age is set to 100s and this is set to 80%, the price will be refreshed if it's older than 80s.", + "Price is always refreshed if this set to 0." + ], + "type": "u8" + }, + { + "name": "liquidation_max_debt_close_factor_pct", + "docs": [ + "Percentage of the total borrowed value in an obligation available for liquidation" + ], + "type": "u8" + }, + { + "name": "insolvency_risk_unhealthy_ltv_pct", + "docs": [ + "Minimum acceptable unhealthy LTV before max_debt_close_factor_pct becomes 100%" + ], + "type": "u8" + }, + { + "name": "min_full_liquidation_value_threshold", + "docs": [ + "Minimum liquidation value threshold triggering full liquidation for an obligation" + ], + "type": "u64" + }, + { + "name": "max_liquidatable_debt_market_value_at_once", + "docs": ["Max allowed liquidation value in one ix call"], + "type": "u64" + }, + { + "name": "reserved0", + "docs": [ + "[DEPRECATED] Global maximum unhealthy borrow value allowed for any obligation" + ], + "type": { + "array": ["u8", 8] + } + }, + { + "name": "global_allowed_borrow_value", + "docs": [ + "Global maximum allowed borrow value allowed for any obligation" + ], + "type": "u64" + }, + { + "name": "risk_council", + "docs": [ + "The address of the risk council, in charge of making parameter and risk decisions on behalf of the protocol" + ], + "type": "pubkey" + }, + { + "name": "reserved1", + "docs": [ + "[DEPRECATED] Reward points multiplier per obligation type" + ], + "type": { + "array": ["u8", 8] + } + }, + { + "name": "elevation_groups", + "docs": [ + "Elevation groups are used to group together reserves that have the same risk parameters and can bump the ltv and liquidation threshold" + ], + "type": { + "array": [ + { + "defined": { + "name": "ElevationGroup" + } + }, + 32 + ] + } + }, + { + "name": "elevation_group_padding", + "type": { + "array": ["u64", 90] + } + }, + { + "name": "min_net_value_in_obligation_sf", + "docs": [ + "Min net value accepted to be found in a position after any lending action in an obligation (scaled by quote currency decimals)" + ], + "type": "u128" + }, + { + "name": "min_value_skip_liquidation_ltv_checks", + "docs": [ + "Minimum value to enforce smallest ltv priority checks on the collateral reserves on liquidation" + ], + "type": "u64" + }, + { + "name": "name", + "docs": ["Market name, zero-padded."], + "type": { + "array": ["u8", 32] + } + }, + { + "name": "min_value_skip_liquidation_bf_checks", + "docs": [ + "Minimum value to enforce highest borrow factor priority checks on the debt reserves on liquidation" + ], + "type": "u64" + }, + { + "name": "individual_autodeleverage_margin_call_period_secs", + "docs": [ + "Time (in seconds) that must pass before liquidation is allowed on an obligation that has", + "been individually marked for auto-deleveraging (by the risk council)." + ], + "type": "u64" + }, + { + "name": "min_initial_deposit_amount", + "docs": [ + "Minimum amount of deposit at creation of a reserve to prevent artificial inflation", + "Note: this amount cannot be recovered, the ctoken associated are never minted" + ], + "type": "u64" + }, + { + "name": "obligation_order_execution_enabled", + "docs": [ + "Whether the obligation orders should be evaluated during liquidations." + ], + "type": "u8" + }, + { + "name": "immutable", + "docs": ["Whether the lending market is set as immutable."], + "type": "u8" + }, + { + "name": "obligation_order_creation_enabled", + "docs": [ + "Whether new obligation orders can be created.", + "Note: updating or cancelling existing orders is *not* affected by this flag." + ], + "type": "u8" + }, + { + "name": "price_triggered_liquidation_disabled", + "docs": [ + "Whether the liquidation operations that are triggered by price changes should be disabled.", + "This includes regular liquidation (i.e. LTV exceeding the unhealthy threshold) and some", + "obligation orders' execution.", + "", + "*Caution:* this flag is *disabling* the liquidations when `1` - contrary to all the other", + "liquidation-driving flags (see e.g. [Self::autodeleverage_enabled])." + ], + "type": "u8" + }, + { + "name": "padding2", + "type": { + "array": ["u8", 4] + } + }, + { + "name": "proposer_authority", + "docs": [ + "Authority that can propose creating of new reserves but cannot enable them." + ], + "type": "pubkey" + }, + { + "name": "padding1", + "type": { + "array": ["u64", 165] + } + } + ] + } + }, + { + "name": "Obligation", + "docs": ["Lending market obligation state"], + "type": { + "kind": "struct", + "fields": [ + { + "name": "tag", + "docs": ["Version of the struct"], + "type": "u64" + }, + { + "name": "last_update", + "docs": [ + "Last update to collateral, liquidity, or their market values" + ], + "type": { + "defined": { + "name": "LastUpdate" + } + } + }, + { + "name": "lending_market", + "docs": ["Lending market address"], + "type": "pubkey" + }, + { + "name": "owner", + "docs": ["Owner authority which can borrow liquidity"], + "type": "pubkey" + }, + { + "name": "deposits", + "docs": [ + "Deposited collateral for the obligation, unique by deposit reserve address" + ], + "type": { + "array": [ + { + "defined": { + "name": "ObligationCollateral" + } + }, + 8 + ] + } + }, + { + "name": "lowest_reserve_deposit_liquidation_ltv", + "docs": [ + "Worst LTV for the collaterals backing the loan, represented as a percentage" + ], + "type": "u64" + }, + { + "name": "deposited_value_sf", + "docs": ["Market value of deposits (scaled fraction)"], + "type": "u128" + }, + { + "name": "borrows", + "docs": [ + "Borrowed liquidity for the obligation, unique by borrow reserve address" + ], + "type": { + "array": [ + { + "defined": { + "name": "ObligationLiquidity" + } + }, + 5 + ] + } + }, + { + "name": "borrow_factor_adjusted_debt_value_sf", + "docs": [ + "Risk adjusted market value of borrows/debt (sum of price * borrowed_amount * borrow_factor) (scaled fraction)" + ], + "type": "u128" + }, + { + "name": "borrowed_assets_market_value_sf", + "docs": [ + "Market value of borrows - used for max_liquidatable_borrowed_amount (scaled fraction)" + ], + "type": "u128" + }, + { + "name": "allowed_borrow_value_sf", + "docs": [ + "The maximum borrow value at the weighted average loan to value ratio (scaled fraction)" + ], + "type": "u128" + }, + { + "name": "unhealthy_borrow_value_sf", + "docs": [ + "The dangerous borrow value at the weighted average liquidation threshold (scaled fraction)" + ], + "type": "u128" + }, + { + "name": "deposits_asset_tiers", + "docs": ["The asset tier of the deposits"], + "type": { + "array": ["u8", 8] + } + }, + { + "name": "borrows_asset_tiers", + "docs": ["The asset tier of the borrows"], + "type": { + "array": ["u8", 5] + } + }, + { + "name": "elevation_group", + "docs": ["The elevation group id the obligation opted into."], + "type": "u8" + }, + { + "name": "num_of_obsolete_deposit_reserves", + "docs": [ + "The number of obsolete reserves the obligation has a deposit in" + ], + "type": "u8" + }, + { + "name": "has_debt", + "docs": [ + "Marked = 1 if borrows array is not empty, 0 = borrows empty" + ], + "type": "u8" + }, + { + "name": "referrer", + "docs": ["Wallet address of the referrer"], + "type": "pubkey" + }, + { + "name": "borrowing_disabled", + "docs": ["Marked = 1 if borrowing disabled, 0 = borrowing enabled"], + "type": "u8" + }, + { + "name": "autodeleverage_target_ltv_pct", + "docs": [ + "A target LTV set by the risk council when marking this obligation for deleveraging.", + "Only effective when `deleveraging_margin_call_started_slot != 0`." + ], + "type": "u8" + }, + { + "name": "lowest_reserve_deposit_max_ltv_pct", + "docs": [ + "The lowest max LTV found amongst the collateral deposits" + ], + "type": "u8" + }, + { + "name": "num_of_obsolete_borrow_reserves", + "docs": [ + "The number of obsolete reserves the obligation has a borrow in" + ], + "type": "u8" + }, + { + "name": "reserved", + "type": { + "array": ["u8", 4] + } + }, + { + "name": "highest_borrow_factor_pct", + "type": "u64" + }, + { + "name": "autodeleverage_margin_call_started_timestamp", + "docs": [ + "A timestamp at which the risk council most-recently marked this obligation for deleveraging.", + "Zero if not currently subject to deleveraging." + ], + "type": "u64" + }, + { + "name": "orders", + "docs": [ + "Owner-defined, liquidator-executed orders applicable to this obligation.", + "Typical use-cases would be a stop-loss and a take-profit (possibly co-existing)." + ], + "type": { + "array": [ + { + "defined": { + "name": "ObligationOrder" + } + }, + 2 + ] + } + }, + { + "name": "padding3", + "type": { + "array": ["u64", 93] + } + } + ] + } + }, + { + "name": "ReferrerState", + "type": { + "kind": "struct", + "fields": [ + { + "name": "short_url", + "type": "pubkey" + }, + { + "name": "owner", + "type": "pubkey" + } + ] + } + }, + { + "name": "ReferrerTokenState", + "docs": [ + "Referrer account -> each owner can have multiple accounts for specific reserves" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "referrer", + "docs": ["Pubkey of the referrer/owner"], + "type": "pubkey" + }, + { + "name": "mint", + "docs": ["Token mint for the account"], + "type": "pubkey" + }, + { + "name": "amount_unclaimed_sf", + "docs": [ + "Amount that has been accumulated and not claimed yet -> available to claim (scaled fraction)" + ], + "type": "u128" + }, + { + "name": "amount_cumulative_sf", + "docs": [ + "Amount that has been accumulated in total -> both already claimed and unclaimed (scaled fraction)" + ], + "type": "u128" + }, + { + "name": "bump", + "docs": ["Referrer token state bump, used for address validation"], + "type": "u64" + }, + { + "name": "padding", + "type": { + "array": ["u64", 31] + } + } + ] + } + }, + { + "name": "ShortUrl", + "type": { + "kind": "struct", + "fields": [ + { + "name": "referrer", + "type": "pubkey" + }, + { + "name": "short_url", + "type": "string" + } + ] + } + }, + { + "name": "UserMetadata", + "docs": [ + "Referrer account -> each owner can have multiple accounts for specific reserves" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "referrer", + "docs": [ + "Pubkey of the referrer/owner - pubkey::default if no referrer" + ], + "type": "pubkey" + }, + { + "name": "bump", + "docs": ["Bump used for validation of account address"], + "type": "u64" + }, + { + "name": "user_lookup_table", + "docs": [ + "User lookup table - used to store all user accounts - atas for each reserve mint, each obligation PDA, UserMetadata itself and all referrer_token_states if there is a referrer" + ], + "type": "pubkey" + }, + { + "name": "owner", + "docs": ["User metadata account owner"], + "type": "pubkey" + }, + { + "name": "padding1", + "type": { + "array": ["u64", 51] + } + }, + { + "name": "padding2", + "type": { + "array": ["u64", 64] + } + } + ] + } + }, + { + "name": "Reserve", + "type": { + "kind": "struct", + "fields": [ + { + "name": "version", + "docs": ["Version of the reserve"], + "type": "u64" + }, + { + "name": "last_update", + "docs": ["Last slot when supply and rates updated"], + "type": { + "defined": { + "name": "LastUpdate" + } + } + }, + { + "name": "lending_market", + "docs": ["Lending market address"], + "type": "pubkey" + }, + { + "name": "farm_collateral", + "type": "pubkey" + }, + { + "name": "farm_debt", + "type": "pubkey" + }, + { + "name": "liquidity", + "docs": ["Reserve liquidity"], + "type": { + "defined": { + "name": "ReserveLiquidity" + } + } + }, + { + "name": "reserve_liquidity_padding", + "type": { + "array": ["u64", 150] + } + }, + { + "name": "collateral", + "docs": ["Reserve collateral"], + "type": { + "defined": { + "name": "ReserveCollateral" + } + } + }, + { + "name": "reserve_collateral_padding", + "type": { + "array": ["u64", 150] + } + }, + { + "name": "config", + "docs": ["Reserve configuration values"], + "type": { + "defined": { + "name": "ReserveConfig" + } + } + }, + { + "name": "config_padding", + "type": { + "array": ["u64", 116] + } + }, + { + "name": "borrowed_amount_outside_elevation_group", + "type": "u64" + }, + { + "name": "borrowed_amounts_against_this_reserve_in_elevation_groups", + "docs": [ + "Amount of token borrowed in lamport of debt asset in the given", + "elevation group when this reserve is part of the collaterals." + ], + "type": { + "array": ["u64", 32] + } + }, + { + "name": "padding", + "type": { + "array": ["u64", 207] + } + } + ] + } + } + ] +} diff --git a/crates/core/src/scenarios/protocols/kamino/v1/overrides.yaml b/crates/core/src/scenarios/protocols/kamino/v1/overrides.yaml new file mode 100644 index 00000000..2d41deb1 --- /dev/null +++ b/crates/core/src/scenarios/protocols/kamino/v1/overrides.yaml @@ -0,0 +1,55 @@ +protocol: kamino +version: v1.12.6 +account_type: Reserve +idl_file_path: idl.json + +tags: + - lending + - risk-engine + - defi + +templates: + - id: kamino-reserve-state + name: Override Reserve Liquidity & Rates + description: Override Kamino Reserve liquidity and interest rate data + idl_account_name: Reserve + properties: + [ + liquidity.available_amount, + liquidity.borrowed_amount_sf, + liquidity.market_price_sf, + liquidity.cumulative_borrow_rate_bsf, + ] + address: + type: pubkey + + - id: kamino-reserve-config + name: Override Reserve Risk Configuration + description: Override Kamino Reserve risk parameters and liquidation settings + idl_account_name: Reserve + properties: + [ + config.loan_to_value_pct, + config.liquidation_threshold_pct, + config.min_liquidation_bonus_bps, + config.max_liquidation_bonus_bps, + ] + addresses: + type: pubkey + + - id: kamino-obligation-health + name: Override Obligation Health + description: Override Kamino Obligation health metrics for testing liquidation scenarios + idl_account_name: Obligation + properties: + [ + last_update_slot, + lending_market, + owner, + deposited_value_sf, + borrowed_value_sf, + allowed_borrow_value_sf, + unhealthy_borrow_value_sf, + ] + address: + type: pubkey diff --git a/crates/core/src/scenarios/registry.rs b/crates/core/src/scenarios/registry.rs index 33bd09a1..0194f5c7 100644 --- a/crates/core/src/scenarios/registry.rs +++ b/crates/core/src/scenarios/registry.rs @@ -13,6 +13,9 @@ pub const RAYDIUM_CLMM_IDL_CONTENT: &str = include_str!("./protocols/raydium/v3/ pub const RAYDIUM_CLMM_OVERRIDES_CONTENT: &str = include_str!("./protocols/raydium/v3/overrides.yaml"); +pub const KAMINO_V1_IDL_CONTENT: &str = include_str!("./protocols/kamino/v1/idl.json"); +pub const KAMINO_V1_OVERRIDES_CONTENT: &str = include_str!("./protocols/kamino/v1/overrides.yaml"); + /// Registry for managing override templates loaded from YAML files #[derive(Clone, Debug, Default)] pub struct TemplateRegistry { @@ -27,6 +30,7 @@ impl TemplateRegistry { default.load_pyth_overrides(); default.load_jupiter_overrides(); default.load_raydium_overrides(); + default.load_kamino_overrides(); default } @@ -42,6 +46,18 @@ impl TemplateRegistry { ); } + pub fn load_raydium_overrides(&mut self) { + self.load_protocol_overrides( + RAYDIUM_CLMM_IDL_CONTENT, + RAYDIUM_CLMM_OVERRIDES_CONTENT, + "raydium", + ); + } + + pub fn load_kamino_overrides(&mut self) { + self.load_protocol_overrides(KAMINO_V1_IDL_CONTENT, KAMINO_V1_OVERRIDES_CONTENT, "kamino"); + } + fn load_protocol_overrides( &mut self, idl_content: &str, @@ -69,28 +85,6 @@ impl TemplateRegistry { } } - pub fn load_raydium_overrides(&mut self) { - let idl = match serde_json::from_str(RAYDIUM_CLMM_IDL_CONTENT) { - Ok(idl) => idl, - Err(e) => panic!("unable to load raydium idl: {}", e), - }; - - let Ok(collection) = - serde_yaml::from_str::(RAYDIUM_CLMM_OVERRIDES_CONTENT) - else { - panic!("unable to load raydium overrides"); - }; - - // Convert all templates in the collection - let templates = collection.to_override_templates(idl); - - // Register each template - for template in templates { - let template_id = template.id.clone(); - self.templates.insert(template_id.clone(), template); - } - } - /// Get a template by ID pub fn get(&self, template_id: &str) -> Option<&OverrideTemplate> { self.templates.get(template_id) From e1762ac90cc645e8391bf091071ff2e7526473bb Mon Sep 17 00:00:00 2001 From: JKrishnaD Date: Sun, 23 Nov 2025 23:10:59 +0530 Subject: [PATCH 2/5] test: added kamino and raydium scenario tests --- crates/core/src/scenarios/registry.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/crates/core/src/scenarios/registry.rs b/crates/core/src/scenarios/registry.rs index 0194f5c7..47cffa65 100644 --- a/crates/core/src/scenarios/registry.rs +++ b/crates/core/src/scenarios/registry.rs @@ -135,10 +135,10 @@ mod tests { fn test_registry_loads_both_protocols() { let registry = TemplateRegistry::new(); - // Should have Pyth (4 templates) + Jupiter (1 template) + Raydium(3 templates) = 5 total + // Should have Pyth (4 templates) + Jupiter (1 template) + Raydium(3 templates) + Kamino(3 templates) = 11 total assert_eq!( registry.count(), - 8, + 11, "Registry should load 5 templates total" ); @@ -152,6 +152,10 @@ mod tests { assert!(registry.contains("raydium-clmm-sol-usdc")); assert!(registry.contains("raydium-clmm-btc-usdc")); assert!(registry.contains("raydium-clmm-eth-usdc")); + + assert!(registry.contains("kamino-reserve-state")); + assert!(registry.contains("kamino-reserve-config")); + assert!(registry.contains("kamino-obligation-health")) } #[test] @@ -188,6 +192,16 @@ mod tests { let jupiter_templates = registry.by_protocol("Jupiter"); assert_eq!(jupiter_templates.len(), 1, "Should have 1 Jupiter template"); + + let raydium_templates = registry.by_protocol("Raydium"); + assert_eq!( + raydium_templates.len(), + 3, + "Should have 3 Raydium templates" + ); + + let kamino_templates = registry.by_protocol("Kamino"); + assert_eq!(kamino_templates.len(), 3, "Should have 3 Kamino templates"); } #[test] @@ -234,9 +248,12 @@ mod tests { let registry = TemplateRegistry::new(); let ids = registry.list_ids(); - assert_eq!(ids.len(), 8); + assert_eq!(ids.len(), 11); assert!(ids.contains(&"raydium-clmm-sol-usdc".to_string())); assert!(ids.contains(&"jupiter-token-ledger-override".to_string())); assert!(ids.contains(&"pyth-sol-usd-v2".to_string())); + assert!(ids.contains(&"kamino-reserve-state".to_string())); + assert!(ids.contains(&"kamino-reserve-config".to_string())); + assert!(ids.contains(&"kamino-obligation-health".to_string())); } } From f1f554f7be150cc82d34e76ae561f3986112a979 Mon Sep 17 00:00:00 2001 From: JKrishnaD Date: Wed, 26 Nov 2025 11:42:32 +0530 Subject: [PATCH 3/5] fix: correct overrides to pass the tests --- .../protocols/kamino/v1/overrides.yaml | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/core/src/scenarios/protocols/kamino/v1/overrides.yaml b/crates/core/src/scenarios/protocols/kamino/v1/overrides.yaml index 2d41deb1..2439e187 100644 --- a/crates/core/src/scenarios/protocols/kamino/v1/overrides.yaml +++ b/crates/core/src/scenarios/protocols/kamino/v1/overrides.yaml @@ -15,10 +15,10 @@ templates: idl_account_name: Reserve properties: [ - liquidity.available_amount, - liquidity.borrowed_amount_sf, - liquidity.market_price_sf, - liquidity.cumulative_borrow_rate_bsf, + "liquidity.available_amount", + "liquidity.borrowed_amount_sf", + "liquidity.market_price_sf", + "liquidity.cumulative_borrow_rate_bsf", ] address: type: pubkey @@ -29,12 +29,12 @@ templates: idl_account_name: Reserve properties: [ - config.loan_to_value_pct, - config.liquidation_threshold_pct, - config.min_liquidation_bonus_bps, - config.max_liquidation_bonus_bps, + "config.loan_to_value_pct", + "config.liquidation_threshold_pct", + "config.min_liquidation_bonus_bps", + "config.max_liquidation_bonus_bps", ] - addresses: + address: type: pubkey - id: kamino-obligation-health @@ -43,13 +43,13 @@ templates: idl_account_name: Obligation properties: [ - last_update_slot, - lending_market, - owner, - deposited_value_sf, - borrowed_value_sf, - allowed_borrow_value_sf, - unhealthy_borrow_value_sf, + "last_update_slot", + "lending_market", + "owner", + "deposited_value_sf", + "borrowed_value_sf", + "allowed_borrow_value_sf", + "unhealthy_borrow_value_sf", ] address: type: pubkey From ac309a1d9622dbbf3be86fc0dfd302eca4b5f54d Mon Sep 17 00:00:00 2001 From: JKrishnaD Date: Wed, 26 Nov 2025 18:53:30 +0530 Subject: [PATCH 4/5] fix: added readme --- crates/core/src/scenarios/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/core/src/scenarios/README.md b/crates/core/src/scenarios/README.md index 9d447bf5..c217d48f 100644 --- a/crates/core/src/scenarios/README.md +++ b/crates/core/src/scenarios/README.md @@ -15,6 +15,7 @@ Protocols that are natively supported by Surfpool will have their IDLs included **Currently supported protocols:** - **Pyth v2** - Price oracle with 4 price feed templates (SOL/USD, BTC/USD, ETH/BTC, ETH/USD) - **Jupiter v6** - DEX aggregator with TokenLedger manipulation template +- **Kamino v1.x** – Lending protocol with Reserve liquidity, risk config, and Obligation health override templates For custom protocols, an IDL can be registered at runtime using the [`surfnet_registerIdl`](https://docs.surfpool.run/rpc/cheatcodes#surfnet-registeridl) RPC cheatcode. From 777b9da8bb6eea6c5446cbab43c4d30ea399d6f0 Mon Sep 17 00:00:00 2001 From: JKrishnaD Date: Wed, 26 Nov 2025 21:19:31 +0530 Subject: [PATCH 5/5] fix: conflicts resloved --- crates/core/src/scenarios/registry.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/core/src/scenarios/registry.rs b/crates/core/src/scenarios/registry.rs index 80b32c5c..64d41f8f 100644 --- a/crates/core/src/scenarios/registry.rs +++ b/crates/core/src/scenarios/registry.rs @@ -61,7 +61,7 @@ impl TemplateRegistry { pub fn load_kamino_overrides(&mut self) { self.load_protocol_overrides(KAMINO_V1_IDL_CONTENT, KAMINO_V1_OVERRIDES_CONTENT, "kamino"); } - + pub fn load_drift_overrides(&mut self) { self.load_protocol_overrides(DRIFT_V2_IDL_CONTENT, DRIFT_V2_OVERRIDES_CONTENT, "drift"); } @@ -164,7 +164,7 @@ mod tests { assert!(registry.contains("kamino-reserve-state")); assert!(registry.contains("kamino-reserve-config")); assert!(registry.contains("kamino-obligation-health")); - + assert!(registry.contains("drift-perp-market")); assert!(registry.contains("drift-spot-market")); assert!(registry.contains("drift-user-state")); @@ -261,7 +261,7 @@ mod tests { let registry = TemplateRegistry::new(); let ids = registry.list_ids(); - assert_eq!(ids.len(), 11); + assert_eq!(ids.len(), 15); assert!(ids.contains(&"raydium-clmm-sol-usdc".to_string())); assert!(ids.contains(&"jupiter-token-ledger-override".to_string())); assert!(ids.contains(&"pyth-sol-usd-v2".to_string()));