Skip to main content

klend_interface/helpers/
common.rs

1use solana_instruction::{AccountMeta, Instruction};
2use solana_pubkey::Pubkey;
3
4use super::info::{ObligationInfo, ReserveInfo};
5use crate::{
6    instructions::refresh::{RefreshObligationAccounts, RefreshReserveAccounts},
7    pda,
8    util::writable,
9    KLEND_PROGRAM_ID,
10};
11
12pub(super) fn build_refresh_reserve(reserve: &ReserveInfo) -> Instruction {
13    crate::instructions::refresh::refresh_reserve(RefreshReserveAccounts {
14        reserve: reserve.address,
15        lending_market: reserve.lending_market,
16        pyth_oracle: reserve.pyth_oracle,
17        switchboard_price_oracle: reserve.switchboard_price_oracle,
18        switchboard_twap_oracle: reserve.switchboard_twap_oracle,
19        scope_prices: reserve.scope_prices,
20    })
21}
22
23pub(super) fn build_refresh_obligation_remaining_accounts(
24    obligation: &ObligationInfo,
25) -> Vec<AccountMeta> {
26    let referrer_count = if obligation.referrer.is_some() {
27        obligation.borrow_reserves.len()
28    } else {
29        0
30    };
31    let mut remaining = Vec::with_capacity(
32        obligation.deposit_reserves.len() + obligation.borrow_reserves.len() + referrer_count,
33    );
34
35    for r in &obligation.deposit_reserves {
36        remaining.push(writable(*r));
37    }
38    for r in &obligation.borrow_reserves {
39        remaining.push(writable(*r));
40    }
41    if let Some(referrer) = obligation.referrer {
42        for borrow_reserve in &obligation.borrow_reserves {
43            let (rts, _) = pda::referrer_token_state(&KLEND_PROGRAM_ID, &referrer, borrow_reserve);
44            remaining.push(writable(rts));
45        }
46    }
47
48    remaining
49}
50
51pub(super) fn build_refresh_obligation(
52    lending_market: &Pubkey,
53    obligation: &ObligationInfo,
54) -> Instruction {
55    let remaining = build_refresh_obligation_remaining_accounts(obligation);
56    crate::instructions::refresh::refresh_obligation(
57        RefreshObligationAccounts {
58            lending_market: *lending_market,
59            obligation: obligation.address,
60        },
61        remaining,
62    )
63}
64
65pub(super) fn build_deposit_reserves_remaining(obligation: &ObligationInfo) -> Vec<AccountMeta> {
66    obligation
67        .deposit_reserves
68        .iter()
69        .map(|r| writable(*r))
70        .collect()
71}
72
73/// Build refresh instructions for all unique obligation reserves, skipping any
74/// already-refreshed ones.
75///
76/// This ensures that `refresh_obligation` won't fail due to stale reserves in
77/// multi-position obligations. Reserves not found in `obligation_reserves` are
78/// silently skipped (the caller is responsible for providing all required
79/// reserves via `obligation_reserves`).
80pub(super) fn build_refresh_all_obligation_reserves(
81    obligation: &ObligationInfo,
82    obligation_reserves: &[ReserveInfo],
83    already_refreshed: &[Pubkey],
84) -> Vec<Instruction> {
85    let mut seen =
86        Vec::with_capacity(obligation.deposit_reserves.len() + obligation.borrow_reserves.len());
87    let mut ixs = Vec::new();
88
89    for key in obligation
90        .deposit_reserves
91        .iter()
92        .chain(obligation.borrow_reserves.iter())
93    {
94        if already_refreshed.contains(key) || seen.contains(key) {
95            continue;
96        }
97        seen.push(*key);
98
99        if let Some(info) = obligation_reserves.iter().find(|r| r.address == *key) {
100            ixs.push(build_refresh_reserve(info));
101        }
102    }
103
104    ixs
105}