Skip to main content

klend_interface/helpers/
borrow.rs

1use solana_instruction::Instruction;
2use solana_pubkey::Pubkey;
3
4use super::{
5    common::{
6        build_deposit_reserves_remaining, build_refresh_all_obligation_reserves,
7        build_refresh_obligation, build_refresh_reserve,
8    },
9    info::{FarmsAccounts, ObligationInfo, ReserveInfo},
10};
11use crate::{
12    instructions::borrow::{
13        borrow_obligation_liquidity_v2, rollover_fixed_term_borrow as rollover_ix,
14        BorrowObligationLiquidityV2Accounts, RolloverFixedTermBorrowAccounts,
15    },
16    pda::{self, ReservePdas},
17    KLEND_PROGRAM_ID,
18};
19
20/// Build instructions to borrow liquidity from a reserve against an obligation.
21///
22/// `obligation_reserves` should contain [`ReserveInfo`] for every deposit and
23/// borrow reserve on the obligation. Any reserves not already refreshed by this
24/// helper are refreshed automatically so that `refresh_obligation` succeeds.
25///
26/// Returns: `[refresh_other_reserves..., refresh_borrow_reserve, refresh_obligation, borrow_obligation_liquidity_v2]`
27pub fn borrow(
28    owner: Pubkey,
29    borrow_reserve: &ReserveInfo,
30    obligation: &ObligationInfo,
31    obligation_reserves: &[ReserveInfo],
32    user_destination_liquidity: Pubkey,
33    liquidity_amount: u64,
34    farms: Option<&FarmsAccounts>,
35) -> Vec<Instruction> {
36    let pdas = ReservePdas::derive(&KLEND_PROGRAM_ID, &borrow_reserve.address);
37    let (lma, _) = pda::lending_market_authority(&KLEND_PROGRAM_ID, &borrow_reserve.lending_market);
38
39    let referrer_token_state = obligation.referrer.map(|referrer| {
40        pda::referrer_token_state(&KLEND_PROGRAM_ID, &referrer, &borrow_reserve.address).0
41    });
42
43    let remaining = build_deposit_reserves_remaining(obligation);
44
45    let mut ixs = build_refresh_all_obligation_reserves(
46        obligation,
47        obligation_reserves,
48        &[borrow_reserve.address],
49    );
50    ixs.push(build_refresh_reserve(borrow_reserve));
51    ixs.push(build_refresh_obligation(
52        &borrow_reserve.lending_market,
53        obligation,
54    ));
55    ixs.push(borrow_obligation_liquidity_v2(
56        BorrowObligationLiquidityV2Accounts {
57            owner,
58            obligation: obligation.address,
59            lending_market: borrow_reserve.lending_market,
60            lending_market_authority: lma,
61            borrow_reserve: borrow_reserve.address,
62            borrow_reserve_liquidity_mint: borrow_reserve.liquidity_mint,
63            reserve_source_liquidity: pdas.liquidity_supply_vault,
64            borrow_reserve_liquidity_fee_receiver: pdas.fee_vault,
65            user_destination_liquidity,
66            referrer_token_state,
67            token_program: borrow_reserve.liquidity_token_program,
68            obligation_farm_user_state: farms.map(|f| f.obligation_farm_user_state),
69            reserve_farm_state: farms.map(|f| f.reserve_farm_state),
70        },
71        liquidity_amount,
72        remaining,
73    ));
74
75    ixs
76}
77
78/// Build instructions to roll over a fixed-term borrow into a new reserve (or same reserve).
79///
80/// `obligation_reserves` should contain [`ReserveInfo`] for every deposit and
81/// borrow reserve on the obligation. The source and target reserves will be
82/// refreshed automatically.
83///
84/// Returns: `[refresh_other_reserves..., refresh_source, refresh_target (if different), refresh_obligation, rollover_fixed_term_borrow]`
85pub fn rollover_fixed_term_borrow(
86    payer: Pubkey,
87    obligation: &ObligationInfo,
88    obligation_reserves: &[ReserveInfo],
89    source_reserve: &ReserveInfo,
90    target_reserve: &ReserveInfo,
91    source_farms: Option<&FarmsAccounts>,
92    target_farms: Option<&FarmsAccounts>,
93) -> Vec<Instruction> {
94    let (lma, _) = pda::lending_market_authority(&KLEND_PROGRAM_ID, &source_reserve.lending_market);
95    let source_pdas = ReservePdas::derive(&KLEND_PROGRAM_ID, &source_reserve.address);
96    let target_pdas = ReservePdas::derive(&KLEND_PROGRAM_ID, &target_reserve.address);
97
98    let mut already_refreshed = vec![source_reserve.address];
99    let same_reserve = source_reserve.address == target_reserve.address;
100    if !same_reserve {
101        already_refreshed.push(target_reserve.address);
102    }
103
104    let mut ixs =
105        build_refresh_all_obligation_reserves(obligation, obligation_reserves, &already_refreshed);
106    ixs.push(build_refresh_reserve(source_reserve));
107    if !same_reserve {
108        ixs.push(build_refresh_reserve(target_reserve));
109    }
110    ixs.push(build_refresh_obligation(
111        &source_reserve.lending_market,
112        obligation,
113    ));
114
115    ixs.push(rollover_ix(RolloverFixedTermBorrowAccounts {
116        payer,
117        obligation: obligation.address,
118        lending_market: source_reserve.lending_market,
119        lending_market_authority: lma,
120        source_borrow_reserve: source_reserve.address,
121        target_borrow_reserve: target_reserve.address,
122        liquidity_mint: source_reserve.liquidity_mint,
123        source_borrow_reserve_liquidity: source_pdas.liquidity_supply_vault,
124        target_borrow_reserve_liquidity: target_pdas.liquidity_supply_vault,
125        token_program: source_reserve.liquidity_token_program,
126        source_obligation_farm_user_state: source_farms.map(|f| f.obligation_farm_user_state),
127        source_reserve_farm_state: source_farms.map(|f| f.reserve_farm_state),
128        target_obligation_farm_user_state: target_farms.map(|f| f.obligation_farm_user_state),
129        target_reserve_farm_state: target_farms.map(|f| f.reserve_farm_state),
130    }));
131
132    ixs
133}