1use 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
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 {
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)] fn 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)] fn 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 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 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)] fn 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)] fn 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)] fn 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)] fn 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)] fn 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)] fn 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 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 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 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
1750fn 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#[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#[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#[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#[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
1934fn 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#[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}