spl_token_lending/
processor.rs

1//! Program state processor
2
3use crate::{
4    error::LendingError,
5    instruction::LendingInstruction,
6    math::{Decimal, Rate, TryAdd, TryDiv, TryMul},
7    pyth,
8    state::{
9        CalculateBorrowResult, CalculateLiquidationResult, CalculateRepayResult,
10        InitLendingMarketParams, InitObligationParams, InitReserveParams, LendingMarket,
11        NewReserveCollateralParams, NewReserveLiquidityParams, Obligation, Reserve,
12        ReserveCollateral, ReserveConfig, ReserveLiquidity,
13    },
14};
15use num_traits::FromPrimitive;
16use solana_program::{
17    account_info::{next_account_info, AccountInfo},
18    decode_error::DecodeError,
19    entrypoint::ProgramResult,
20    instruction::Instruction,
21    msg,
22    program::{invoke, invoke_signed},
23    program_error::{PrintProgramError, ProgramError},
24    program_pack::{IsInitialized, Pack},
25    pubkey::Pubkey,
26    sysvar::{clock::Clock, rent::Rent, Sysvar},
27};
28use spl_token::solana_program::instruction::AccountMeta;
29use spl_token::state::{Account, Mint};
30use std::convert::TryInto;
31
32/// Processes an instruction
33pub fn process_instruction(
34    program_id: &Pubkey,
35    accounts: &[AccountInfo],
36    input: &[u8],
37) -> ProgramResult {
38    let instruction = LendingInstruction::unpack(input)?;
39    match instruction {
40        LendingInstruction::InitLendingMarket {
41            owner,
42            quote_currency,
43        } => {
44            msg!("Instruction: Init Lending Market");
45            process_init_lending_market(program_id, owner, quote_currency, accounts)
46        }
47        LendingInstruction::SetLendingMarketOwner { new_owner } => {
48            msg!("Instruction: Set Lending Market Owner");
49            process_set_lending_market_owner(program_id, new_owner, accounts)
50        }
51        LendingInstruction::InitReserve {
52            liquidity_amount,
53            config,
54        } => {
55            msg!("Instruction: Init Reserve");
56            process_init_reserve(program_id, liquidity_amount, config, accounts)
57        }
58        LendingInstruction::RefreshReserve => {
59            msg!("Instruction: Refresh Reserve");
60            process_refresh_reserve(program_id, accounts)
61        }
62        LendingInstruction::DepositReserveLiquidity { liquidity_amount } => {
63            msg!("Instruction: Deposit Reserve Liquidity");
64            process_deposit_reserve_liquidity(program_id, liquidity_amount, accounts)
65        }
66        LendingInstruction::RedeemReserveCollateral { collateral_amount } => {
67            msg!("Instruction: Redeem Reserve Collateral");
68            process_redeem_reserve_collateral(program_id, collateral_amount, accounts)
69        }
70        LendingInstruction::InitObligation => {
71            msg!("Instruction: Init Obligation");
72            process_init_obligation(program_id, accounts)
73        }
74        LendingInstruction::RefreshObligation => {
75            msg!("Instruction: Refresh Obligation");
76            process_refresh_obligation(program_id, accounts)
77        }
78        LendingInstruction::DepositObligationCollateral { collateral_amount } => {
79            msg!("Instruction: Deposit Obligation Collateral");
80            process_deposit_obligation_collateral(program_id, collateral_amount, accounts)
81        }
82        LendingInstruction::WithdrawObligationCollateral { collateral_amount } => {
83            msg!("Instruction: Withdraw Obligation Collateral");
84            process_withdraw_obligation_collateral(program_id, collateral_amount, accounts)
85        }
86        LendingInstruction::BorrowObligationLiquidity {
87            liquidity_amount,
88            slippage_limit,
89        } => {
90            msg!("Instruction: Borrow Obligation Liquidity");
91            process_borrow_obligation_liquidity(
92                program_id,
93                liquidity_amount,
94                slippage_limit,
95                accounts,
96            )
97        }
98        LendingInstruction::RepayObligationLiquidity { liquidity_amount } => {
99            msg!("Instruction: Repay Obligation Liquidity");
100            process_repay_obligation_liquidity(program_id, liquidity_amount, accounts)
101        }
102        LendingInstruction::LiquidateObligation { liquidity_amount } => {
103            msg!("Instruction: Liquidate Obligation");
104            process_liquidate_obligation(program_id, liquidity_amount, accounts)
105        }
106        LendingInstruction::FlashLoan { amount } => {
107            msg!("Instruction: Flash Loan");
108            process_flash_loan(program_id, amount, accounts)
109        }
110        LendingInstruction::ModifyReserveConfig { new_config } => {
111            msg!("Instruction: Modify Reserve Config");
112            process_modify_reserve_config(program_id, new_config, accounts)
113        }
114    }
115}
116
117fn process_init_lending_market(
118    program_id: &Pubkey,
119    owner: Pubkey,
120    quote_currency: [u8; 32],
121    accounts: &[AccountInfo],
122) -> ProgramResult {
123    let account_info_iter = &mut accounts.iter();
124    let lending_market_info = next_account_info(account_info_iter)?;
125    let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?;
126    let token_program_id = next_account_info(account_info_iter)?;
127    let oracle_program_id = next_account_info(account_info_iter)?;
128
129    assert_rent_exempt(rent, lending_market_info)?;
130    let mut lending_market = assert_uninitialized::<LendingMarket>(lending_market_info)?;
131    if lending_market_info.owner != program_id {
132        msg!("Lending market provided is not owned by the lending program");
133        return Err(LendingError::InvalidAccountOwner.into());
134    }
135
136    lending_market.init(InitLendingMarketParams {
137        bump_seed: Pubkey::find_program_address(&[lending_market_info.key.as_ref()], program_id).1,
138        owner,
139        quote_currency,
140        token_program_id: *token_program_id.key,
141        oracle_program_id: *oracle_program_id.key,
142    });
143    LendingMarket::pack(lending_market, &mut lending_market_info.data.borrow_mut())?;
144
145    Ok(())
146}
147
148#[inline(never)] // avoid stack frame limit
149fn process_set_lending_market_owner(
150    program_id: &Pubkey,
151    new_owner: Pubkey,
152    accounts: &[AccountInfo],
153) -> ProgramResult {
154    let account_info_iter = &mut accounts.iter();
155    let lending_market_info = next_account_info(account_info_iter)?;
156    let lending_market_owner_info = next_account_info(account_info_iter)?;
157
158    let mut lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
159    if lending_market_info.owner != program_id {
160        msg!("Lending market provided is not owned by the lending program");
161        return Err(LendingError::InvalidAccountOwner.into());
162    }
163    if &lending_market.owner != lending_market_owner_info.key {
164        msg!("Lending market owner does not match the lending market owner provided");
165        return Err(LendingError::InvalidMarketOwner.into());
166    }
167    if !lending_market_owner_info.is_signer {
168        msg!("Lending market owner provided must be a signer");
169        return Err(LendingError::InvalidSigner.into());
170    }
171
172    lending_market.owner = new_owner;
173    LendingMarket::pack(lending_market, &mut lending_market_info.data.borrow_mut())?;
174
175    Ok(())
176}
177
178fn process_init_reserve(
179    program_id: &Pubkey,
180    liquidity_amount: u64,
181    config: ReserveConfig,
182    accounts: &[AccountInfo],
183) -> ProgramResult {
184    if liquidity_amount == 0 {
185        msg!("Reserve must be initialized with liquidity");
186        return Err(LendingError::InvalidAmount.into());
187    }
188
189    config.validate()?;
190
191    let account_info_iter = &mut accounts.iter().peekable();
192    let source_liquidity_info = next_account_info(account_info_iter)?;
193    let destination_collateral_info = next_account_info(account_info_iter)?;
194    let reserve_info = next_account_info(account_info_iter)?;
195    let reserve_liquidity_mint_info = next_account_info(account_info_iter)?;
196    let reserve_liquidity_supply_info = next_account_info(account_info_iter)?;
197    let reserve_liquidity_fee_receiver_info = next_account_info(account_info_iter)?;
198    let reserve_collateral_mint_info = next_account_info(account_info_iter)?;
199    let reserve_collateral_supply_info = next_account_info(account_info_iter)?;
200    let pyth_product_info = next_account_info(account_info_iter)?;
201    let pyth_price_info = next_account_info(account_info_iter)?;
202    let lending_market_info = next_account_info(account_info_iter)?;
203    let lending_market_authority_info = next_account_info(account_info_iter)?;
204    let lending_market_owner_info = next_account_info(account_info_iter)?;
205    let user_transfer_authority_info = next_account_info(account_info_iter)?;
206    let clock = &Clock::from_account_info(next_account_info(account_info_iter)?)?;
207    let rent_info = next_account_info(account_info_iter)?;
208    let rent = &Rent::from_account_info(rent_info)?;
209    let token_program_id = next_account_info(account_info_iter)?;
210
211    assert_rent_exempt(rent, reserve_info)?;
212    let mut reserve = assert_uninitialized::<Reserve>(reserve_info)?;
213    if reserve_info.owner != program_id {
214        msg!("Reserve provided is not owned by the lending program");
215        return Err(LendingError::InvalidAccountOwner.into());
216    }
217
218    if reserve_liquidity_supply_info.key == source_liquidity_info.key {
219        msg!("Reserve liquidity supply cannot be used as the source liquidity provided");
220        return Err(LendingError::InvalidAccountInput.into());
221    }
222
223    let lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
224    if lending_market_info.owner != program_id {
225        msg!("Lending market provided is not owned by the lending program");
226        return Err(LendingError::InvalidAccountOwner.into());
227    }
228    if &lending_market.token_program_id != token_program_id.key {
229        msg!("Lending market token program does not match the token program provided");
230        return Err(LendingError::InvalidTokenProgram.into());
231    }
232    if &lending_market.owner != lending_market_owner_info.key {
233        msg!("Lending market owner does not match the lending market owner provided");
234        return Err(LendingError::InvalidMarketOwner.into());
235    }
236    if !lending_market_owner_info.is_signer {
237        msg!("Lending market owner provided must be a signer");
238        return Err(LendingError::InvalidSigner.into());
239    }
240
241    if &lending_market.oracle_program_id != pyth_product_info.owner {
242        msg!("Pyth product account provided is not owned by the lending market oracle program");
243        return Err(LendingError::InvalidOracleConfig.into());
244    }
245    if &lending_market.oracle_program_id != pyth_price_info.owner {
246        msg!("Pyth price account provided is not owned by the lending market oracle program");
247        return Err(LendingError::InvalidOracleConfig.into());
248    }
249
250    let pyth_product_data = pyth_product_info.try_borrow_data()?;
251    let pyth_product = pyth::load::<pyth::Product>(&pyth_product_data)
252        .map_err(|_| ProgramError::InvalidAccountData)?;
253    if pyth_product.magic != pyth::MAGIC {
254        msg!("Pyth product account provided is not a valid Pyth account");
255        return Err(LendingError::InvalidOracleConfig.into());
256    }
257    if pyth_product.ver != pyth::VERSION_2 {
258        msg!("Pyth product account provided has a different version than expected");
259        return Err(LendingError::InvalidOracleConfig.into());
260    }
261    if pyth_product.atype != pyth::AccountType::Product as u32 {
262        msg!("Pyth product account provided is not a valid Pyth product account");
263        return Err(LendingError::InvalidOracleConfig.into());
264    }
265
266    let pyth_price_pubkey_bytes: &[u8; 32] = pyth_price_info
267        .key
268        .as_ref()
269        .try_into()
270        .map_err(|_| LendingError::InvalidAccountInput)?;
271    if &pyth_product.px_acc.val != pyth_price_pubkey_bytes {
272        msg!("Pyth product price account does not match the Pyth price provided");
273        return Err(LendingError::InvalidOracleConfig.into());
274    }
275
276    let quote_currency = get_pyth_product_quote_currency(pyth_product)?;
277    if lending_market.quote_currency != quote_currency {
278        msg!("Lending market quote currency does not match the oracle quote currency");
279        return Err(LendingError::InvalidOracleConfig.into());
280    }
281
282    let market_price = get_pyth_price(pyth_price_info, clock)?;
283
284    let authority_signer_seeds = &[
285        lending_market_info.key.as_ref(),
286        &[lending_market.bump_seed],
287    ];
288    let lending_market_authority_pubkey =
289        Pubkey::create_program_address(authority_signer_seeds, program_id)?;
290    if &lending_market_authority_pubkey != lending_market_authority_info.key {
291        msg!(
292            "Derived lending market authority does not match the lending market authority provided"
293        );
294        return Err(LendingError::InvalidMarketAuthority.into());
295    }
296
297    let reserve_liquidity_mint = unpack_mint(&reserve_liquidity_mint_info.data.borrow())?;
298    if reserve_liquidity_mint_info.owner != token_program_id.key {
299        msg!("Reserve liquidity mint is not owned by the token program provided");
300        return Err(LendingError::InvalidTokenOwner.into());
301    }
302
303    reserve.init(InitReserveParams {
304        current_slot: clock.slot,
305        lending_market: *lending_market_info.key,
306        liquidity: ReserveLiquidity::new(NewReserveLiquidityParams {
307            mint_pubkey: *reserve_liquidity_mint_info.key,
308            mint_decimals: reserve_liquidity_mint.decimals,
309            supply_pubkey: *reserve_liquidity_supply_info.key,
310            fee_receiver: *reserve_liquidity_fee_receiver_info.key,
311            oracle_pubkey: *pyth_price_info.key,
312            market_price,
313        }),
314        collateral: ReserveCollateral::new(NewReserveCollateralParams {
315            mint_pubkey: *reserve_collateral_mint_info.key,
316            supply_pubkey: *reserve_collateral_supply_info.key,
317        }),
318        config,
319    });
320
321    let collateral_amount = reserve.deposit_liquidity(liquidity_amount)?;
322    Reserve::pack(reserve, &mut reserve_info.data.borrow_mut())?;
323
324    spl_token_init_account(TokenInitializeAccountParams {
325        account: reserve_liquidity_supply_info.clone(),
326        mint: reserve_liquidity_mint_info.clone(),
327        owner: lending_market_authority_info.clone(),
328        rent: rent_info.clone(),
329        token_program: token_program_id.clone(),
330    })?;
331
332    spl_token_init_account(TokenInitializeAccountParams {
333        account: reserve_liquidity_fee_receiver_info.clone(),
334        mint: reserve_liquidity_mint_info.clone(),
335        owner: lending_market_authority_info.clone(),
336        rent: rent_info.clone(),
337        token_program: token_program_id.clone(),
338    })?;
339
340    spl_token_init_mint(TokenInitializeMintParams {
341        mint: reserve_collateral_mint_info.clone(),
342        authority: lending_market_authority_info.key,
343        rent: rent_info.clone(),
344        decimals: reserve_liquidity_mint.decimals,
345        token_program: token_program_id.clone(),
346    })?;
347
348    spl_token_init_account(TokenInitializeAccountParams {
349        account: reserve_collateral_supply_info.clone(),
350        mint: reserve_collateral_mint_info.clone(),
351        owner: lending_market_authority_info.clone(),
352        rent: rent_info.clone(),
353        token_program: token_program_id.clone(),
354    })?;
355
356    spl_token_init_account(TokenInitializeAccountParams {
357        account: destination_collateral_info.clone(),
358        mint: reserve_collateral_mint_info.clone(),
359        owner: user_transfer_authority_info.clone(),
360        rent: rent_info.clone(),
361        token_program: token_program_id.clone(),
362    })?;
363
364    spl_token_transfer(TokenTransferParams {
365        source: source_liquidity_info.clone(),
366        destination: reserve_liquidity_supply_info.clone(),
367        amount: liquidity_amount,
368        authority: user_transfer_authority_info.clone(),
369        authority_signer_seeds: &[],
370        token_program: token_program_id.clone(),
371    })?;
372
373    spl_token_mint_to(TokenMintToParams {
374        mint: reserve_collateral_mint_info.clone(),
375        destination: destination_collateral_info.clone(),
376        amount: collateral_amount,
377        authority: lending_market_authority_info.clone(),
378        authority_signer_seeds,
379        token_program: token_program_id.clone(),
380    })?;
381
382    Ok(())
383}
384
385fn process_refresh_reserve(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
386    let account_info_iter = &mut accounts.iter().peekable();
387    let reserve_info = next_account_info(account_info_iter)?;
388    let reserve_liquidity_oracle_info = next_account_info(account_info_iter)?;
389    let clock = &Clock::from_account_info(next_account_info(account_info_iter)?)?;
390
391    let mut reserve = Reserve::unpack(&reserve_info.data.borrow())?;
392    if reserve_info.owner != program_id {
393        msg!("Reserve provided is not owned by the lending program");
394        return Err(LendingError::InvalidAccountOwner.into());
395    }
396    if &reserve.liquidity.oracle_pubkey != reserve_liquidity_oracle_info.key {
397        msg!("Reserve liquidity oracle does not match the reserve liquidity oracle provided");
398        return Err(LendingError::InvalidAccountInput.into());
399    }
400
401    reserve.liquidity.market_price = get_pyth_price(reserve_liquidity_oracle_info, clock)?;
402
403    reserve.accrue_interest(clock.slot)?;
404    reserve.last_update.update_slot(clock.slot);
405    Reserve::pack(reserve, &mut reserve_info.data.borrow_mut())?;
406
407    Ok(())
408}
409
410fn process_deposit_reserve_liquidity(
411    program_id: &Pubkey,
412    liquidity_amount: u64,
413    accounts: &[AccountInfo],
414) -> ProgramResult {
415    if liquidity_amount == 0 {
416        msg!("Liquidity amount provided cannot be zero");
417        return Err(LendingError::InvalidAmount.into());
418    }
419
420    let account_info_iter = &mut accounts.iter();
421    let source_liquidity_info = next_account_info(account_info_iter)?;
422    let destination_collateral_info = next_account_info(account_info_iter)?;
423    let reserve_info = next_account_info(account_info_iter)?;
424    let reserve_liquidity_supply_info = next_account_info(account_info_iter)?;
425    let reserve_collateral_mint_info = next_account_info(account_info_iter)?;
426    let lending_market_info = next_account_info(account_info_iter)?;
427    let lending_market_authority_info = next_account_info(account_info_iter)?;
428    let user_transfer_authority_info = next_account_info(account_info_iter)?;
429    let clock = &Clock::from_account_info(next_account_info(account_info_iter)?)?;
430    let token_program_id = next_account_info(account_info_iter)?;
431
432    let lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
433    if lending_market_info.owner != program_id {
434        msg!("Lending market provided is not owned by the lending program");
435        return Err(LendingError::InvalidAccountOwner.into());
436    }
437    if &lending_market.token_program_id != token_program_id.key {
438        msg!("Lending market token program does not match the token program provided");
439        return Err(LendingError::InvalidTokenProgram.into());
440    }
441
442    let mut reserve = Reserve::unpack(&reserve_info.data.borrow())?;
443    if reserve_info.owner != program_id {
444        msg!("Reserve provided is not owned by the lending program");
445        return Err(LendingError::InvalidAccountOwner.into());
446    }
447    if &reserve.lending_market != lending_market_info.key {
448        msg!("Reserve lending market does not match the lending market provided");
449        return Err(LendingError::InvalidAccountInput.into());
450    }
451    if &reserve.liquidity.supply_pubkey != reserve_liquidity_supply_info.key {
452        msg!("Reserve liquidity supply does not match the reserve liquidity supply provided");
453        return Err(LendingError::InvalidAccountInput.into());
454    }
455    if &reserve.collateral.mint_pubkey != reserve_collateral_mint_info.key {
456        msg!("Reserve collateral mint does not match the reserve collateral mint provided");
457        return Err(LendingError::InvalidAccountInput.into());
458    }
459    if &reserve.liquidity.supply_pubkey == source_liquidity_info.key {
460        msg!("Reserve liquidity supply cannot be used as the source liquidity provided");
461        return Err(LendingError::InvalidAccountInput.into());
462    }
463    if &reserve.collateral.supply_pubkey == destination_collateral_info.key {
464        msg!("Reserve collateral supply cannot be used as the destination collateral provided");
465        return Err(LendingError::InvalidAccountInput.into());
466    }
467    if reserve.last_update.is_stale(clock.slot)? {
468        msg!("Reserve is stale and must be refreshed in the current slot");
469        return Err(LendingError::ReserveStale.into());
470    }
471
472    let authority_signer_seeds = &[
473        lending_market_info.key.as_ref(),
474        &[lending_market.bump_seed],
475    ];
476    let lending_market_authority_pubkey =
477        Pubkey::create_program_address(authority_signer_seeds, program_id)?;
478    if &lending_market_authority_pubkey != lending_market_authority_info.key {
479        msg!(
480            "Derived lending market authority {} does not match the lending market authority provided {}",
481            &lending_market_authority_pubkey.to_string(),
482            &lending_market_authority_info.key.to_string(),
483        );
484        return Err(LendingError::InvalidMarketAuthority.into());
485    }
486
487    let collateral_amount = reserve.deposit_liquidity(liquidity_amount)?;
488    reserve.last_update.mark_stale();
489    Reserve::pack(reserve, &mut reserve_info.data.borrow_mut())?;
490
491    spl_token_transfer(TokenTransferParams {
492        source: source_liquidity_info.clone(),
493        destination: reserve_liquidity_supply_info.clone(),
494        amount: liquidity_amount,
495        authority: user_transfer_authority_info.clone(),
496        authority_signer_seeds: &[],
497        token_program: token_program_id.clone(),
498    })?;
499
500    spl_token_mint_to(TokenMintToParams {
501        mint: reserve_collateral_mint_info.clone(),
502        destination: destination_collateral_info.clone(),
503        amount: collateral_amount,
504        authority: lending_market_authority_info.clone(),
505        authority_signer_seeds,
506        token_program: token_program_id.clone(),
507    })?;
508
509    Ok(())
510}
511
512fn process_redeem_reserve_collateral(
513    program_id: &Pubkey,
514    collateral_amount: u64,
515    accounts: &[AccountInfo],
516) -> ProgramResult {
517    if collateral_amount == 0 {
518        msg!("Collateral amount provided cannot be zero");
519        return Err(LendingError::InvalidAmount.into());
520    }
521
522    let account_info_iter = &mut accounts.iter();
523    let source_collateral_info = next_account_info(account_info_iter)?;
524    let destination_liquidity_info = next_account_info(account_info_iter)?;
525    let reserve_info = next_account_info(account_info_iter)?;
526    let reserve_collateral_mint_info = next_account_info(account_info_iter)?;
527    let reserve_liquidity_supply_info = next_account_info(account_info_iter)?;
528    let lending_market_info = next_account_info(account_info_iter)?;
529    let lending_market_authority_info = next_account_info(account_info_iter)?;
530    let user_transfer_authority_info = next_account_info(account_info_iter)?;
531    let clock = &Clock::from_account_info(next_account_info(account_info_iter)?)?;
532    let token_program_id = next_account_info(account_info_iter)?;
533
534    let lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
535    if lending_market_info.owner != program_id {
536        msg!("Lending market provided is not owned by the lending program");
537        return Err(LendingError::InvalidAccountOwner.into());
538    }
539    if &lending_market.token_program_id != token_program_id.key {
540        msg!("Lending market token program does not match the token program provided");
541        return Err(LendingError::InvalidTokenProgram.into());
542    }
543
544    let mut reserve = Reserve::unpack(&reserve_info.data.borrow())?;
545    if reserve_info.owner != program_id {
546        msg!("Reserve provided is not owned by the lending program");
547        return Err(LendingError::InvalidAccountOwner.into());
548    }
549    if &reserve.lending_market != lending_market_info.key {
550        msg!("Reserve lending market does not match the lending market provided");
551        return Err(LendingError::InvalidAccountInput.into());
552    }
553    if &reserve.collateral.mint_pubkey != reserve_collateral_mint_info.key {
554        msg!("Reserve collateral mint does not match the reserve collateral mint provided");
555        return Err(LendingError::InvalidAccountInput.into());
556    }
557    if &reserve.collateral.supply_pubkey == source_collateral_info.key {
558        msg!("Reserve collateral supply cannot be used as the source collateral provided");
559        return Err(LendingError::InvalidAccountInput.into());
560    }
561    if &reserve.liquidity.supply_pubkey != reserve_liquidity_supply_info.key {
562        msg!("Reserve liquidity supply does not match the reserve liquidity supply provided");
563        return Err(LendingError::InvalidAccountInput.into());
564    }
565    if &reserve.liquidity.supply_pubkey == destination_liquidity_info.key {
566        msg!("Reserve liquidity supply cannot be used as the destination liquidity provided");
567        return Err(LendingError::InvalidAccountInput.into());
568    }
569    if reserve.last_update.is_stale(clock.slot)? {
570        msg!("Reserve is stale and must be refreshed in the current slot");
571        return Err(LendingError::ReserveStale.into());
572    }
573
574    let authority_signer_seeds = &[
575        lending_market_info.key.as_ref(),
576        &[lending_market.bump_seed],
577    ];
578    let lending_market_authority_pubkey =
579        Pubkey::create_program_address(authority_signer_seeds, program_id)?;
580    if &lending_market_authority_pubkey != lending_market_authority_info.key {
581        msg!(
582            "Derived lending market authority does not match the lending market authority provided"
583        );
584        return Err(LendingError::InvalidMarketAuthority.into());
585    }
586
587    let liquidity_amount = reserve.redeem_collateral(collateral_amount)?;
588    reserve.last_update.mark_stale();
589    Reserve::pack(reserve, &mut reserve_info.data.borrow_mut())?;
590
591    spl_token_burn(TokenBurnParams {
592        mint: reserve_collateral_mint_info.clone(),
593        source: source_collateral_info.clone(),
594        amount: collateral_amount,
595        authority: user_transfer_authority_info.clone(),
596        authority_signer_seeds: &[],
597        token_program: token_program_id.clone(),
598    })?;
599
600    spl_token_transfer(TokenTransferParams {
601        source: reserve_liquidity_supply_info.clone(),
602        destination: destination_liquidity_info.clone(),
603        amount: liquidity_amount,
604        authority: lending_market_authority_info.clone(),
605        authority_signer_seeds,
606        token_program: token_program_id.clone(),
607    })?;
608
609    Ok(())
610}
611
612#[inline(never)] // avoid stack frame limit
613fn process_init_obligation(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
614    let account_info_iter = &mut accounts.iter();
615    let obligation_info = next_account_info(account_info_iter)?;
616    let lending_market_info = next_account_info(account_info_iter)?;
617    let obligation_owner_info = next_account_info(account_info_iter)?;
618    let clock = &Clock::from_account_info(next_account_info(account_info_iter)?)?;
619    let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?;
620    let token_program_id = next_account_info(account_info_iter)?;
621
622    assert_rent_exempt(rent, obligation_info)?;
623    let mut obligation = assert_uninitialized::<Obligation>(obligation_info)?;
624    if obligation_info.owner != program_id {
625        msg!("Obligation provided is not owned by the lending program");
626        return Err(LendingError::InvalidAccountOwner.into());
627    }
628
629    let lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
630    if lending_market_info.owner != program_id {
631        msg!("Lending market provided is not owned by the lending program");
632        return Err(LendingError::InvalidAccountOwner.into());
633    }
634    if &lending_market.token_program_id != token_program_id.key {
635        msg!("Lending market token program does not match the token program provided");
636        return Err(LendingError::InvalidTokenProgram.into());
637    }
638
639    if !obligation_owner_info.is_signer {
640        msg!("Obligation owner provided must be a signer");
641        return Err(LendingError::InvalidSigner.into());
642    }
643
644    obligation.init(InitObligationParams {
645        current_slot: clock.slot,
646        lending_market: *lending_market_info.key,
647        owner: *obligation_owner_info.key,
648        deposits: vec![],
649        borrows: vec![],
650    });
651    Obligation::pack(obligation, &mut obligation_info.data.borrow_mut())?;
652
653    Ok(())
654}
655
656fn process_refresh_obligation(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
657    let account_info_iter = &mut accounts.iter().peekable();
658    let obligation_info = next_account_info(account_info_iter)?;
659    let clock = &Clock::from_account_info(next_account_info(account_info_iter)?)?;
660
661    let mut obligation = Obligation::unpack(&obligation_info.data.borrow())?;
662    if obligation_info.owner != program_id {
663        msg!("Obligation provided is not owned by the lending program");
664        return Err(LendingError::InvalidAccountOwner.into());
665    }
666
667    let mut deposited_value = Decimal::zero();
668    let mut borrowed_value = Decimal::zero();
669    let mut allowed_borrow_value = Decimal::zero();
670    let mut unhealthy_borrow_value = Decimal::zero();
671
672    for (index, collateral) in obligation.deposits.iter_mut().enumerate() {
673        let deposit_reserve_info = next_account_info(account_info_iter)?;
674        if deposit_reserve_info.owner != program_id {
675            msg!(
676                "Deposit reserve provided for collateral {} is not owned by the lending program",
677                index
678            );
679            return Err(LendingError::InvalidAccountOwner.into());
680        }
681        if collateral.deposit_reserve != *deposit_reserve_info.key {
682            msg!(
683                "Deposit reserve of collateral {} does not match the deposit reserve provided",
684                index
685            );
686            return Err(LendingError::InvalidAccountInput.into());
687        }
688
689        let deposit_reserve = Reserve::unpack(&deposit_reserve_info.data.borrow())?;
690        if deposit_reserve.last_update.is_stale(clock.slot)? {
691            msg!(
692                "Deposit reserve provided for collateral {} is stale and must be refreshed in the current slot",
693                index
694            );
695            return Err(LendingError::ReserveStale.into());
696        }
697
698        // @TODO: add lookup table https://git.io/JOCYq
699        let decimals = 10u64
700            .checked_pow(deposit_reserve.liquidity.mint_decimals as u32)
701            .ok_or(LendingError::MathOverflow)?;
702
703        let market_value = deposit_reserve
704            .collateral_exchange_rate()?
705            .decimal_collateral_to_liquidity(collateral.deposited_amount.into())?
706            .try_mul(deposit_reserve.liquidity.market_price)?
707            .try_div(decimals)?;
708        collateral.market_value = market_value;
709
710        let loan_to_value_rate = Rate::from_percent(deposit_reserve.config.loan_to_value_ratio);
711        let liquidation_threshold_rate =
712            Rate::from_percent(deposit_reserve.config.liquidation_threshold);
713
714        deposited_value = deposited_value.try_add(market_value)?;
715        allowed_borrow_value =
716            allowed_borrow_value.try_add(market_value.try_mul(loan_to_value_rate)?)?;
717        unhealthy_borrow_value =
718            unhealthy_borrow_value.try_add(market_value.try_mul(liquidation_threshold_rate)?)?;
719    }
720
721    for (index, liquidity) in obligation.borrows.iter_mut().enumerate() {
722        let borrow_reserve_info = next_account_info(account_info_iter)?;
723        if borrow_reserve_info.owner != program_id {
724            msg!(
725                "Borrow reserve provided for liquidity {} is not owned by the lending program",
726                index
727            );
728            return Err(LendingError::InvalidAccountOwner.into());
729        }
730        if liquidity.borrow_reserve != *borrow_reserve_info.key {
731            msg!(
732                "Borrow reserve of liquidity {} does not match the borrow reserve provided",
733                index
734            );
735            return Err(LendingError::InvalidAccountInput.into());
736        }
737
738        let borrow_reserve = Reserve::unpack(&borrow_reserve_info.data.borrow())?;
739        if borrow_reserve.last_update.is_stale(clock.slot)? {
740            msg!(
741                "Borrow reserve provided for liquidity {} is stale and must be refreshed in the current slot",
742                index
743            );
744            return Err(LendingError::ReserveStale.into());
745        }
746
747        liquidity.accrue_interest(borrow_reserve.liquidity.cumulative_borrow_rate_wads)?;
748
749        // @TODO: add lookup table https://git.io/JOCYq
750        let decimals = 10u64
751            .checked_pow(borrow_reserve.liquidity.mint_decimals as u32)
752            .ok_or(LendingError::MathOverflow)?;
753
754        let market_value = liquidity
755            .borrowed_amount_wads
756            .try_mul(borrow_reserve.liquidity.market_price)?
757            .try_div(decimals)?;
758        liquidity.market_value = market_value;
759
760        borrowed_value = borrowed_value.try_add(market_value)?;
761    }
762
763    if account_info_iter.peek().is_some() {
764        msg!("Too many obligation deposit or borrow reserves provided");
765        return Err(LendingError::InvalidAccountInput.into());
766    }
767
768    obligation.deposited_value = deposited_value;
769    obligation.borrowed_value = borrowed_value;
770    obligation.allowed_borrow_value = allowed_borrow_value;
771    obligation.unhealthy_borrow_value = unhealthy_borrow_value;
772
773    obligation.last_update.update_slot(clock.slot);
774    Obligation::pack(obligation, &mut obligation_info.data.borrow_mut())?;
775
776    Ok(())
777}
778
779#[inline(never)] // avoid stack frame limit
780fn process_deposit_obligation_collateral(
781    program_id: &Pubkey,
782    collateral_amount: u64,
783    accounts: &[AccountInfo],
784) -> ProgramResult {
785    if collateral_amount == 0 {
786        msg!("Collateral amount provided cannot be zero");
787        return Err(LendingError::InvalidAmount.into());
788    }
789
790    let account_info_iter = &mut accounts.iter();
791    let source_collateral_info = next_account_info(account_info_iter)?;
792    let destination_collateral_info = next_account_info(account_info_iter)?;
793    let deposit_reserve_info = next_account_info(account_info_iter)?;
794    let obligation_info = next_account_info(account_info_iter)?;
795    let lending_market_info = next_account_info(account_info_iter)?;
796    let obligation_owner_info = next_account_info(account_info_iter)?;
797    let user_transfer_authority_info = next_account_info(account_info_iter)?;
798    let clock = &Clock::from_account_info(next_account_info(account_info_iter)?)?;
799    let token_program_id = next_account_info(account_info_iter)?;
800
801    let lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
802    if lending_market_info.owner != program_id {
803        msg!("Lending market provided is not owned by the lending program");
804        return Err(LendingError::InvalidAccountOwner.into());
805    }
806    if &lending_market.token_program_id != token_program_id.key {
807        msg!("Lending market token program does not match the token program provided");
808        return Err(LendingError::InvalidTokenProgram.into());
809    }
810
811    let deposit_reserve = Reserve::unpack(&deposit_reserve_info.data.borrow())?;
812    if deposit_reserve_info.owner != program_id {
813        msg!("Deposit reserve provided is not owned by the lending program");
814        return Err(LendingError::InvalidAccountOwner.into());
815    }
816    if &deposit_reserve.lending_market != lending_market_info.key {
817        msg!("Deposit reserve lending market does not match the lending market provided");
818        return Err(LendingError::InvalidAccountInput.into());
819    }
820    if &deposit_reserve.collateral.supply_pubkey == source_collateral_info.key {
821        msg!("Deposit reserve collateral supply cannot be used as the source collateral provided");
822        return Err(LendingError::InvalidAccountInput.into());
823    }
824    if &deposit_reserve.collateral.supply_pubkey != destination_collateral_info.key {
825        msg!(
826            "Deposit reserve collateral supply must be used as the destination collateral provided"
827        );
828        return Err(LendingError::InvalidAccountInput.into());
829    }
830    if deposit_reserve.last_update.is_stale(clock.slot)? {
831        msg!("Deposit reserve is stale and must be refreshed in the current slot");
832        return Err(LendingError::ReserveStale.into());
833    }
834    if deposit_reserve.config.loan_to_value_ratio == 0 {
835        msg!("Deposit reserve has collateral disabled for borrowing");
836        return Err(LendingError::ReserveCollateralDisabled.into());
837    }
838
839    let mut obligation = Obligation::unpack(&obligation_info.data.borrow())?;
840    if obligation_info.owner != program_id {
841        msg!("Obligation provided is not owned by the lending program");
842        return Err(LendingError::InvalidAccountOwner.into());
843    }
844    if &obligation.lending_market != lending_market_info.key {
845        msg!("Obligation lending market does not match the lending market provided");
846        return Err(LendingError::InvalidAccountInput.into());
847    }
848    if &obligation.owner != obligation_owner_info.key {
849        msg!("Obligation owner does not match the obligation owner provided");
850        return Err(LendingError::InvalidObligationOwner.into());
851    }
852    if !obligation_owner_info.is_signer {
853        msg!("Obligation owner provided must be a signer");
854        return Err(LendingError::InvalidSigner.into());
855    }
856
857    obligation
858        .find_or_add_collateral_to_deposits(*deposit_reserve_info.key)?
859        .deposit(collateral_amount)?;
860    obligation.last_update.mark_stale();
861    Obligation::pack(obligation, &mut obligation_info.data.borrow_mut())?;
862
863    spl_token_transfer(TokenTransferParams {
864        source: source_collateral_info.clone(),
865        destination: destination_collateral_info.clone(),
866        amount: collateral_amount,
867        authority: user_transfer_authority_info.clone(),
868        authority_signer_seeds: &[],
869        token_program: token_program_id.clone(),
870    })?;
871
872    Ok(())
873}
874
875#[inline(never)] // avoid stack frame limit
876fn process_withdraw_obligation_collateral(
877    program_id: &Pubkey,
878    collateral_amount: u64,
879    accounts: &[AccountInfo],
880) -> ProgramResult {
881    if collateral_amount == 0 {
882        msg!("Collateral amount provided cannot be zero");
883        return Err(LendingError::InvalidAmount.into());
884    }
885
886    let account_info_iter = &mut accounts.iter();
887    let source_collateral_info = next_account_info(account_info_iter)?;
888    let destination_collateral_info = next_account_info(account_info_iter)?;
889    let withdraw_reserve_info = next_account_info(account_info_iter)?;
890    let obligation_info = next_account_info(account_info_iter)?;
891    let lending_market_info = next_account_info(account_info_iter)?;
892    let lending_market_authority_info = next_account_info(account_info_iter)?;
893    let obligation_owner_info = next_account_info(account_info_iter)?;
894    let clock = &Clock::from_account_info(next_account_info(account_info_iter)?)?;
895    let token_program_id = next_account_info(account_info_iter)?;
896
897    let lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
898    if lending_market_info.owner != program_id {
899        msg!("Lending market provided is not owned by the lending program");
900        return Err(LendingError::InvalidAccountOwner.into());
901    }
902    if &lending_market.token_program_id != token_program_id.key {
903        msg!("Lending market token program does not match the token program provided");
904        return Err(LendingError::InvalidTokenProgram.into());
905    }
906
907    let withdraw_reserve = Reserve::unpack(&withdraw_reserve_info.data.borrow())?;
908    if withdraw_reserve_info.owner != program_id {
909        msg!("Withdraw reserve provided is not owned by the lending program");
910        return Err(LendingError::InvalidAccountOwner.into());
911    }
912    if &withdraw_reserve.lending_market != lending_market_info.key {
913        msg!("Withdraw reserve lending market does not match the lending market provided");
914        return Err(LendingError::InvalidAccountInput.into());
915    }
916    if &withdraw_reserve.collateral.supply_pubkey != source_collateral_info.key {
917        msg!("Withdraw reserve collateral supply must be used as the source collateral provided");
918        return Err(LendingError::InvalidAccountInput.into());
919    }
920    if &withdraw_reserve.collateral.supply_pubkey == destination_collateral_info.key {
921        msg!("Withdraw reserve collateral supply cannot be used as the destination collateral provided");
922        return Err(LendingError::InvalidAccountInput.into());
923    }
924    if withdraw_reserve.last_update.is_stale(clock.slot)? {
925        msg!("Withdraw reserve is stale and must be refreshed in the current slot");
926        return Err(LendingError::ReserveStale.into());
927    }
928
929    let mut obligation = Obligation::unpack(&obligation_info.data.borrow())?;
930    if obligation_info.owner != program_id {
931        msg!("Obligation provided is not owned by the lending program");
932        return Err(LendingError::InvalidAccountOwner.into());
933    }
934    if &obligation.lending_market != lending_market_info.key {
935        msg!("Obligation lending market does not match the lending market provided");
936        return Err(LendingError::InvalidAccountInput.into());
937    }
938    if &obligation.owner != obligation_owner_info.key {
939        msg!("Obligation owner does not match the obligation owner provided");
940        return Err(LendingError::InvalidObligationOwner.into());
941    }
942    if !obligation_owner_info.is_signer {
943        msg!("Obligation owner provided must be a signer");
944        return Err(LendingError::InvalidSigner.into());
945    }
946    if obligation.last_update.is_stale(clock.slot)? {
947        msg!("Obligation is stale and must be refreshed in the current slot");
948        return Err(LendingError::ObligationStale.into());
949    }
950
951    let (collateral, collateral_index) =
952        obligation.find_collateral_in_deposits(*withdraw_reserve_info.key)?;
953    if collateral.deposited_amount == 0 {
954        msg!("Collateral deposited amount is zero");
955        return Err(LendingError::ObligationCollateralEmpty.into());
956    }
957
958    let authority_signer_seeds = &[
959        lending_market_info.key.as_ref(),
960        &[lending_market.bump_seed],
961    ];
962    let lending_market_authority_pubkey =
963        Pubkey::create_program_address(authority_signer_seeds, program_id)?;
964    if &lending_market_authority_pubkey != lending_market_authority_info.key {
965        msg!(
966            "Derived lending market authority does not match the lending market authority provided"
967        );
968        return Err(LendingError::InvalidMarketAuthority.into());
969    }
970
971    let withdraw_amount = if obligation.borrows.is_empty() {
972        if collateral_amount == u64::MAX {
973            collateral.deposited_amount
974        } else {
975            collateral.deposited_amount.min(collateral_amount)
976        }
977    } else if obligation.deposited_value == Decimal::zero() {
978        msg!("Obligation deposited value is zero");
979        return Err(LendingError::ObligationDepositsZero.into());
980    } else {
981        let max_withdraw_value = obligation.max_withdraw_value(Rate::from_percent(
982            withdraw_reserve.config.loan_to_value_ratio,
983        ))?;
984        if max_withdraw_value == Decimal::zero() {
985            msg!("Maximum withdraw value is zero");
986            return Err(LendingError::WithdrawTooLarge.into());
987        }
988
989        let withdraw_amount = if collateral_amount == u64::MAX {
990            let withdraw_value = max_withdraw_value.min(collateral.market_value);
991            let withdraw_pct = withdraw_value.try_div(collateral.market_value)?;
992            withdraw_pct
993                .try_mul(collateral.deposited_amount)?
994                .try_floor_u64()?
995                .min(collateral.deposited_amount)
996        } else {
997            let withdraw_amount = collateral_amount.min(collateral.deposited_amount);
998            let withdraw_pct =
999                Decimal::from(withdraw_amount).try_div(collateral.deposited_amount)?;
1000            let withdraw_value = collateral.market_value.try_mul(withdraw_pct)?;
1001            if withdraw_value > max_withdraw_value {
1002                msg!("Withdraw value cannot exceed maximum withdraw value");
1003                return Err(LendingError::WithdrawTooLarge.into());
1004            }
1005            withdraw_amount
1006        };
1007        if withdraw_amount == 0 {
1008            msg!("Withdraw amount is too small to transfer collateral");
1009            return Err(LendingError::WithdrawTooSmall.into());
1010        }
1011        withdraw_amount
1012    };
1013
1014    obligation.withdraw(withdraw_amount, collateral_index)?;
1015    obligation.last_update.mark_stale();
1016    Obligation::pack(obligation, &mut obligation_info.data.borrow_mut())?;
1017
1018    spl_token_transfer(TokenTransferParams {
1019        source: source_collateral_info.clone(),
1020        destination: destination_collateral_info.clone(),
1021        amount: withdraw_amount,
1022        authority: lending_market_authority_info.clone(),
1023        authority_signer_seeds,
1024        token_program: token_program_id.clone(),
1025    })?;
1026
1027    Ok(())
1028}
1029
1030#[inline(never)] // avoid stack frame limit
1031fn process_borrow_obligation_liquidity(
1032    program_id: &Pubkey,
1033    liquidity_amount: u64,
1034    slippage_limit: u64,
1035    accounts: &[AccountInfo],
1036) -> ProgramResult {
1037    if liquidity_amount == 0 {
1038        msg!("Liquidity amount provided cannot be zero");
1039        return Err(LendingError::InvalidAmount.into());
1040    }
1041
1042    let account_info_iter = &mut accounts.iter();
1043    let source_liquidity_info = next_account_info(account_info_iter)?;
1044    let destination_liquidity_info = next_account_info(account_info_iter)?;
1045    let borrow_reserve_info = next_account_info(account_info_iter)?;
1046    let borrow_reserve_liquidity_fee_receiver_info = next_account_info(account_info_iter)?;
1047    let obligation_info = next_account_info(account_info_iter)?;
1048    let lending_market_info = next_account_info(account_info_iter)?;
1049    let lending_market_authority_info = next_account_info(account_info_iter)?;
1050    let obligation_owner_info = next_account_info(account_info_iter)?;
1051    let clock = &Clock::from_account_info(next_account_info(account_info_iter)?)?;
1052    let token_program_id = next_account_info(account_info_iter)?;
1053
1054    let lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
1055    if lending_market_info.owner != program_id {
1056        msg!("Lending market provided is not owned by the lending program");
1057        return Err(LendingError::InvalidAccountOwner.into());
1058    }
1059    if &lending_market.token_program_id != token_program_id.key {
1060        msg!("Lending market token program does not match the token program provided");
1061        return Err(LendingError::InvalidTokenProgram.into());
1062    }
1063
1064    let mut borrow_reserve = Reserve::unpack(&borrow_reserve_info.data.borrow())?;
1065    if borrow_reserve_info.owner != program_id {
1066        msg!("Borrow reserve provided is not owned by the lending program");
1067        return Err(LendingError::InvalidAccountOwner.into());
1068    }
1069    if &borrow_reserve.lending_market != lending_market_info.key {
1070        msg!("Borrow reserve lending market does not match the lending market provided");
1071        return Err(LendingError::InvalidAccountInput.into());
1072    }
1073    if &borrow_reserve.liquidity.supply_pubkey != source_liquidity_info.key {
1074        msg!("Borrow reserve liquidity supply must be used as the source liquidity provided");
1075        return Err(LendingError::InvalidAccountInput.into());
1076    }
1077    if &borrow_reserve.liquidity.supply_pubkey == destination_liquidity_info.key {
1078        msg!(
1079            "Borrow reserve liquidity supply cannot be used as the destination liquidity provided"
1080        );
1081        return Err(LendingError::InvalidAccountInput.into());
1082    }
1083    if &borrow_reserve.liquidity.fee_receiver != borrow_reserve_liquidity_fee_receiver_info.key {
1084        msg!("Borrow reserve liquidity fee receiver does not match the borrow reserve liquidity fee receiver provided");
1085        return Err(LendingError::InvalidAccountInput.into());
1086    }
1087    if borrow_reserve.last_update.is_stale(clock.slot)? {
1088        msg!("Borrow reserve is stale and must be refreshed in the current slot");
1089        return Err(LendingError::ReserveStale.into());
1090    }
1091
1092    let mut obligation = Obligation::unpack(&obligation_info.data.borrow())?;
1093    if obligation_info.owner != program_id {
1094        msg!("Obligation provided is not owned by the lending program");
1095        return Err(LendingError::InvalidAccountOwner.into());
1096    }
1097    if &obligation.lending_market != lending_market_info.key {
1098        msg!("Obligation lending market does not match the lending market provided");
1099        return Err(LendingError::InvalidAccountInput.into());
1100    }
1101    if &obligation.owner != obligation_owner_info.key {
1102        msg!("Obligation owner does not match the obligation owner provided");
1103        return Err(LendingError::InvalidObligationOwner.into());
1104    }
1105    if !obligation_owner_info.is_signer {
1106        msg!("Obligation owner provided must be a signer");
1107        return Err(LendingError::InvalidSigner.into());
1108    }
1109    if obligation.last_update.is_stale(clock.slot)? {
1110        msg!("Obligation is stale and must be refreshed in the current slot");
1111        return Err(LendingError::ObligationStale.into());
1112    }
1113    if obligation.deposits.is_empty() {
1114        msg!("Obligation has no deposits to borrow against");
1115        return Err(LendingError::ObligationDepositsEmpty.into());
1116    }
1117    if obligation.deposited_value == Decimal::zero() {
1118        msg!("Obligation deposits have zero value");
1119        return Err(LendingError::ObligationDepositsZero.into());
1120    }
1121
1122    let authority_signer_seeds = &[
1123        lending_market_info.key.as_ref(),
1124        &[lending_market.bump_seed],
1125    ];
1126    let lending_market_authority_pubkey =
1127        Pubkey::create_program_address(authority_signer_seeds, program_id)?;
1128    if &lending_market_authority_pubkey != lending_market_authority_info.key {
1129        msg!(
1130            "Derived lending market authority does not match the lending market authority provided"
1131        );
1132        return Err(LendingError::InvalidMarketAuthority.into());
1133    }
1134
1135    let remaining_borrow_value = obligation.remaining_borrow_value()?;
1136    if remaining_borrow_value == Decimal::zero() {
1137        msg!("Remaining borrow value is zero");
1138        return Err(LendingError::BorrowTooLarge.into());
1139    }
1140
1141    let CalculateBorrowResult {
1142        borrow_amount,
1143        receive_amount,
1144        borrow_fee,
1145        host_fee,
1146    } = borrow_reserve.calculate_borrow(liquidity_amount, remaining_borrow_value)?;
1147
1148    if receive_amount == 0 {
1149        msg!("Borrow amount is too small to receive liquidity after fees");
1150        return Err(LendingError::BorrowTooSmall.into());
1151    }
1152
1153    if liquidity_amount == u64::MAX && receive_amount < slippage_limit {
1154        msg!("Received liquidity would be smaller than the desired slippage limit");
1155        return Err(LendingError::ExceededSlippage.into());
1156    }
1157
1158    borrow_reserve.liquidity.borrow(borrow_amount)?;
1159    borrow_reserve.last_update.mark_stale();
1160    Reserve::pack(borrow_reserve, &mut borrow_reserve_info.data.borrow_mut())?;
1161
1162    obligation
1163        .find_or_add_liquidity_to_borrows(*borrow_reserve_info.key)?
1164        .borrow(borrow_amount)?;
1165    obligation.last_update.mark_stale();
1166    Obligation::pack(obligation, &mut obligation_info.data.borrow_mut())?;
1167
1168    let mut owner_fee = borrow_fee;
1169    if let Ok(host_fee_receiver_info) = next_account_info(account_info_iter) {
1170        if host_fee > 0 {
1171            owner_fee = owner_fee
1172                .checked_sub(host_fee)
1173                .ok_or(LendingError::MathOverflow)?;
1174
1175            spl_token_transfer(TokenTransferParams {
1176                source: source_liquidity_info.clone(),
1177                destination: host_fee_receiver_info.clone(),
1178                amount: host_fee,
1179                authority: lending_market_authority_info.clone(),
1180                authority_signer_seeds,
1181                token_program: token_program_id.clone(),
1182            })?;
1183        }
1184    }
1185    if owner_fee > 0 {
1186        spl_token_transfer(TokenTransferParams {
1187            source: source_liquidity_info.clone(),
1188            destination: borrow_reserve_liquidity_fee_receiver_info.clone(),
1189            amount: owner_fee,
1190            authority: lending_market_authority_info.clone(),
1191            authority_signer_seeds,
1192            token_program: token_program_id.clone(),
1193        })?;
1194    }
1195
1196    spl_token_transfer(TokenTransferParams {
1197        source: source_liquidity_info.clone(),
1198        destination: destination_liquidity_info.clone(),
1199        amount: receive_amount,
1200        authority: lending_market_authority_info.clone(),
1201        authority_signer_seeds,
1202        token_program: token_program_id.clone(),
1203    })?;
1204
1205    Ok(())
1206}
1207
1208#[inline(never)] // avoid stack frame limit
1209fn process_repay_obligation_liquidity(
1210    program_id: &Pubkey,
1211    liquidity_amount: u64,
1212    accounts: &[AccountInfo],
1213) -> ProgramResult {
1214    if liquidity_amount == 0 {
1215        msg!("Liquidity amount provided cannot be zero");
1216        return Err(LendingError::InvalidAmount.into());
1217    }
1218
1219    let account_info_iter = &mut accounts.iter();
1220    let source_liquidity_info = next_account_info(account_info_iter)?;
1221    let destination_liquidity_info = next_account_info(account_info_iter)?;
1222    let repay_reserve_info = next_account_info(account_info_iter)?;
1223    let obligation_info = next_account_info(account_info_iter)?;
1224    let lending_market_info = next_account_info(account_info_iter)?;
1225    let user_transfer_authority_info = next_account_info(account_info_iter)?;
1226    let clock = &Clock::from_account_info(next_account_info(account_info_iter)?)?;
1227    let token_program_id = next_account_info(account_info_iter)?;
1228
1229    let lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
1230    if lending_market_info.owner != program_id {
1231        msg!("Lending market provided is not owned by the lending program");
1232        return Err(LendingError::InvalidAccountOwner.into());
1233    }
1234    if &lending_market.token_program_id != token_program_id.key {
1235        msg!("Lending market token program does not match the token program provided");
1236        return Err(LendingError::InvalidTokenProgram.into());
1237    }
1238
1239    let mut repay_reserve = Reserve::unpack(&repay_reserve_info.data.borrow())?;
1240    if repay_reserve_info.owner != program_id {
1241        msg!("Repay reserve provided is not owned by the lending program");
1242        return Err(LendingError::InvalidAccountOwner.into());
1243    }
1244    if &repay_reserve.lending_market != lending_market_info.key {
1245        msg!("Repay reserve lending market does not match the lending market provided");
1246        return Err(LendingError::InvalidAccountInput.into());
1247    }
1248    if &repay_reserve.liquidity.supply_pubkey == source_liquidity_info.key {
1249        msg!("Repay reserve liquidity supply cannot be used as the source liquidity provided");
1250        return Err(LendingError::InvalidAccountInput.into());
1251    }
1252    if &repay_reserve.liquidity.supply_pubkey != destination_liquidity_info.key {
1253        msg!("Repay reserve liquidity supply must be used as the destination liquidity provided");
1254        return Err(LendingError::InvalidAccountInput.into());
1255    }
1256    if repay_reserve.last_update.is_stale(clock.slot)? {
1257        msg!("Repay reserve is stale and must be refreshed in the current slot");
1258        return Err(LendingError::ReserveStale.into());
1259    }
1260
1261    let mut obligation = Obligation::unpack(&obligation_info.data.borrow())?;
1262    if obligation_info.owner != program_id {
1263        msg!("Obligation provided is not owned by the lending program");
1264        return Err(LendingError::InvalidAccountOwner.into());
1265    }
1266    if &obligation.lending_market != lending_market_info.key {
1267        msg!("Obligation lending market does not match the lending market provided");
1268        return Err(LendingError::InvalidAccountInput.into());
1269    }
1270    if obligation.last_update.is_stale(clock.slot)? {
1271        msg!("Obligation is stale and must be refreshed in the current slot");
1272        return Err(LendingError::ObligationStale.into());
1273    }
1274
1275    let (liquidity, liquidity_index) =
1276        obligation.find_liquidity_in_borrows(*repay_reserve_info.key)?;
1277    if liquidity.borrowed_amount_wads == Decimal::zero() {
1278        msg!("Liquidity borrowed amount is zero");
1279        return Err(LendingError::ObligationLiquidityEmpty.into());
1280    }
1281
1282    let CalculateRepayResult {
1283        settle_amount,
1284        repay_amount,
1285    } = repay_reserve.calculate_repay(liquidity_amount, liquidity.borrowed_amount_wads)?;
1286
1287    if repay_amount == 0 {
1288        msg!("Repay amount is too small to transfer liquidity");
1289        return Err(LendingError::RepayTooSmall.into());
1290    }
1291
1292    repay_reserve.liquidity.repay(repay_amount, settle_amount)?;
1293    repay_reserve.last_update.mark_stale();
1294    Reserve::pack(repay_reserve, &mut repay_reserve_info.data.borrow_mut())?;
1295
1296    obligation.repay(settle_amount, liquidity_index)?;
1297    obligation.last_update.mark_stale();
1298    Obligation::pack(obligation, &mut obligation_info.data.borrow_mut())?;
1299
1300    spl_token_transfer(TokenTransferParams {
1301        source: source_liquidity_info.clone(),
1302        destination: destination_liquidity_info.clone(),
1303        amount: repay_amount,
1304        authority: user_transfer_authority_info.clone(),
1305        authority_signer_seeds: &[],
1306        token_program: token_program_id.clone(),
1307    })?;
1308
1309    Ok(())
1310}
1311
1312#[inline(never)] // avoid stack frame limit
1313fn process_liquidate_obligation(
1314    program_id: &Pubkey,
1315    liquidity_amount: u64,
1316    accounts: &[AccountInfo],
1317) -> ProgramResult {
1318    if liquidity_amount == 0 {
1319        msg!("Liquidity amount provided cannot be zero");
1320        return Err(LendingError::InvalidAmount.into());
1321    }
1322
1323    let account_info_iter = &mut accounts.iter();
1324    let source_liquidity_info = next_account_info(account_info_iter)?;
1325    let destination_collateral_info = next_account_info(account_info_iter)?;
1326    let repay_reserve_info = next_account_info(account_info_iter)?;
1327    let repay_reserve_liquidity_supply_info = next_account_info(account_info_iter)?;
1328    let withdraw_reserve_info = next_account_info(account_info_iter)?;
1329    let withdraw_reserve_collateral_supply_info = next_account_info(account_info_iter)?;
1330    let obligation_info = next_account_info(account_info_iter)?;
1331    let lending_market_info = next_account_info(account_info_iter)?;
1332    let lending_market_authority_info = next_account_info(account_info_iter)?;
1333    let user_transfer_authority_info = next_account_info(account_info_iter)?;
1334    let clock = &Clock::from_account_info(next_account_info(account_info_iter)?)?;
1335    let token_program_id = next_account_info(account_info_iter)?;
1336
1337    let lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
1338    if lending_market_info.owner != program_id {
1339        msg!("Lending market provided is not owned by the lending program");
1340        return Err(LendingError::InvalidAccountOwner.into());
1341    }
1342    if &lending_market.token_program_id != token_program_id.key {
1343        msg!("Lending market token program does not match the token program provided");
1344        return Err(LendingError::InvalidTokenProgram.into());
1345    }
1346
1347    let mut repay_reserve = Reserve::unpack(&repay_reserve_info.data.borrow())?;
1348    if repay_reserve_info.owner != program_id {
1349        msg!("Repay reserve provided is not owned by the lending program");
1350        return Err(LendingError::InvalidAccountOwner.into());
1351    }
1352    if &repay_reserve.lending_market != lending_market_info.key {
1353        msg!("Repay reserve lending market does not match the lending market provided");
1354        return Err(LendingError::InvalidAccountInput.into());
1355    }
1356    if &repay_reserve.liquidity.supply_pubkey != repay_reserve_liquidity_supply_info.key {
1357        msg!("Repay reserve liquidity supply does not match the repay reserve liquidity supply provided");
1358        return Err(LendingError::InvalidAccountInput.into());
1359    }
1360    if &repay_reserve.liquidity.supply_pubkey == source_liquidity_info.key {
1361        msg!("Repay reserve liquidity supply cannot be used as the source liquidity provided");
1362        return Err(LendingError::InvalidAccountInput.into());
1363    }
1364    if &repay_reserve.collateral.supply_pubkey == destination_collateral_info.key {
1365        msg!(
1366            "Repay reserve collateral supply cannot be used as the destination collateral provided"
1367        );
1368        return Err(LendingError::InvalidAccountInput.into());
1369    }
1370    if repay_reserve.last_update.is_stale(clock.slot)? {
1371        msg!("Repay reserve is stale and must be refreshed in the current slot");
1372        return Err(LendingError::ReserveStale.into());
1373    }
1374
1375    let withdraw_reserve = Reserve::unpack(&withdraw_reserve_info.data.borrow())?;
1376    if withdraw_reserve_info.owner != program_id {
1377        msg!("Withdraw reserve provided is not owned by the lending program");
1378        return Err(LendingError::InvalidAccountOwner.into());
1379    }
1380    if &withdraw_reserve.lending_market != lending_market_info.key {
1381        msg!("Withdraw reserve lending market does not match the lending market provided");
1382        return Err(LendingError::InvalidAccountInput.into());
1383    }
1384    if &withdraw_reserve.collateral.supply_pubkey != withdraw_reserve_collateral_supply_info.key {
1385        msg!("Withdraw reserve collateral supply does not match the withdraw reserve collateral supply provided");
1386        return Err(LendingError::InvalidAccountInput.into());
1387    }
1388    if &withdraw_reserve.liquidity.supply_pubkey == source_liquidity_info.key {
1389        msg!("Withdraw reserve liquidity supply cannot be used as the source liquidity provided");
1390        return Err(LendingError::InvalidAccountInput.into());
1391    }
1392    if &withdraw_reserve.collateral.supply_pubkey == destination_collateral_info.key {
1393        msg!("Withdraw reserve collateral supply cannot be used as the destination collateral provided");
1394        return Err(LendingError::InvalidAccountInput.into());
1395    }
1396    if withdraw_reserve.last_update.is_stale(clock.slot)? {
1397        msg!("Withdraw reserve is stale and must be refreshed in the current slot");
1398        return Err(LendingError::ReserveStale.into());
1399    }
1400
1401    let mut obligation = Obligation::unpack(&obligation_info.data.borrow())?;
1402    if obligation_info.owner != program_id {
1403        msg!("Obligation provided is not owned by the lending program");
1404        return Err(LendingError::InvalidAccountOwner.into());
1405    }
1406    if &obligation.lending_market != lending_market_info.key {
1407        msg!("Obligation lending market does not match the lending market provided");
1408        return Err(LendingError::InvalidAccountInput.into());
1409    }
1410    if obligation.last_update.is_stale(clock.slot)? {
1411        msg!("Obligation is stale and must be refreshed in the current slot");
1412        return Err(LendingError::ObligationStale.into());
1413    }
1414    if obligation.deposited_value == Decimal::zero() {
1415        msg!("Obligation deposited value is zero");
1416        return Err(LendingError::ObligationDepositsZero.into());
1417    }
1418    if obligation.borrowed_value == Decimal::zero() {
1419        msg!("Obligation borrowed value is zero");
1420        return Err(LendingError::ObligationBorrowsZero.into());
1421    }
1422    if obligation.borrowed_value < obligation.unhealthy_borrow_value {
1423        msg!("Obligation is healthy and cannot be liquidated");
1424        return Err(LendingError::ObligationHealthy.into());
1425    }
1426
1427    let (liquidity, liquidity_index) =
1428        obligation.find_liquidity_in_borrows(*repay_reserve_info.key)?;
1429    if liquidity.market_value == Decimal::zero() {
1430        msg!("Obligation borrow value is zero");
1431        return Err(LendingError::ObligationLiquidityEmpty.into());
1432    }
1433
1434    let (collateral, collateral_index) =
1435        obligation.find_collateral_in_deposits(*withdraw_reserve_info.key)?;
1436    if collateral.market_value == Decimal::zero() {
1437        msg!("Obligation deposit value is zero");
1438        return Err(LendingError::ObligationCollateralEmpty.into());
1439    }
1440
1441    let authority_signer_seeds = &[
1442        lending_market_info.key.as_ref(),
1443        &[lending_market.bump_seed],
1444    ];
1445    let lending_market_authority_pubkey =
1446        Pubkey::create_program_address(authority_signer_seeds, program_id)?;
1447    if &lending_market_authority_pubkey != lending_market_authority_info.key {
1448        msg!(
1449            "Derived lending market authority does not match the lending market authority provided"
1450        );
1451        return Err(LendingError::InvalidMarketAuthority.into());
1452    }
1453
1454    let CalculateLiquidationResult {
1455        settle_amount,
1456        repay_amount,
1457        withdraw_amount,
1458    } = withdraw_reserve.calculate_liquidation(
1459        liquidity_amount,
1460        &obligation,
1461        liquidity,
1462        collateral,
1463    )?;
1464
1465    if repay_amount == 0 {
1466        msg!("Liquidation is too small to transfer liquidity");
1467        return Err(LendingError::LiquidationTooSmall.into());
1468    }
1469    if withdraw_amount == 0 {
1470        msg!("Liquidation is too small to receive collateral");
1471        return Err(LendingError::LiquidationTooSmall.into());
1472    }
1473
1474    repay_reserve.liquidity.repay(repay_amount, settle_amount)?;
1475    repay_reserve.last_update.mark_stale();
1476    Reserve::pack(repay_reserve, &mut repay_reserve_info.data.borrow_mut())?;
1477
1478    obligation.repay(settle_amount, liquidity_index)?;
1479    obligation.withdraw(withdraw_amount, collateral_index)?;
1480    obligation.last_update.mark_stale();
1481    Obligation::pack(obligation, &mut obligation_info.data.borrow_mut())?;
1482
1483    spl_token_transfer(TokenTransferParams {
1484        source: source_liquidity_info.clone(),
1485        destination: repay_reserve_liquidity_supply_info.clone(),
1486        amount: repay_amount,
1487        authority: user_transfer_authority_info.clone(),
1488        authority_signer_seeds: &[],
1489        token_program: token_program_id.clone(),
1490    })?;
1491
1492    spl_token_transfer(TokenTransferParams {
1493        source: withdraw_reserve_collateral_supply_info.clone(),
1494        destination: destination_collateral_info.clone(),
1495        amount: withdraw_amount,
1496        authority: lending_market_authority_info.clone(),
1497        authority_signer_seeds,
1498        token_program: token_program_id.clone(),
1499    })?;
1500
1501    Ok(())
1502}
1503
1504#[inline(never)] // avoid stack frame limit
1505fn process_flash_loan(
1506    program_id: &Pubkey,
1507    liquidity_amount: u64,
1508    accounts: &[AccountInfo],
1509) -> ProgramResult {
1510    if liquidity_amount == 0 {
1511        msg!("Liquidity amount provided cannot be zero");
1512        return Err(LendingError::InvalidAmount.into());
1513    }
1514
1515    let account_info_iter = &mut accounts.iter();
1516    let source_liquidity_info = next_account_info(account_info_iter)?;
1517    let destination_liquidity_info = next_account_info(account_info_iter)?;
1518    let reserve_info = next_account_info(account_info_iter)?;
1519    let reserve_liquidity_fee_receiver_info = next_account_info(account_info_iter)?;
1520    let host_fee_receiver_info = next_account_info(account_info_iter)?;
1521    let lending_market_info = next_account_info(account_info_iter)?;
1522    let lending_market_authority_info = next_account_info(account_info_iter)?;
1523    let token_program_id = next_account_info(account_info_iter)?;
1524    let flash_loan_receiver_program_id = next_account_info(account_info_iter)?;
1525
1526    if program_id == flash_loan_receiver_program_id.key {
1527        msg!("Lending program cannot be used as the flash loan receiver program provided");
1528        return Err(LendingError::InvalidFlashLoanReceiverProgram.into());
1529    }
1530
1531    let lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
1532    if lending_market_info.owner != program_id {
1533        return Err(LendingError::InvalidAccountOwner.into());
1534    }
1535    if &lending_market.token_program_id != token_program_id.key {
1536        msg!("Lending market token program does not match the token program provided");
1537        return Err(LendingError::InvalidTokenProgram.into());
1538    }
1539
1540    let authority_signer_seeds = &[
1541        lending_market_info.key.as_ref(),
1542        &[lending_market.bump_seed],
1543    ];
1544    let lending_market_authority_pubkey =
1545        Pubkey::create_program_address(authority_signer_seeds, program_id)?;
1546    if &lending_market_authority_pubkey != lending_market_authority_info.key {
1547        msg!(
1548            "Derived lending market authority does not match the lending market authority provided"
1549        );
1550        return Err(LendingError::InvalidMarketAuthority.into());
1551    }
1552
1553    let mut reserve = Reserve::unpack(&reserve_info.data.borrow())?;
1554    if reserve_info.owner != program_id {
1555        msg!("Reserve provided is not owned by the lending program");
1556        return Err(LendingError::InvalidAccountOwner.into());
1557    }
1558    if &reserve.lending_market != lending_market_info.key {
1559        msg!("Invalid reserve lending market account");
1560        return Err(LendingError::InvalidAccountInput.into());
1561    }
1562    if &reserve.liquidity.supply_pubkey != source_liquidity_info.key {
1563        msg!("Reserve liquidity supply must be used as the source liquidity provided");
1564        return Err(LendingError::InvalidAccountInput.into());
1565    }
1566    if &reserve.liquidity.fee_receiver != reserve_liquidity_fee_receiver_info.key {
1567        msg!("Reserve liquidity fee receiver does not match the reserve liquidity fee receiver provided");
1568        return Err(LendingError::InvalidAccountInput.into());
1569    }
1570
1571    // @FIXME: if u64::MAX is flash loaned, fees should be inclusive as with ordinary borrows
1572    let flash_loan_amount = if liquidity_amount == u64::MAX {
1573        reserve.liquidity.available_amount
1574    } else {
1575        liquidity_amount
1576    };
1577
1578    let flash_loan_amount_decimal = Decimal::from(flash_loan_amount);
1579    let (origination_fee, host_fee) = reserve
1580        .config
1581        .fees
1582        .calculate_flash_loan_fees(flash_loan_amount_decimal)?;
1583
1584    let balance_before_flash_loan = Account::unpack(&source_liquidity_info.data.borrow())?.amount;
1585    let expected_balance_after_flash_loan = balance_before_flash_loan
1586        .checked_add(origination_fee)
1587        .ok_or(LendingError::MathOverflow)?;
1588    let returned_amount_required = flash_loan_amount
1589        .checked_add(origination_fee)
1590        .ok_or(LendingError::MathOverflow)?;
1591
1592    let mut flash_loan_instruction_accounts = vec![
1593        AccountMeta::new(*destination_liquidity_info.key, false),
1594        AccountMeta::new(*source_liquidity_info.key, false),
1595        AccountMeta::new_readonly(*token_program_id.key, false),
1596    ];
1597    let mut flash_loan_instruction_account_infos = vec![
1598        destination_liquidity_info.clone(),
1599        flash_loan_receiver_program_id.clone(),
1600        source_liquidity_info.clone(),
1601        token_program_id.clone(),
1602    ];
1603    for account_info in account_info_iter {
1604        flash_loan_instruction_accounts.push(AccountMeta {
1605            pubkey: *account_info.key,
1606            is_signer: account_info.is_signer,
1607            is_writable: account_info.is_writable,
1608        });
1609        flash_loan_instruction_account_infos.push(account_info.clone());
1610    }
1611
1612    reserve.liquidity.borrow(flash_loan_amount_decimal)?;
1613    Reserve::pack(reserve, &mut reserve_info.data.borrow_mut())?;
1614
1615    spl_token_transfer(TokenTransferParams {
1616        source: source_liquidity_info.clone(),
1617        destination: destination_liquidity_info.clone(),
1618        amount: flash_loan_amount,
1619        authority: lending_market_authority_info.clone(),
1620        authority_signer_seeds,
1621        token_program: token_program_id.clone(),
1622    })?;
1623
1624    const RECEIVE_FLASH_LOAN_INSTRUCTION_DATA_SIZE: usize = 9;
1625    // @FIXME: don't use 0 to indicate a flash loan receiver instruction https://git.io/JGzz9
1626    const RECEIVE_FLASH_LOAN_INSTRUCTION_TAG: u8 = 0u8;
1627
1628    let mut data = Vec::with_capacity(RECEIVE_FLASH_LOAN_INSTRUCTION_DATA_SIZE);
1629    data.push(RECEIVE_FLASH_LOAN_INSTRUCTION_TAG);
1630    data.extend_from_slice(&returned_amount_required.to_le_bytes());
1631
1632    invoke(
1633        &Instruction {
1634            program_id: *flash_loan_receiver_program_id.key,
1635            accounts: flash_loan_instruction_accounts,
1636            data,
1637        },
1638        &flash_loan_instruction_account_infos[..],
1639    )?;
1640
1641    reserve = Reserve::unpack(&reserve_info.data.borrow())?;
1642    reserve
1643        .liquidity
1644        .repay(flash_loan_amount, flash_loan_amount_decimal)?;
1645    Reserve::pack(reserve, &mut reserve_info.data.borrow_mut())?;
1646
1647    let actual_balance_after_flash_loan =
1648        Account::unpack(&source_liquidity_info.data.borrow())?.amount;
1649    if actual_balance_after_flash_loan < expected_balance_after_flash_loan {
1650        msg!("Insufficient reserve liquidity after flash loan");
1651        return Err(LendingError::NotEnoughLiquidityAfterFlashLoan.into());
1652    }
1653
1654    let mut owner_fee = origination_fee;
1655    if host_fee > 0 {
1656        owner_fee = owner_fee
1657            .checked_sub(host_fee)
1658            .ok_or(LendingError::MathOverflow)?;
1659        spl_token_transfer(TokenTransferParams {
1660            source: source_liquidity_info.clone(),
1661            destination: host_fee_receiver_info.clone(),
1662            amount: host_fee,
1663            authority: lending_market_authority_info.clone(),
1664            authority_signer_seeds,
1665            token_program: token_program_id.clone(),
1666        })?;
1667    }
1668
1669    if owner_fee > 0 {
1670        spl_token_transfer(TokenTransferParams {
1671            source: source_liquidity_info.clone(),
1672            destination: reserve_liquidity_fee_receiver_info.clone(),
1673            amount: owner_fee,
1674            authority: lending_market_authority_info.clone(),
1675            authority_signer_seeds,
1676            token_program: token_program_id.clone(),
1677        })?;
1678    }
1679
1680    Ok(())
1681}
1682
1683fn process_modify_reserve_config(
1684    program_id: &Pubkey,
1685    new_config: ReserveConfig,
1686    accounts: &[AccountInfo],
1687) -> ProgramResult {
1688    new_config.validate()?;
1689
1690    let account_info_iter = &mut accounts.iter().peekable();
1691    let reserve_info = next_account_info(account_info_iter)?;
1692    let lending_market_info = next_account_info(account_info_iter)?;
1693    let lending_market_owner_info = next_account_info(account_info_iter)?;
1694
1695    if reserve_info.owner != program_id {
1696        msg!("Reserve provided is not owned by the lending program");
1697        return Err(LendingError::InvalidAccountOwner.into());
1698    }
1699
1700    let lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
1701    if lending_market_info.owner != program_id {
1702        msg!("Lending market provided is not owned by the lending program");
1703        return Err(LendingError::InvalidAccountOwner.into());
1704    }
1705    if &lending_market.owner != lending_market_owner_info.key {
1706        msg!("Lending market owner does not match the lending market owner provided");
1707        return Err(LendingError::InvalidMarketOwner.into());
1708    }
1709    if !lending_market_owner_info.is_signer {
1710        msg!("Lending market owner provided must be a signer");
1711        return Err(LendingError::InvalidSigner.into());
1712    }
1713
1714    let mut reserve = Reserve::unpack(&reserve_info.data.borrow_mut())?;
1715    // Validate that the reserve account corresponds to the correct lending market,
1716    // after validating above that the lending market and lending market owner correspond,
1717    // to prevent one compromised lending market owner from changing configs on other lending markets
1718    if reserve.lending_market != *lending_market_info.key {
1719        msg!("Reserve account does not match the lending market");
1720        return Err(LendingError::InvalidAccountInput.into());
1721    }
1722
1723    reserve.config = new_config;
1724
1725    Reserve::pack(reserve, &mut reserve_info.data.borrow_mut())?;
1726
1727    Ok(())
1728}
1729
1730fn assert_rent_exempt(rent: &Rent, account_info: &AccountInfo) -> ProgramResult {
1731    if !rent.is_exempt(account_info.lamports(), account_info.data_len()) {
1732        msg!(&rent.minimum_balance(account_info.data_len()).to_string());
1733        Err(LendingError::NotRentExempt.into())
1734    } else {
1735        Ok(())
1736    }
1737}
1738
1739fn assert_uninitialized<T: Pack + IsInitialized>(
1740    account_info: &AccountInfo,
1741) -> Result<T, ProgramError> {
1742    let account: T = T::unpack_unchecked(&account_info.data.borrow())?;
1743    if account.is_initialized() {
1744        Err(LendingError::AlreadyInitialized.into())
1745    } else {
1746        Ok(account)
1747    }
1748}
1749
1750/// Unpacks a spl_token `Mint`.
1751fn unpack_mint(data: &[u8]) -> Result<Mint, LendingError> {
1752    Mint::unpack(data).map_err(|_| LendingError::InvalidTokenMint)
1753}
1754
1755fn get_pyth_product_quote_currency(pyth_product: &pyth::Product) -> Result<[u8; 32], ProgramError> {
1756    const LEN: usize = 14;
1757    const KEY: &[u8; LEN] = b"quote_currency";
1758
1759    let mut start = 0;
1760    while start < pyth::PROD_ATTR_SIZE {
1761        let mut length = pyth_product.attr[start] as usize;
1762        start += 1;
1763
1764        if length == LEN {
1765            let mut end = start + length;
1766            if end > pyth::PROD_ATTR_SIZE {
1767                msg!("Pyth product attribute key length too long");
1768                return Err(LendingError::InvalidOracleConfig.into());
1769            }
1770
1771            let key = &pyth_product.attr[start..end];
1772            if key == KEY {
1773                start += length;
1774                length = pyth_product.attr[start] as usize;
1775                start += 1;
1776
1777                end = start + length;
1778                if length > 32 || end > pyth::PROD_ATTR_SIZE {
1779                    msg!("Pyth product quote currency value too long");
1780                    return Err(LendingError::InvalidOracleConfig.into());
1781                }
1782
1783                let mut value = [0u8; 32];
1784                value[0..length].copy_from_slice(&pyth_product.attr[start..end]);
1785                return Ok(value);
1786            }
1787        }
1788
1789        start += length;
1790        start += 1 + pyth_product.attr[start] as usize;
1791    }
1792
1793    msg!("Pyth product quote currency not found");
1794    Err(LendingError::InvalidOracleConfig.into())
1795}
1796
1797fn get_pyth_price(pyth_price_info: &AccountInfo, clock: &Clock) -> Result<Decimal, ProgramError> {
1798    const STALE_AFTER_SLOTS_ELAPSED: u64 = 5;
1799
1800    let pyth_price_data = pyth_price_info.try_borrow_data()?;
1801    let pyth_price = pyth::load::<pyth::Price>(&pyth_price_data)
1802        .map_err(|_| ProgramError::InvalidAccountData)?;
1803
1804    if pyth_price.ptype != pyth::PriceType::Price {
1805        msg!("Oracle price type is invalid");
1806        return Err(LendingError::InvalidOracleConfig.into());
1807    }
1808
1809    if pyth_price.agg.status != pyth::PriceStatus::Trading {
1810        msg!("Oracle price status is invalid");
1811        return Err(LendingError::InvalidOracleConfig.into());
1812    }
1813
1814    let slots_elapsed = clock
1815        .slot
1816        .checked_sub(pyth_price.valid_slot)
1817        .ok_or(LendingError::MathOverflow)?;
1818    if slots_elapsed >= STALE_AFTER_SLOTS_ELAPSED {
1819        msg!("Oracle price is stale");
1820        return Err(LendingError::InvalidOracleConfig.into());
1821    }
1822
1823    let price: u64 = pyth_price.agg.price.try_into().map_err(|_| {
1824        msg!("Oracle price cannot be negative");
1825        LendingError::InvalidOracleConfig
1826    })?;
1827
1828    let market_price = if pyth_price.expo >= 0 {
1829        let exponent = pyth_price
1830            .expo
1831            .try_into()
1832            .map_err(|_| LendingError::MathOverflow)?;
1833        let zeros = 10u64
1834            .checked_pow(exponent)
1835            .ok_or(LendingError::MathOverflow)?;
1836        Decimal::from(price).try_mul(zeros)?
1837    } else {
1838        let exponent = pyth_price
1839            .expo
1840            .checked_abs()
1841            .ok_or(LendingError::MathOverflow)?
1842            .try_into()
1843            .map_err(|_| LendingError::MathOverflow)?;
1844        let decimals = 10u64
1845            .checked_pow(exponent)
1846            .ok_or(LendingError::MathOverflow)?;
1847        Decimal::from(price).try_div(decimals)?
1848    };
1849
1850    Ok(market_price)
1851}
1852
1853/// Issue a spl_token `InitializeAccount` instruction.
1854#[inline(always)]
1855fn spl_token_init_account(params: TokenInitializeAccountParams<'_>) -> ProgramResult {
1856    let TokenInitializeAccountParams {
1857        account,
1858        mint,
1859        owner,
1860        rent,
1861        token_program,
1862    } = params;
1863    let ix = spl_token::instruction::initialize_account(
1864        token_program.key,
1865        account.key,
1866        mint.key,
1867        owner.key,
1868    )?;
1869    let result = invoke(&ix, &[account, mint, owner, rent, token_program]);
1870    result.map_err(|_| LendingError::TokenInitializeAccountFailed.into())
1871}
1872
1873/// Issue a spl_token `InitializeMint` instruction.
1874#[inline(always)]
1875fn spl_token_init_mint(params: TokenInitializeMintParams<'_, '_>) -> ProgramResult {
1876    let TokenInitializeMintParams {
1877        mint,
1878        rent,
1879        authority,
1880        token_program,
1881        decimals,
1882    } = params;
1883    let ix = spl_token::instruction::initialize_mint(
1884        token_program.key,
1885        mint.key,
1886        authority,
1887        None,
1888        decimals,
1889    )?;
1890    let result = invoke(&ix, &[mint, rent, token_program]);
1891    result.map_err(|_| LendingError::TokenInitializeMintFailed.into())
1892}
1893
1894/// Invoke signed unless signers seeds are empty
1895#[inline(always)]
1896fn invoke_optionally_signed(
1897    instruction: &Instruction,
1898    account_infos: &[AccountInfo],
1899    authority_signer_seeds: &[&[u8]],
1900) -> ProgramResult {
1901    if authority_signer_seeds.is_empty() {
1902        invoke(instruction, account_infos)
1903    } else {
1904        invoke_signed(instruction, account_infos, &[authority_signer_seeds])
1905    }
1906}
1907
1908/// Issue a spl_token `Transfer` instruction.
1909#[inline(always)]
1910fn spl_token_transfer(params: TokenTransferParams<'_, '_>) -> ProgramResult {
1911    let TokenTransferParams {
1912        source,
1913        destination,
1914        authority,
1915        token_program,
1916        amount,
1917        authority_signer_seeds,
1918    } = params;
1919    let result = invoke_optionally_signed(
1920        &spl_token::instruction::transfer(
1921            token_program.key,
1922            source.key,
1923            destination.key,
1924            authority.key,
1925            &[],
1926            amount,
1927        )?,
1928        &[source, destination, authority, token_program],
1929        authority_signer_seeds,
1930    );
1931    result.map_err(|_| LendingError::TokenTransferFailed.into())
1932}
1933
1934/// Issue a spl_token `MintTo` instruction.
1935fn spl_token_mint_to(params: TokenMintToParams<'_, '_>) -> ProgramResult {
1936    let TokenMintToParams {
1937        mint,
1938        destination,
1939        authority,
1940        token_program,
1941        amount,
1942        authority_signer_seeds,
1943    } = params;
1944    let result = invoke_optionally_signed(
1945        &spl_token::instruction::mint_to(
1946            token_program.key,
1947            mint.key,
1948            destination.key,
1949            authority.key,
1950            &[],
1951            amount,
1952        )?,
1953        &[mint, destination, authority, token_program],
1954        authority_signer_seeds,
1955    );
1956    result.map_err(|_| LendingError::TokenMintToFailed.into())
1957}
1958
1959/// Issue a spl_token `Burn` instruction.
1960#[inline(always)]
1961fn spl_token_burn(params: TokenBurnParams<'_, '_>) -> ProgramResult {
1962    let TokenBurnParams {
1963        mint,
1964        source,
1965        authority,
1966        token_program,
1967        amount,
1968        authority_signer_seeds,
1969    } = params;
1970    let result = invoke_optionally_signed(
1971        &spl_token::instruction::burn(
1972            token_program.key,
1973            source.key,
1974            mint.key,
1975            authority.key,
1976            &[],
1977            amount,
1978        )?,
1979        &[source, mint, authority, token_program],
1980        authority_signer_seeds,
1981    );
1982    result.map_err(|_| LendingError::TokenBurnFailed.into())
1983}
1984
1985struct TokenInitializeMintParams<'a: 'b, 'b> {
1986    mint: AccountInfo<'a>,
1987    rent: AccountInfo<'a>,
1988    authority: &'b Pubkey,
1989    decimals: u8,
1990    token_program: AccountInfo<'a>,
1991}
1992
1993struct TokenInitializeAccountParams<'a> {
1994    account: AccountInfo<'a>,
1995    mint: AccountInfo<'a>,
1996    owner: AccountInfo<'a>,
1997    rent: AccountInfo<'a>,
1998    token_program: AccountInfo<'a>,
1999}
2000
2001struct TokenTransferParams<'a: 'b, 'b> {
2002    source: AccountInfo<'a>,
2003    destination: AccountInfo<'a>,
2004    amount: u64,
2005    authority: AccountInfo<'a>,
2006    authority_signer_seeds: &'b [&'b [u8]],
2007    token_program: AccountInfo<'a>,
2008}
2009
2010struct TokenMintToParams<'a: 'b, 'b> {
2011    mint: AccountInfo<'a>,
2012    destination: AccountInfo<'a>,
2013    amount: u64,
2014    authority: AccountInfo<'a>,
2015    authority_signer_seeds: &'b [&'b [u8]],
2016    token_program: AccountInfo<'a>,
2017}
2018
2019struct TokenBurnParams<'a: 'b, 'b> {
2020    mint: AccountInfo<'a>,
2021    source: AccountInfo<'a>,
2022    amount: u64,
2023    authority: AccountInfo<'a>,
2024    authority_signer_seeds: &'b [&'b [u8]],
2025    token_program: AccountInfo<'a>,
2026}
2027
2028impl PrintProgramError for LendingError {
2029    fn print<E>(&self)
2030    where
2031        E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
2032    {
2033        msg!(&self.to_string());
2034    }
2035}