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