port_variable_rate_lending/
instruction.rs

1//! Instruction types
2
3use std::{convert::TryInto, mem::size_of};
4
5use solana_program::{
6    instruction::{AccountMeta, Instruction},
7    msg,
8    program_error::ProgramError,
9    pubkey::{Pubkey, PUBKEY_BYTES},
10    sysvar,
11};
12use solana_program::program_option::COption;
13
14use crate::error::LendingError;
15
16/// Instructions supported by the lending program.
17#[derive(Clone, Debug, PartialEq)]
18pub enum LendingInstruction {
19    // 3
20    /// Accrue interest and update market price of liquidity on a reserve.
21    ///
22    /// Accounts expected by this instruction:
23    ///
24    ///   0. `[writable]` Reserve account.
25    ///   1. `[]` Clock sysvar.
26    ///   2. `[]` Reserve liquidity oracle account.
27    ///             Must be the Pyth price account specified at InitReserve.
28    RefreshReserve,
29
30    // 4
31    /// Deposit liquidity into a reserve in exchange for collateral. Collateral represents a share
32    /// of the reserve liquidity pool.
33    ///
34    /// Accounts expected by this instruction:
35    ///
36    ///   0. `[writable]` Source liquidity token account.
37    ///                     $authority can transfer $liquidity_amount.
38    ///   1. `[writable]` Destination collateral token account.
39    ///   2. `[writable]` Reserve account.
40    ///   3. `[writable]` Reserve liquidity supply SPL Token account.
41    ///   4. `[writable]` Reserve collateral SPL Token mint.
42    ///   5. `[]` Lending market account.
43    ///   6. `[]` Derived lending market authority.
44    ///   7. `[signer]` User transfer authority ($authority).
45    ///   8. `[]` Clock sysvar.
46    ///   9. `[]` Token program id.
47    DepositReserveLiquidity {
48        /// Amount of liquidity to deposit in exchange for collateral tokens
49        liquidity_amount: u64,
50    },
51
52    // 5
53    /// Redeem collateral from a reserve in exchange for liquidity.
54    ///
55    /// Accounts expected by this instruction:
56    ///
57    ///   0. `[writable]` Source collateral token account.
58    ///                     $authority can transfer $collateral_amount.
59    ///   1. `[writable]` Destination liquidity token account.
60    ///   2. `[writable]` Reserve account.
61    ///   3. `[writable]` Reserve collateral SPL Token mint.
62    ///   4. `[writable]` Reserve liquidity supply SPL Token account.
63    ///   5. `[]` Lending market account.
64    ///   6. `[]` Derived lending market authority.
65    ///   7. `[signer]` User transfer authority ($authority).
66    ///   8. `[]` Clock sysvar.
67    ///   9. `[]` Token program id.
68    RedeemReserveCollateral {
69        /// Amount of collateral tokens to redeem in exchange for liquidity
70        collateral_amount: u64,
71    },
72
73    // 6
74    /// Initializes a new lending market obligation.
75    ///
76    /// Accounts expected by this instruction:
77    ///
78    ///   0. `[writable]` Obligation account - uninitialized.
79    ///   1. `[]` Lending market account.
80    ///   2. `[signer]` Obligation owner.
81    ///   3. `[]` Clock sysvar.
82    ///   4. `[]` Rent sysvar.
83    ///   5. `[]` Token program id.
84    InitObligation,
85
86    // 7
87    /// Refresh an obligation's accrued interest and collateral and liquidity prices. Requires
88    /// refreshed reserves, as all obligation collateral deposit reserves in order, followed by all
89    /// liquidity borrow reserves in order.
90    ///
91    /// Accounts expected by this instruction:
92    ///
93    ///   0. `[writable]` Obligation account.
94    ///   1. `[]` Clock sysvar.
95    ///   .. `[]` Collateral deposit reserve accounts - refreshed, all, in order.
96    ///   .. `[]` Liquidity borrow reserve accounts - refreshed, all, in order.
97    RefreshObligation,
98
99    // 8
100    /// Deposit collateral to an obligation. Requires a refreshed reserve.
101    ///
102    /// Accounts expected by this instruction:
103    ///
104    ///   0. `[writable]` Source collateral token account.
105    ///                     Minted by deposit reserve collateral mint.
106    ///                     $authority can transfer $collateral_amount.
107    ///   1. `[writable]` Destination deposit reserve collateral supply SPL Token account.
108    ///   2. `[]` Deposit reserve account - refreshed.
109    ///   3. `[writable]` Obligation account.
110    ///   4. `[]` Lending market account.
111    ///   5. `[]` Derived lending market authority.
112    ///   6. `[signer]` Obligation owner.
113    ///   7. `[signer]` User transfer authority ($authority).
114    ///   8. `[]` Clock sysvar.
115    ///   9. `[]` Token program id.
116    ///   10 `[writable, optional]` Stake account.
117    ///   11 `[writable, optional]` Staking pool.
118    ///   12 `[optional]` staking program id.
119    DepositObligationCollateral {
120        /// Amount of collateral tokens to deposit
121        collateral_amount: u64,
122    },
123
124    // 9
125    /// Withdraw collateral from an obligation. Requires a refreshed obligation and reserve.
126    ///
127    /// Accounts expected by this instruction:
128    ///
129    ///   0. `[writable]` Source withdraw reserve collateral supply SPL Token account.
130    ///   1. `[writable]` Destination collateral token account.
131    ///                     Minted by withdraw reserve collateral mint.
132    ///   2. `[]` Withdraw reserve account - refreshed.
133    ///   3. `[writable]` Obligation account - refreshed.
134    ///   4. `[]` Lending market account.
135    ///   5. `[]` Derived lending market authority.
136    ///   6. `[signer]` Obligation owner.
137    ///   7. `[]` Clock sysvar.
138    ///   8. `[]` Token program id.
139    ///   9. `[writable, optional]` Stake account.
140    ///   10 `[writable, optional]` Staking pool.
141    ///   11 `[optional]` staking program id.
142    WithdrawObligationCollateral {
143        /// Amount of collateral tokens to withdraw - u64::MAX for up to 100% of deposited amount
144        collateral_amount: u64,
145    },
146
147    // 10
148    /// Borrow liquidity from a reserve by depositing collateral tokens. Requires a refreshed
149    /// obligation and reserve.
150    ///
151    /// Accounts expected by this instruction:
152    ///
153    ///   0. `[writable]` Source borrow reserve liquidity supply SPL Token account.
154    ///   1. `[writable]` Destination liquidity token account.
155    ///                     Minted by borrow reserve liquidity mint.
156    ///   2. `[writable]` Borrow reserve account - refreshed.
157    ///   3. `[writable]` Borrow reserve liquidity fee receiver account.
158    ///                     Must be the fee account specified at InitReserve.
159    ///   4. `[writable]` Obligation account - refreshed.
160    ///   5. `[]` Lending market account.
161    ///   6. `[]` Derived lending market authority.
162    ///   7. `[signer]` Obligation owner.
163    ///   8. `[]` Clock sysvar.
164    ///   9. `[]` Token program id.
165    BorrowObligationLiquidity {
166        /// Amount of liquidity to borrow - u64::MAX for 100% of borrowing power
167        liquidity_amount: u64,
168        // @TODO: slippage constraint - https://git.io/JmV67
169    },
170
171    // 11
172    /// Repay borrowed liquidity to a reserve. Requires a refreshed obligation and reserve.
173    ///
174    /// Accounts expected by this instruction:
175    ///
176    ///   0. `[writable]` Source liquidity token account.
177    ///                     Minted by repay reserve liquidity mint.
178    ///                     $authority can transfer $liquidity_amount.
179    ///   1. `[writable]` Destination repay reserve liquidity supply SPL Token account.
180    ///   2. `[writable]` Repay reserve account - refreshed.
181    ///   3. `[writable]` Obligation account - refreshed.
182    ///   4. `[]` Lending market account.
183    ///   5. `[signer]` User transfer authority ($authority).
184    ///   6. `[]` Clock sysvar.
185    ///   7. `[]` Token program id.
186    RepayObligationLiquidity {
187        /// Amount of liquidity to repay - u64::MAX for 100% of borrowed amount
188        liquidity_amount: u64,
189    },
190
191    // 12
192    /// Repay borrowed liquidity to a reserve to receive collateral at a discount from an unhealthy
193    /// obligation. Requires a refreshed obligation and reserves.
194    ///
195    /// Accounts expected by this instruction:
196    ///
197    ///   0. `[writable]` Source liquidity token account.
198    ///                     Minted by repay reserve liquidity mint.
199    ///                     $authority can transfer $liquidity_amount.
200    ///   1. `[writable]` Destination collateral token account.
201    ///                     Minted by withdraw reserve collateral mint.
202    ///   2. `[writable]` Repay reserve account - refreshed.
203    ///   3. `[writable]` Repay reserve liquidity supply SPL Token account.
204    ///   4. `[]` Withdraw reserve account - refreshed.
205    ///   5. `[writable]` Withdraw reserve collateral supply SPL Token account.
206    ///   6. `[writable]` Obligation account - refreshed.
207    ///   7. `[]` Lending market account.
208    ///   8. `[]` Derived lending market authority.
209    ///   9. `[signer]` User transfer authority ($authority).
210    ///   10 `[]` Clock sysvar.
211    ///   11 `[]` Token program id.
212    ///   14 `[writable, optional]` Deposit stake account.
213    ///   15 `[writable, optional]` Deposit staking pool.
214    ///   16 `[optional]` staking program id.
215    LiquidateObligation {
216        /// Amount of liquidity to repay - u64::MAX for up to 100% of borrowed amount
217        liquidity_amount: u64,
218    },
219
220    // 13
221    /// Make a flash loan.
222    ///
223    /// Accounts expected by this instruction:
224    ///
225    ///   0. `[writable]` Source liquidity token account.
226    ///                     Minted by reserve liquidity mint.
227    ///                     Must match the reserve liquidity supply.
228    ///   1. `[writable]` Destination liquidity token account.
229    ///                     Minted by reserve liquidity mint.
230    ///   2. `[writable]` Reserve account.
231    ///   3. `[]` Lending market account.
232    ///   4. `[]` Derived lending market authority.
233    ///   5. `[]` Flash loan receiver program account.
234    ///             Must implement an instruction that has tag of 0 and a signature of `(repay_amount: u64)`
235    ///             This instruction must return the amount to the source liquidity account.
236    ///   6. `[]` Token program id.
237    ///   7. `[writable]` Flash loan fee receiver account.
238    ///                     Must match the reserve liquidity fee receiver.
239    ///   8. `[writable]` Host fee receiver.
240    ///   .. `[any]` Additional accounts expected by the receiving program's `ReceiveFlashLoan` instruction.
241    ///
242    ///   The flash loan receiver program that is to be invoked should contain an instruction with
243    ///   tag `0` and accept the total amount (including fee) that needs to be returned back after
244    ///   its execution has completed.
245    ///
246    ///   Flash loan receiver should have an instruction with the following signature:
247    ///
248    ///   0. `[writable]` Source liquidity (matching the destination from above).
249    ///   1. `[writable]` Destination liquidity (matching the source from above).
250    ///   2. `[]` Token program id
251    ///   .. `[any]` Additional accounts provided to the lending program's `FlashLoan` instruction above.
252    ///   ReceiveFlashLoan {
253    ///       // Amount that is loaned to the receiver program
254    ///       amount: u64
255    ///   }
256    FlashLoan {
257        /// The amount that is to be borrowed - u64::MAX for up to 100% of available liquidity
258        amount: u64,
259    },
260
261    // 14
262    /// Combines DepositReserveLiquidity and DepositObligationCollateral
263    ///
264    /// Accounts expected by this instruction:
265    ///
266    ///   0. `[writable]` Source liquidity token account.
267    ///                     $authority can transfer $liquidity_amount.
268    ///   1. `[writable]` Destination collateral token account.
269    ///   2. `[writable]` Reserve account.
270    ///   3. `[writable]` Reserve liquidity supply SPL Token account.
271    ///   4. `[writable]` Reserve collateral SPL Token mint.
272    ///   5. `[]` Lending market account.
273    ///   6. `[]` Derived lending market authority.
274    ///   7. `[writable]` Destination deposit reserve collateral supply SPL Token account.
275    ///   8. `[writable]` Obligation account.
276    ///   9. `[signer]` Obligation owner.
277    ///   10 `[signer]` User transfer authority ($authority).
278    ///   11 `[]` Clock sysvar.
279    ///   12 `[]` Token program id.
280    ///   13 `[writable, optional]` Stake account.
281    ///   14 `[writable, optional]` Staking pool.
282    ///   15 `[optional]` staking program id.
283    DepositReserveLiquidityAndObligationCollateral {
284        /// Amount of liquidity to deposit in exchange
285        liquidity_amount: u64,
286    },
287}
288
289impl LendingInstruction {
290    /// Unpacks a byte buffer into a [LendingInstruction](enum.LendingInstruction.html).
291    pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
292        let (&tag, rest) = input
293            .split_first()
294            .ok_or(LendingError::InstructionUnpackError)?;
295        Ok(match tag {
296            3 => Self::RefreshReserve,
297            4 => {
298                let (liquidity_amount, _rest) = Self::unpack_u64(rest)?;
299                Self::DepositReserveLiquidity { liquidity_amount }
300            }
301            5 => {
302                let (collateral_amount, _rest) = Self::unpack_u64(rest)?;
303                Self::RedeemReserveCollateral { collateral_amount }
304            }
305            6 => Self::InitObligation,
306            7 => Self::RefreshObligation,
307            8 => {
308                let (collateral_amount, _rest) = Self::unpack_u64(rest)?;
309                Self::DepositObligationCollateral { collateral_amount }
310            }
311            9 => {
312                let (collateral_amount, _rest) = Self::unpack_u64(rest)?;
313                Self::WithdrawObligationCollateral { collateral_amount }
314            }
315            10 => {
316                let (liquidity_amount, _rest) = Self::unpack_u64(rest)?;
317                Self::BorrowObligationLiquidity { liquidity_amount }
318            }
319            11 => {
320                let (liquidity_amount, _rest) = Self::unpack_u64(rest)?;
321                Self::RepayObligationLiquidity { liquidity_amount }
322            }
323            12 => {
324                let (liquidity_amount, _rest) = Self::unpack_u64(rest)?;
325                Self::LiquidateObligation { liquidity_amount }
326            }
327            13 => {
328                let (amount, _rest) = Self::unpack_u64(rest)?;
329                Self::FlashLoan { amount }
330            }
331            14 => {
332                let (liquidity_amount, _rest) = Self::unpack_u64(rest)?;
333                Self::DepositReserveLiquidityAndObligationCollateral { liquidity_amount }
334            }
335
336            _ => {
337                msg!("Instruction cannot be unpacked");
338                return Err(LendingError::InstructionUnpackError.into());
339            }
340        })
341    }
342
343    fn unpack_u64(input: &[u8]) -> Result<(u64, &[u8]), ProgramError> {
344        if input.len() < 8 {
345            msg!("u64 cannot be unpacked");
346            return Err(LendingError::InstructionUnpackError.into());
347        }
348        let (bytes, rest) = input.split_at(8);
349        let value = bytes
350            .get(..8)
351            .and_then(|slice| slice.try_into().ok())
352            .map(u64::from_le_bytes)
353            .ok_or(LendingError::InstructionUnpackError)?;
354        Ok((value, rest))
355    }
356
357    /// Packs a [LendingInstruction](enum.LendingInstruction.html) into a byte buffer.
358    pub fn pack(&self) -> Vec<u8> {
359        let mut buf = Vec::with_capacity(size_of::<Self>());
360        match *self {
361            Self::RefreshReserve => {
362                buf.push(3);
363            }
364            Self::DepositReserveLiquidity { liquidity_amount } => {
365                buf.push(4);
366                buf.extend_from_slice(&liquidity_amount.to_le_bytes());
367            }
368            Self::RedeemReserveCollateral { collateral_amount } => {
369                buf.push(5);
370                buf.extend_from_slice(&collateral_amount.to_le_bytes());
371            }
372            Self::InitObligation => {
373                buf.push(6);
374            }
375            Self::RefreshObligation => {
376                buf.push(7);
377            }
378            Self::DepositObligationCollateral { collateral_amount } => {
379                buf.push(8);
380                buf.extend_from_slice(&collateral_amount.to_le_bytes());
381            }
382            Self::WithdrawObligationCollateral { collateral_amount } => {
383                buf.push(9);
384                buf.extend_from_slice(&collateral_amount.to_le_bytes());
385            }
386            Self::BorrowObligationLiquidity { liquidity_amount } => {
387                buf.push(10);
388                buf.extend_from_slice(&liquidity_amount.to_le_bytes());
389            }
390            Self::RepayObligationLiquidity { liquidity_amount } => {
391                buf.push(11);
392                buf.extend_from_slice(&liquidity_amount.to_le_bytes());
393            }
394            Self::LiquidateObligation { liquidity_amount } => {
395                buf.push(12);
396                buf.extend_from_slice(&liquidity_amount.to_le_bytes());
397            }
398            Self::FlashLoan { amount } => {
399                buf.push(13);
400                buf.extend_from_slice(&amount.to_le_bytes());
401            }
402            Self::DepositReserveLiquidityAndObligationCollateral { liquidity_amount } => {
403                buf.push(14);
404                buf.extend_from_slice(&liquidity_amount.to_le_bytes());
405            }
406        }
407        buf
408    }
409}
410
411/// Creates a `RefreshReserve` instruction
412pub fn refresh_reserve(
413    program_id: Pubkey,
414    reserve_pubkey: Pubkey,
415    reserve_liquidity_oracle_pubkey: COption<Pubkey>,
416) -> Instruction {
417    let mut accounts = vec![
418        AccountMeta::new(reserve_pubkey, false),
419        AccountMeta::new_readonly(sysvar::clock::id(), false),
420    ];
421    if let COption::Some(reserve_liquidity_oracle_pubkey) = reserve_liquidity_oracle_pubkey {
422        accounts.push(AccountMeta::new_readonly(
423            reserve_liquidity_oracle_pubkey,
424            false,
425        ));
426    }
427    Instruction {
428        program_id,
429        accounts,
430        data: LendingInstruction::RefreshReserve.pack(),
431    }
432}
433
434/// Creates a 'DepositReserveLiquidity' instruction.
435#[allow(clippy::too_many_arguments)]
436pub fn deposit_reserve_liquidity(
437    program_id: Pubkey,
438    liquidity_amount: u64,
439    source_liquidity_pubkey: Pubkey,
440    destination_collateral_pubkey: Pubkey,
441    reserve_pubkey: Pubkey,
442    reserve_liquidity_supply_pubkey: Pubkey,
443    reserve_collateral_mint_pubkey: Pubkey,
444    lending_market_pubkey: Pubkey,
445    user_transfer_authority_pubkey: Pubkey,
446) -> Instruction {
447    let (lending_market_authority_pubkey, _bump_seed) = Pubkey::find_program_address(
448        &[&lending_market_pubkey.to_bytes()[..PUBKEY_BYTES]],
449        &program_id,
450    );
451    Instruction {
452        program_id,
453        accounts: vec![
454            AccountMeta::new(source_liquidity_pubkey, false),
455            AccountMeta::new(destination_collateral_pubkey, false),
456            AccountMeta::new(reserve_pubkey, false),
457            AccountMeta::new(reserve_liquidity_supply_pubkey, false),
458            AccountMeta::new(reserve_collateral_mint_pubkey, false),
459            AccountMeta::new_readonly(lending_market_pubkey, false),
460            AccountMeta::new_readonly(lending_market_authority_pubkey, false),
461            AccountMeta::new_readonly(user_transfer_authority_pubkey, true),
462            AccountMeta::new_readonly(sysvar::clock::id(), false),
463            AccountMeta::new_readonly(spl_token::id(), false),
464        ],
465        data: LendingInstruction::DepositReserveLiquidity { liquidity_amount }.pack(),
466    }
467}
468
469/// Creates a 'RedeemReserveCollateral' instruction.
470#[allow(clippy::too_many_arguments)]
471pub fn redeem_reserve_collateral(
472    program_id: Pubkey,
473    collateral_amount: u64,
474    source_collateral_pubkey: Pubkey,
475    destination_liquidity_pubkey: Pubkey,
476    reserve_pubkey: Pubkey,
477    reserve_collateral_mint_pubkey: Pubkey,
478    reserve_liquidity_supply_pubkey: Pubkey,
479    lending_market_pubkey: Pubkey,
480    user_transfer_authority_pubkey: Pubkey,
481) -> Instruction {
482    let (lending_market_authority_pubkey, _bump_seed) = Pubkey::find_program_address(
483        &[&lending_market_pubkey.to_bytes()[..PUBKEY_BYTES]],
484        &program_id,
485    );
486    Instruction {
487        program_id,
488        accounts: vec![
489            AccountMeta::new(source_collateral_pubkey, false),
490            AccountMeta::new(destination_liquidity_pubkey, false),
491            AccountMeta::new(reserve_pubkey, false),
492            AccountMeta::new(reserve_collateral_mint_pubkey, false),
493            AccountMeta::new(reserve_liquidity_supply_pubkey, false),
494            AccountMeta::new_readonly(lending_market_pubkey, false),
495            AccountMeta::new_readonly(lending_market_authority_pubkey, false),
496            AccountMeta::new_readonly(user_transfer_authority_pubkey, true),
497            AccountMeta::new_readonly(sysvar::clock::id(), false),
498            AccountMeta::new_readonly(spl_token::id(), false),
499        ],
500        data: LendingInstruction::RedeemReserveCollateral { collateral_amount }.pack(),
501    }
502}
503
504/// Creates an `InitObligation` instruction.
505#[allow(clippy::too_many_arguments)]
506pub fn init_obligation(
507    program_id: Pubkey,
508    obligation_pubkey: Pubkey,
509    lending_market_pubkey: Pubkey,
510    obligation_owner_pubkey: Pubkey,
511) -> Instruction {
512    Instruction {
513        program_id,
514        accounts: vec![
515            AccountMeta::new(obligation_pubkey, false),
516            AccountMeta::new_readonly(lending_market_pubkey, false),
517            AccountMeta::new_readonly(obligation_owner_pubkey, true),
518            AccountMeta::new_readonly(sysvar::clock::id(), false),
519            AccountMeta::new_readonly(sysvar::rent::id(), false),
520            AccountMeta::new_readonly(spl_token::id(), false),
521        ],
522        data: LendingInstruction::InitObligation.pack(),
523    }
524}
525
526/// Creates a `RefreshObligation` instruction.
527#[allow(clippy::too_many_arguments)]
528pub fn refresh_obligation(
529    program_id: Pubkey,
530    obligation_pubkey: Pubkey,
531    reserve_pubkeys: Vec<Pubkey>,
532) -> Instruction {
533    let mut accounts = vec![
534        AccountMeta::new(obligation_pubkey, false),
535        AccountMeta::new_readonly(sysvar::clock::id(), false),
536    ];
537    accounts.extend(
538        reserve_pubkeys
539            .into_iter()
540            .map(|pubkey| AccountMeta::new_readonly(pubkey, false)),
541    );
542    Instruction {
543        program_id,
544        accounts,
545        data: LendingInstruction::RefreshObligation.pack(),
546    }
547}
548
549/// Creates a `DepositObligationCollateral` instruction.
550#[allow(clippy::too_many_arguments)]
551pub fn deposit_obligation_collateral(
552    program_id: Pubkey,
553    collateral_amount: u64,
554    source_collateral_pubkey: Pubkey,
555    destination_collateral_pubkey: Pubkey,
556    deposit_reserve_pubkey: Pubkey,
557    obligation_pubkey: Pubkey,
558    lending_market_pubkey: Pubkey,
559    obligation_owner_pubkey: Pubkey,
560    user_transfer_authority_pubkey: Pubkey,
561    option_stake_account_pubkey: Option<Pubkey>,
562    option_staking_pool_pubkey: Option<Pubkey>,
563) -> Instruction {
564    let (lending_market_authority_pubkey, _bump_seed) = Pubkey::find_program_address(
565        &[&lending_market_pubkey.to_bytes()[..PUBKEY_BYTES]],
566        &program_id,
567    );
568    let mut accounts = vec![
569        AccountMeta::new(source_collateral_pubkey, false),
570        AccountMeta::new(destination_collateral_pubkey, false),
571        AccountMeta::new_readonly(deposit_reserve_pubkey, false),
572        AccountMeta::new(obligation_pubkey, false),
573        AccountMeta::new_readonly(lending_market_pubkey, false),
574        AccountMeta::new_readonly(lending_market_authority_pubkey, false),
575        AccountMeta::new_readonly(obligation_owner_pubkey, true),
576        AccountMeta::new_readonly(user_transfer_authority_pubkey, true),
577        AccountMeta::new_readonly(sysvar::clock::id(), false),
578        AccountMeta::new_readonly(spl_token::id(), false),
579    ];
580
581    if let [Some(staking_account_pubkey), Some(staking_pool_pubkey)] =
582        [option_stake_account_pubkey, option_staking_pool_pubkey]
583    {
584        accounts.push(AccountMeta::new(staking_account_pubkey, false));
585        accounts.push(AccountMeta::new(staking_pool_pubkey, false));
586        accounts.push(AccountMeta::new_readonly(port_finance_staking::id(), false));
587    }
588    Instruction {
589        program_id,
590        accounts,
591        data: LendingInstruction::DepositObligationCollateral { collateral_amount }.pack(),
592    }
593}
594
595/// Creates a `WithdrawObligationCollateral` instruction.
596#[allow(clippy::too_many_arguments)]
597pub fn withdraw_obligation_collateral(
598    program_id: Pubkey,
599    collateral_amount: u64,
600    source_collateral_pubkey: Pubkey,
601    destination_collateral_pubkey: Pubkey,
602    withdraw_reserve_pubkey: Pubkey,
603    obligation_pubkey: Pubkey,
604    lending_market_pubkey: Pubkey,
605    obligation_owner_pubkey: Pubkey,
606    option_stake_account_pubkey: Option<Pubkey>,
607    option_staking_pool_pubkey: Option<Pubkey>,
608) -> Instruction {
609    let (lending_market_authority_pubkey, _bump_seed) = Pubkey::find_program_address(
610        &[&lending_market_pubkey.to_bytes()[..PUBKEY_BYTES]],
611        &program_id,
612    );
613    let mut accounts = vec![
614        AccountMeta::new(source_collateral_pubkey, false),
615        AccountMeta::new(destination_collateral_pubkey, false),
616        AccountMeta::new_readonly(withdraw_reserve_pubkey, false),
617        AccountMeta::new(obligation_pubkey, false),
618        AccountMeta::new_readonly(lending_market_pubkey, false),
619        AccountMeta::new_readonly(lending_market_authority_pubkey, false),
620        AccountMeta::new_readonly(obligation_owner_pubkey, true),
621        AccountMeta::new_readonly(sysvar::clock::id(), false),
622        AccountMeta::new_readonly(spl_token::id(), false),
623    ];
624
625    if let [Some(staking_account_pubkey), Some(staking_pool_pubkey)] =
626        [option_stake_account_pubkey, option_staking_pool_pubkey]
627    {
628        accounts.push(AccountMeta::new(staking_account_pubkey, false));
629        accounts.push(AccountMeta::new(staking_pool_pubkey, false));
630        accounts.push(AccountMeta::new_readonly(port_finance_staking::id(), false));
631    }
632    Instruction {
633        program_id,
634        accounts,
635        data: LendingInstruction::WithdrawObligationCollateral { collateral_amount }.pack(),
636    }
637}
638
639/// Creates a `BorrowObligationLiquidity` instruction.
640#[allow(clippy::too_many_arguments)]
641pub fn borrow_obligation_liquidity(
642    program_id: Pubkey,
643    liquidity_amount: u64,
644    source_liquidity_pubkey: Pubkey,
645    destination_liquidity_pubkey: Pubkey,
646    borrow_reserve_pubkey: Pubkey,
647    borrow_reserve_liquidity_fee_receiver_pubkey: Pubkey,
648    obligation_pubkey: Pubkey,
649    lending_market_pubkey: Pubkey,
650    obligation_owner_pubkey: Pubkey,
651) -> Instruction {
652    let (lending_market_authority_pubkey, _bump_seed) = Pubkey::find_program_address(
653        &[&lending_market_pubkey.to_bytes()[..PUBKEY_BYTES]],
654        &program_id,
655    );
656    let accounts = vec![
657        AccountMeta::new(source_liquidity_pubkey, false),
658        AccountMeta::new(destination_liquidity_pubkey, false),
659        AccountMeta::new(borrow_reserve_pubkey, false),
660        AccountMeta::new(borrow_reserve_liquidity_fee_receiver_pubkey, false),
661        AccountMeta::new(obligation_pubkey, false),
662        AccountMeta::new_readonly(lending_market_pubkey, false),
663        AccountMeta::new_readonly(lending_market_authority_pubkey, false),
664        AccountMeta::new_readonly(obligation_owner_pubkey, true),
665        AccountMeta::new_readonly(sysvar::clock::id(), false),
666        AccountMeta::new_readonly(spl_token::id(), false),
667    ];
668
669    Instruction {
670        program_id,
671        accounts,
672        data: LendingInstruction::BorrowObligationLiquidity { liquidity_amount }.pack(),
673    }
674}
675
676/// Creates a `RepayObligationLiquidity` instruction
677#[allow(clippy::too_many_arguments)]
678pub fn repay_obligation_liquidity(
679    program_id: Pubkey,
680    liquidity_amount: u64,
681    source_liquidity_pubkey: Pubkey,
682    destination_liquidity_pubkey: Pubkey,
683    repay_reserve_pubkey: Pubkey,
684    obligation_pubkey: Pubkey,
685    lending_market_pubkey: Pubkey,
686    user_transfer_authority_pubkey: Pubkey,
687) -> Instruction {
688    let accounts = vec![
689        AccountMeta::new(source_liquidity_pubkey, false),
690        AccountMeta::new(destination_liquidity_pubkey, false),
691        AccountMeta::new(repay_reserve_pubkey, false),
692        AccountMeta::new(obligation_pubkey, false),
693        AccountMeta::new_readonly(lending_market_pubkey, false),
694        AccountMeta::new_readonly(user_transfer_authority_pubkey, true),
695        AccountMeta::new_readonly(sysvar::clock::id(), false),
696        AccountMeta::new_readonly(spl_token::id(), false),
697    ];
698    Instruction {
699        program_id,
700        accounts,
701        data: LendingInstruction::RepayObligationLiquidity { liquidity_amount }.pack(),
702    }
703}
704
705/// Creates a `LiquidateObligation` instruction
706#[allow(clippy::too_many_arguments)]
707pub fn liquidate_obligation(
708    program_id: Pubkey,
709    liquidity_amount: u64,
710    source_liquidity_pubkey: Pubkey,
711    destination_collateral_pubkey: Pubkey,
712    repay_reserve_pubkey: Pubkey,
713    repay_reserve_liquidity_supply_pubkey: Pubkey,
714    withdraw_reserve_pubkey: Pubkey,
715    withdraw_reserve_collateral_supply_pubkey: Pubkey,
716    obligation_pubkey: Pubkey,
717    lending_market_pubkey: Pubkey,
718    user_transfer_authority_pubkey: Pubkey,
719    option_borrow_stake_account_pubkey: Option<Pubkey>,
720    option_borrow_staking_pool_pubkey: Option<Pubkey>,
721) -> Instruction {
722    let (lending_market_authority_pubkey, _bump_seed) = Pubkey::find_program_address(
723        &[&lending_market_pubkey.to_bytes()[..PUBKEY_BYTES]],
724        &program_id,
725    );
726    let mut accounts = vec![
727        AccountMeta::new(source_liquidity_pubkey, false),
728        AccountMeta::new(destination_collateral_pubkey, false),
729        AccountMeta::new(repay_reserve_pubkey, false),
730        AccountMeta::new(repay_reserve_liquidity_supply_pubkey, false),
731        AccountMeta::new_readonly(withdraw_reserve_pubkey, false),
732        AccountMeta::new(withdraw_reserve_collateral_supply_pubkey, false),
733        AccountMeta::new(obligation_pubkey, false),
734        AccountMeta::new_readonly(lending_market_pubkey, false),
735        AccountMeta::new_readonly(lending_market_authority_pubkey, false),
736        AccountMeta::new_readonly(user_transfer_authority_pubkey, true),
737        AccountMeta::new_readonly(sysvar::clock::id(), false),
738        AccountMeta::new_readonly(spl_token::id(), false),
739    ];
740    if let [Some(staking_account_pubkey), Some(staking_pool_pubkey)] = [
741        option_borrow_stake_account_pubkey,
742        option_borrow_staking_pool_pubkey,
743    ] {
744        accounts.push(AccountMeta::new(staking_account_pubkey, false));
745        accounts.push(AccountMeta::new(staking_pool_pubkey, false));
746        accounts.push(AccountMeta::new_readonly(port_finance_staking::id(), false));
747    }
748    Instruction {
749        program_id,
750        accounts,
751        data: LendingInstruction::LiquidateObligation { liquidity_amount }.pack(),
752    }
753}
754
755/// Creates a `FlashLoan` instruction.
756#[allow(clippy::too_many_arguments)]
757pub fn flash_loan(
758    program_id: Pubkey,
759    amount: u64,
760    source_liquidity_pubkey: Pubkey,
761    destination_liquidity_pubkey: Pubkey,
762    reserve_pubkey: Pubkey,
763    reserve_liquidity_fee_receiver_pubkey: Pubkey,
764    host_fee_receiver_pubkey: Pubkey,
765    lending_market_pubkey: Pubkey,
766    flash_loan_receiver_program_id: Pubkey,
767    flash_loan_receiver_program_accounts: Vec<AccountMeta>,
768) -> Instruction {
769    let (lending_market_authority_pubkey, _bump_seed) = Pubkey::find_program_address(
770        &[&lending_market_pubkey.to_bytes()[..PUBKEY_BYTES]],
771        &program_id,
772    );
773    let mut accounts = vec![
774        AccountMeta::new(source_liquidity_pubkey, false),
775        AccountMeta::new(destination_liquidity_pubkey, false),
776        AccountMeta::new(reserve_pubkey, false),
777        AccountMeta::new(reserve_liquidity_fee_receiver_pubkey, false),
778        AccountMeta::new(host_fee_receiver_pubkey, false),
779        AccountMeta::new_readonly(lending_market_pubkey, false),
780        AccountMeta::new_readonly(lending_market_authority_pubkey, false),
781        AccountMeta::new_readonly(spl_token::id(), false),
782        AccountMeta::new_readonly(flash_loan_receiver_program_id, false),
783    ];
784    accounts.extend(flash_loan_receiver_program_accounts);
785    Instruction {
786        program_id,
787        accounts,
788        data: LendingInstruction::FlashLoan { amount }.pack(),
789    }
790}
791
792/// Creates a `DepositReserveLiquidityAndObligationCollateral` instruction.
793#[allow(clippy::too_many_arguments)]
794pub fn deposit_reserve_liquidity_and_obligation_collateral(
795    program_id: Pubkey,
796    liquidity_amount: u64,
797    source_liquidity_pubkey: Pubkey,
798    user_collateral_pubkey: Pubkey,
799    reserve_pubkey: Pubkey,
800    reserve_liquidity_supply_pubkey: Pubkey,
801    reserve_collateral_mint_pubkey: Pubkey,
802    lending_market_pubkey: Pubkey,
803    destination_deposit_collateral_pubkey: Pubkey,
804    obligation_pubkey: Pubkey,
805    obligation_owner_pubkey: Pubkey,
806    user_transfer_authority_pubkey: Pubkey,
807    option_stake_account_pubkey: Option<Pubkey>,
808    option_staking_pool_pubkey: Option<Pubkey>,
809) -> Instruction {
810    let (lending_market_authority_pubkey, _bump_seed) = Pubkey::find_program_address(
811        &[&lending_market_pubkey.to_bytes()[..PUBKEY_BYTES]],
812        &program_id,
813    );
814    let mut accounts = vec![
815        AccountMeta::new(source_liquidity_pubkey, false),
816        AccountMeta::new(user_collateral_pubkey, false),
817        AccountMeta::new(reserve_pubkey, false),
818        AccountMeta::new(reserve_liquidity_supply_pubkey, false),
819        AccountMeta::new(reserve_collateral_mint_pubkey, false),
820        AccountMeta::new_readonly(lending_market_pubkey, false),
821        AccountMeta::new_readonly(lending_market_authority_pubkey, false),
822        AccountMeta::new(destination_deposit_collateral_pubkey, false),
823        AccountMeta::new(obligation_pubkey, false),
824        AccountMeta::new(obligation_owner_pubkey, true),
825        AccountMeta::new_readonly(user_transfer_authority_pubkey, true),
826        AccountMeta::new_readonly(sysvar::clock::id(), false),
827        AccountMeta::new_readonly(spl_token::id(), false),
828    ];
829    if let [Some(staking_account_pubkey), Some(staking_pool_pubkey)] =
830        [option_stake_account_pubkey, option_staking_pool_pubkey]
831    {
832        accounts.push(AccountMeta::new(staking_account_pubkey, false));
833        accounts.push(AccountMeta::new(staking_pool_pubkey, false));
834        accounts.push(AccountMeta::new_readonly(port_finance_staking::id(), false));
835    }
836    Instruction {
837        program_id,
838        accounts,
839        data: LendingInstruction::DepositReserveLiquidityAndObligationCollateral {
840            liquidity_amount,
841        }
842        .pack(),
843    }
844}