2022-06-08 04:43:12 -07:00
|
|
|
use crate::accounts_zerocopy::*;
|
2022-03-02 21:15:28 -08:00
|
|
|
use crate::error::MangoError;
|
2022-03-29 00:12:10 -07:00
|
|
|
use crate::state::{compute_health_from_fixed_accounts, Bank, Group, HealthType, MangoAccount};
|
2022-05-20 01:55:48 -07:00
|
|
|
use crate::{group_seeds, Mango};
|
2022-03-02 21:15:28 -08:00
|
|
|
use anchor_lang::prelude::*;
|
2022-05-20 01:55:48 -07:00
|
|
|
use anchor_spl::token::{self, Token, TokenAccount};
|
2022-03-26 11:34:44 -07:00
|
|
|
use fixed::types::I80F48;
|
2022-03-02 21:15:28 -08:00
|
|
|
use solana_program::instruction::Instruction;
|
2022-05-19 04:45:46 -07:00
|
|
|
use std::cell::Ref;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2022-06-23 01:19:33 -07:00
|
|
|
/// The flash loan instruction
|
2022-05-20 01:55:48 -07:00
|
|
|
///
|
|
|
|
/// In addition to these accounts, there must be a sequence of remaining_accounts:
|
|
|
|
/// 1. health_accounts: accounts needed for health checking
|
2022-06-18 07:43:45 -07:00
|
|
|
/// 2. per cpi
|
|
|
|
/// 2.a. target_program_id: the target program account
|
|
|
|
/// 2.b. target_accounts: the accounts to pass to the target program
|
2022-05-20 01:55:48 -07:00
|
|
|
///
|
|
|
|
/// Every vault address listed in 3. must also have the matching bank and oracle appear in 1.
|
|
|
|
///
|
|
|
|
/// Every vault that is to be withdrawn from must appear in the `withdraws` instruction argument.
|
|
|
|
/// The corresponding bank may be used as an authority for vault withdrawals.
|
2022-03-02 21:15:28 -08:00
|
|
|
#[derive(Accounts)]
|
2022-06-23 01:19:33 -07:00
|
|
|
pub struct FlashLoan<'info> {
|
2022-03-07 07:16:34 -08:00
|
|
|
pub group: AccountLoader<'info, Group>,
|
2022-03-02 21:15:28 -08:00
|
|
|
|
|
|
|
#[account(
|
|
|
|
mut,
|
|
|
|
has_one = group,
|
|
|
|
has_one = owner,
|
|
|
|
)]
|
|
|
|
pub account: AccountLoader<'info, MangoAccount>,
|
|
|
|
|
|
|
|
pub owner: Signer<'info>,
|
2022-05-20 01:55:48 -07:00
|
|
|
pub token_program: Program<'info, Token>,
|
2022-03-02 21:15:28 -08:00
|
|
|
}
|
|
|
|
|
2022-06-23 07:41:24 -07:00
|
|
|
#[derive(Debug)]
|
2022-05-19 04:45:46 -07:00
|
|
|
struct AllowedVault {
|
2022-05-24 23:02:55 -07:00
|
|
|
/// index of the vault in cpi_ais
|
2022-05-19 04:45:46 -07:00
|
|
|
vault_cpi_ai_index: usize,
|
2022-05-24 23:02:55 -07:00
|
|
|
/// index of the bank in health_ais
|
2022-05-19 04:45:46 -07:00
|
|
|
bank_health_ai_index: usize,
|
2022-05-24 23:02:55 -07:00
|
|
|
/// raw index into account.tokens
|
2022-05-24 04:00:32 -07:00
|
|
|
raw_token_index: usize,
|
2022-05-24 23:02:55 -07:00
|
|
|
/// vault amount before cpi
|
2022-05-19 04:45:46 -07:00
|
|
|
pre_amount: u64,
|
2022-05-24 23:02:55 -07:00
|
|
|
/// requested withdraw amount
|
2022-05-20 01:55:48 -07:00
|
|
|
withdraw_amount: u64,
|
2022-05-24 23:02:55 -07:00
|
|
|
/// amount of withdraw request that is a loan
|
2022-05-20 01:55:48 -07:00
|
|
|
loan_amount: I80F48,
|
2022-05-19 04:45:46 -07:00
|
|
|
}
|
|
|
|
|
2022-06-23 07:41:24 -07:00
|
|
|
#[derive(AnchorDeserialize, AnchorSerialize, Clone, Copy, Debug)]
|
2022-06-23 01:19:33 -07:00
|
|
|
pub struct FlashLoanWithdraw {
|
2022-05-24 23:02:55 -07:00
|
|
|
/// Account index of the vault to withdraw from in the target_accounts section.
|
2022-06-23 04:10:15 -07:00
|
|
|
/// Index is counted after health accounts.
|
2022-05-24 13:03:35 -07:00
|
|
|
pub index: u8,
|
2022-05-24 23:02:55 -07:00
|
|
|
/// Requested withdraw amount.
|
2022-05-24 13:03:35 -07:00
|
|
|
pub amount: u64,
|
|
|
|
}
|
|
|
|
|
2022-06-23 07:41:24 -07:00
|
|
|
#[derive(AnchorDeserialize, AnchorSerialize, Debug)]
|
2022-06-18 08:14:20 -07:00
|
|
|
pub struct CpiData {
|
|
|
|
pub account_start: u8,
|
|
|
|
pub data: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
2022-06-23 01:19:33 -07:00
|
|
|
/// - `withdraws` is a list of FlashLoanWithdraw requests.
|
2022-06-18 07:43:45 -07:00
|
|
|
/// - `cpi_datas` is a list of bytes per cpi to call the target_program_id with.
|
|
|
|
/// - `cpi_account_starts` is a list of index into the remaining accounts per cpi to call the target_program_id with.
|
2022-06-23 01:19:33 -07:00
|
|
|
pub fn flash_loan<'key, 'accounts, 'remaining, 'info>(
|
|
|
|
ctx: Context<'key, 'accounts, 'remaining, 'info, FlashLoan<'info>>,
|
|
|
|
withdraws: Vec<FlashLoanWithdraw>,
|
2022-06-18 08:14:20 -07:00
|
|
|
cpi_datas: Vec<CpiData>,
|
2022-03-04 05:30:53 -08:00
|
|
|
) -> Result<()> {
|
2022-06-18 07:43:45 -07:00
|
|
|
require!(!cpi_datas.is_empty(), MangoError::SomeError);
|
|
|
|
let num_of_cpis = cpi_datas.len();
|
2022-06-18 08:14:20 -07:00
|
|
|
let num_health_accounts = cpi_datas.get(0).unwrap().account_start as usize;
|
2022-06-18 07:43:45 -07:00
|
|
|
|
2022-03-02 21:15:28 -08:00
|
|
|
let group = ctx.accounts.group.load()?;
|
|
|
|
let mut account = ctx.accounts.account.load_mut()?;
|
2022-03-31 05:01:08 -07:00
|
|
|
require!(account.is_bankrupt == 0, MangoError::IsBankrupt);
|
2022-03-04 05:30:53 -08:00
|
|
|
|
2022-05-19 04:45:46 -07:00
|
|
|
// Go over the banks passed as health accounts and:
|
|
|
|
// - Ensure that all banks that are passed in have activated positions.
|
|
|
|
// This is necessary because maybe the user wants to margin trade on a token
|
|
|
|
// that the account hasn't used before.
|
|
|
|
// - Collect the addresses of all banks to potentially sign for in cpi_ais.
|
2022-05-20 01:55:48 -07:00
|
|
|
// - Collect the addresses of all bank vaults.
|
2022-05-19 04:45:46 -07:00
|
|
|
// Note: This depends on the particular health account ordering.
|
|
|
|
let health_ais = &ctx.remaining_accounts[0..num_health_accounts];
|
2022-05-20 01:55:48 -07:00
|
|
|
let mut allowed_banks = HashMap::<&Pubkey, Ref<Bank>>::new();
|
2022-05-24 04:00:32 -07:00
|
|
|
// vault pubkey -> (bank_account_index, raw_token_index)
|
|
|
|
let mut allowed_vaults = HashMap::<Pubkey, (usize, usize)>::new();
|
2022-05-19 04:45:46 -07:00
|
|
|
for (i, ai) in health_ais.iter().enumerate() {
|
|
|
|
match ai.load::<Bank>() {
|
|
|
|
Ok(bank) => {
|
|
|
|
require!(bank.group == account.group, MangoError::SomeError);
|
2022-05-24 04:00:32 -07:00
|
|
|
let (_, raw_token_index) = account.tokens.get_mut_or_create(bank.token_index)?;
|
|
|
|
allowed_vaults.insert(bank.vault, (i, raw_token_index));
|
2022-05-20 01:55:48 -07:00
|
|
|
allowed_banks.insert(ai.key, bank);
|
2022-05-19 04:45:46 -07:00
|
|
|
}
|
|
|
|
Err(Error::AnchorError(error))
|
2022-05-26 11:38:03 -07:00
|
|
|
if error.error_code_number == ErrorCode::AccountDiscriminatorMismatch as u32
|
|
|
|
|| error.error_code_number == ErrorCode::AccountOwnedByWrongProgram as u32 =>
|
2022-05-19 04:45:46 -07:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Err(error) => return Err(error),
|
|
|
|
};
|
|
|
|
}
|
2022-03-04 05:30:53 -08:00
|
|
|
|
2022-05-20 01:55:48 -07:00
|
|
|
// Check pre-cpi health
|
2022-05-24 04:00:32 -07:00
|
|
|
// NOTE: This health check isn't strictly necessary. It will be, later, when
|
|
|
|
// we want to have reduce_only or be able to move an account out of bankruptcy.
|
2022-05-20 01:55:48 -07:00
|
|
|
let pre_cpi_health =
|
2022-05-24 04:00:32 -07:00
|
|
|
compute_health_from_fixed_accounts(&account, HealthType::Init, health_ais)?;
|
2022-05-20 01:55:48 -07:00
|
|
|
require!(pre_cpi_health >= 0, MangoError::HealthMustBePositive);
|
|
|
|
msg!("pre_cpi_health {:?}", pre_cpi_health);
|
|
|
|
|
2022-06-23 04:10:15 -07:00
|
|
|
let all_cpi_ais = &ctx.remaining_accounts[num_health_accounts..];
|
2022-06-18 07:43:45 -07:00
|
|
|
let mut all_cpi_ams = all_cpi_ais
|
2022-05-19 04:45:46 -07:00
|
|
|
.iter()
|
|
|
|
.flat_map(|item| item.to_account_metas(None))
|
|
|
|
.collect::<Vec<_>>();
|
2022-06-18 07:43:45 -07:00
|
|
|
require!(
|
|
|
|
all_cpi_ais.len() == all_cpi_ams.len(),
|
|
|
|
MangoError::SomeError
|
|
|
|
);
|
2022-05-19 04:45:46 -07:00
|
|
|
|
2022-05-20 01:55:48 -07:00
|
|
|
// Check that each group-owned token account is the vault of one of the allowed banks,
|
|
|
|
// and track its balance.
|
2022-06-18 07:43:45 -07:00
|
|
|
let mut used_vaults = all_cpi_ais
|
2022-05-20 01:55:48 -07:00
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.filter_map(|(i, ai)| {
|
|
|
|
if ai.owner != &TokenAccount::owner() {
|
|
|
|
return None;
|
|
|
|
}
|
2022-05-26 12:12:02 -07:00
|
|
|
|
|
|
|
// Skip mints and other accounts that may be owned by the spl_token program
|
|
|
|
let maybe_token_account = Account::<TokenAccount>::try_from(ai);
|
|
|
|
if maybe_token_account.is_err() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let token_account = maybe_token_account.unwrap();
|
2022-05-20 01:55:48 -07:00
|
|
|
if token_account.owner != ctx.accounts.group.key() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Every group-owned token account must be a vault of one of the banks.
|
2022-05-24 04:00:32 -07:00
|
|
|
if let Some(&(bank_index, raw_token_index)) = allowed_vaults.get(&ai.key) {
|
2022-05-20 01:55:48 -07:00
|
|
|
return Some(Ok((
|
|
|
|
ai.key,
|
|
|
|
AllowedVault {
|
2022-05-19 04:45:46 -07:00
|
|
|
vault_cpi_ai_index: i,
|
|
|
|
bank_health_ai_index: bank_index,
|
2022-05-24 04:00:32 -07:00
|
|
|
raw_token_index,
|
2022-05-19 04:45:46 -07:00
|
|
|
pre_amount: token_account.amount,
|
2022-05-20 01:55:48 -07:00
|
|
|
// these two are updated later
|
|
|
|
withdraw_amount: 0,
|
|
|
|
loan_amount: I80F48::ZERO,
|
|
|
|
},
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is to protect users, because if their cpi program sends deposits to a vault
|
|
|
|
// and they forgot to pass in the bank for the vault, their account would not be credited.
|
|
|
|
Some(Err(error!(MangoError::SomeError)))
|
|
|
|
})
|
|
|
|
.collect::<Result<HashMap<_, _>>>()?;
|
|
|
|
|
|
|
|
// Find banks for used vaults in cpi_ais and collect signer seeds for them.
|
|
|
|
// Also update withdraw_amount and loan_amount.
|
|
|
|
let mut bank_signer_data = Vec::with_capacity(used_vaults.len());
|
2022-06-18 07:43:45 -07:00
|
|
|
for (ai, am) in all_cpi_ais.iter().zip(all_cpi_ams.iter_mut()) {
|
2022-05-20 01:55:48 -07:00
|
|
|
if ai.owner != &Mango::id() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if let Some(bank) = allowed_banks.get(ai.key) {
|
|
|
|
if let Some(vault_info) = used_vaults.get_mut(&bank.vault) {
|
|
|
|
let withdraw_amount = withdraws
|
|
|
|
.iter()
|
2022-05-24 13:03:35 -07:00
|
|
|
.find_map(|&withdraw| {
|
|
|
|
(withdraw.index as usize == vault_info.vault_cpi_ai_index)
|
|
|
|
.then(|| withdraw.amount)
|
2022-05-20 01:55:48 -07:00
|
|
|
})
|
2022-05-24 04:00:32 -07:00
|
|
|
// Even if we don't withdraw from a vault we still need to track it:
|
|
|
|
// Possibly the invoked program will deposit funds into it.
|
2022-05-20 01:55:48 -07:00
|
|
|
.unwrap_or(0);
|
|
|
|
require!(
|
|
|
|
withdraw_amount <= vault_info.pre_amount,
|
|
|
|
MangoError::SomeError
|
|
|
|
);
|
|
|
|
vault_info.withdraw_amount = withdraw_amount;
|
|
|
|
|
|
|
|
// if there are withdraws: figure out loan amount, mark as signer
|
|
|
|
if withdraw_amount > 0 {
|
2022-05-24 04:00:32 -07:00
|
|
|
let token_account = account.tokens.get_mut_raw(vault_info.raw_token_index);
|
2022-05-20 01:55:48 -07:00
|
|
|
let native_position = token_account.native(&bank);
|
|
|
|
vault_info.loan_amount = if native_position > 0 {
|
|
|
|
(I80F48::from(withdraw_amount) - native_position).max(I80F48::ZERO)
|
|
|
|
} else {
|
|
|
|
I80F48::from(withdraw_amount)
|
|
|
|
};
|
|
|
|
|
|
|
|
am.is_signer = true;
|
|
|
|
// this is the data we'll need later to build the PDA account signer seeds
|
2022-06-27 02:27:17 -07:00
|
|
|
bank_signer_data.push((
|
|
|
|
bank.token_index.to_le_bytes(),
|
|
|
|
bank.bank_num.to_le_bytes(),
|
|
|
|
[bank.bump],
|
|
|
|
));
|
2022-05-19 04:45:46 -07:00
|
|
|
}
|
2022-03-08 01:23:58 -08:00
|
|
|
}
|
|
|
|
}
|
2022-03-02 21:15:28 -08:00
|
|
|
}
|
|
|
|
|
2022-05-20 01:55:48 -07:00
|
|
|
// Approve bank delegates for withdrawals
|
|
|
|
let group_seeds = group_seeds!(group);
|
|
|
|
let seeds = [&group_seeds[..]];
|
|
|
|
for (_, vault_info) in used_vaults.iter() {
|
|
|
|
if vault_info.withdraw_amount > 0 {
|
|
|
|
let approve_ctx = CpiContext::new(
|
|
|
|
ctx.accounts.token_program.to_account_info(),
|
|
|
|
token::Approve {
|
2022-06-18 07:43:45 -07:00
|
|
|
to: all_cpi_ais[vault_info.vault_cpi_ai_index].clone(),
|
2022-05-20 01:55:48 -07:00
|
|
|
delegate: health_ais[vault_info.bank_health_ai_index].clone(),
|
|
|
|
authority: ctx.accounts.group.to_account_info(),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.with_signer(&seeds);
|
|
|
|
token::approve(approve_ctx, vault_info.withdraw_amount)?;
|
|
|
|
}
|
|
|
|
}
|
2022-03-02 21:15:28 -08:00
|
|
|
|
2022-06-23 07:41:24 -07:00
|
|
|
msg!("withdraws {:#?}", withdraws);
|
|
|
|
msg!("cpi_datas {:#?}", cpi_datas);
|
|
|
|
msg!("allowed_vaults {:#?}", allowed_vaults);
|
|
|
|
msg!("used_vaults {:#?}", used_vaults);
|
|
|
|
|
2022-05-19 04:45:46 -07:00
|
|
|
// get rid of Ref<> to avoid limiting the cpi call
|
|
|
|
drop(allowed_banks);
|
|
|
|
drop(group);
|
|
|
|
drop(account);
|
|
|
|
|
2022-05-20 01:55:48 -07:00
|
|
|
// prepare signer seeds and invoke cpi
|
2022-05-19 04:45:46 -07:00
|
|
|
let group_key = ctx.accounts.group.key();
|
|
|
|
let signers = bank_signer_data
|
|
|
|
.iter()
|
2022-06-27 02:27:17 -07:00
|
|
|
.map(|(token_index, bank_num, bump)| {
|
2022-05-19 04:45:46 -07:00
|
|
|
[
|
|
|
|
group_key.as_ref(),
|
|
|
|
b"Bank".as_ref(),
|
|
|
|
&token_index[..],
|
2022-06-27 02:27:17 -07:00
|
|
|
&bank_num[..],
|
2022-05-19 04:45:46 -07:00
|
|
|
&bump[..],
|
|
|
|
]
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
let signers_ref = signers.iter().map(|v| &v[..]).collect::<Vec<_>>();
|
2022-06-18 08:14:20 -07:00
|
|
|
for (cpi_index, cpi_data) in cpi_datas.iter().enumerate() {
|
|
|
|
let cpi_account_start = cpi_data.account_start as usize;
|
2022-06-18 07:43:45 -07:00
|
|
|
let cpi_program_id = *ctx.remaining_accounts[cpi_account_start].key;
|
|
|
|
require_keys_neq!(cpi_program_id, crate::id(), MangoError::SomeError);
|
|
|
|
|
|
|
|
let all_cpi_ais_end_index = if cpi_index == num_of_cpis - 1 {
|
|
|
|
all_cpi_ams.len()
|
|
|
|
} else {
|
2022-06-18 08:14:20 -07:00
|
|
|
cpi_datas[cpi_index + 1].account_start as usize - num_health_accounts
|
2022-06-18 07:43:45 -07:00
|
|
|
};
|
|
|
|
|
2022-06-23 04:10:15 -07:00
|
|
|
let all_cpi_ais_start_index = cpi_account_start - num_health_accounts + 1;
|
2022-06-18 07:43:45 -07:00
|
|
|
|
|
|
|
let cpi_ais = &all_cpi_ais[all_cpi_ais_start_index..all_cpi_ais_end_index];
|
|
|
|
let cpi_ams = &all_cpi_ams[all_cpi_ais_start_index..all_cpi_ais_end_index];
|
|
|
|
let cpi_ix = Instruction {
|
|
|
|
program_id: cpi_program_id,
|
|
|
|
// todo future: optimise out these to_vecs
|
2022-06-18 08:14:20 -07:00
|
|
|
data: cpi_data.data.to_vec(),
|
2022-06-18 07:43:45 -07:00
|
|
|
accounts: cpi_ams.to_vec(),
|
|
|
|
};
|
|
|
|
solana_program::program::invoke_signed(&cpi_ix, &cpi_ais, &signers_ref)?;
|
|
|
|
}
|
2022-05-19 04:45:46 -07:00
|
|
|
|
2022-05-20 01:55:48 -07:00
|
|
|
// Revoke delegates for vaults
|
|
|
|
let group = ctx.accounts.group.load()?;
|
|
|
|
let group_seeds = group_seeds!(group);
|
|
|
|
for (_, vault_info) in used_vaults.iter() {
|
|
|
|
if vault_info.withdraw_amount > 0 {
|
|
|
|
let ix = token::spl_token::instruction::revoke(
|
|
|
|
&token::spl_token::ID,
|
2022-06-18 07:43:45 -07:00
|
|
|
all_cpi_ais[vault_info.vault_cpi_ai_index].key,
|
2022-05-20 01:55:48 -07:00
|
|
|
&ctx.accounts.group.key(),
|
|
|
|
&[],
|
|
|
|
)?;
|
|
|
|
solana_program::program::invoke_signed(
|
|
|
|
&ix,
|
|
|
|
&[
|
2022-06-18 07:43:45 -07:00
|
|
|
all_cpi_ais[vault_info.vault_cpi_ai_index].clone(),
|
2022-05-20 01:55:48 -07:00
|
|
|
ctx.accounts.group.to_account_info(),
|
|
|
|
],
|
|
|
|
&[group_seeds],
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-24 04:00:32 -07:00
|
|
|
// Track vault changes and apply them to the user's token positions
|
|
|
|
let mut account = ctx.accounts.account.load_mut()?;
|
|
|
|
let inactive_tokens =
|
2022-06-18 07:43:45 -07:00
|
|
|
adjust_for_post_cpi_vault_amounts(health_ais, all_cpi_ais, &used_vaults, &mut account)?;
|
2022-05-24 04:00:32 -07:00
|
|
|
|
|
|
|
// Check post-cpi health
|
|
|
|
let post_cpi_health =
|
|
|
|
compute_health_from_fixed_accounts(&account, HealthType::Init, health_ais)?;
|
|
|
|
require!(post_cpi_health >= 0, MangoError::HealthMustBePositive);
|
|
|
|
msg!("post_cpi_health {:?}", post_cpi_health);
|
|
|
|
|
|
|
|
// Deactivate inactive token accounts after health check
|
|
|
|
for raw_token_index in inactive_tokens {
|
|
|
|
account.tokens.deactivate(raw_token_index);
|
|
|
|
}
|
|
|
|
|
2022-05-19 04:45:46 -07:00
|
|
|
Ok(())
|
2022-03-04 05:30:53 -08:00
|
|
|
}
|
|
|
|
|
2022-05-19 04:45:46 -07:00
|
|
|
fn adjust_for_post_cpi_vault_amounts(
|
|
|
|
health_ais: &[AccountInfo],
|
2022-03-22 03:19:12 -07:00
|
|
|
cpi_ais: &[AccountInfo],
|
2022-05-20 01:55:48 -07:00
|
|
|
used_vaults: &HashMap<&Pubkey, AllowedVault>,
|
2022-03-15 08:38:24 -07:00
|
|
|
account: &mut MangoAccount,
|
2022-05-19 04:45:46 -07:00
|
|
|
) -> Result<Vec<usize>> {
|
|
|
|
let mut inactive_token_raw_indexes = Vec::with_capacity(used_vaults.len());
|
2022-05-20 01:55:48 -07:00
|
|
|
for (_, info) in used_vaults.iter() {
|
2022-05-19 04:45:46 -07:00
|
|
|
let vault = Account::<TokenAccount>::try_from(&cpi_ais[info.vault_cpi_ai_index]).unwrap();
|
|
|
|
let mut bank = health_ais[info.bank_health_ai_index].load_mut::<Bank>()?;
|
2022-05-24 04:00:32 -07:00
|
|
|
let position = account.tokens.get_mut_raw(info.raw_token_index);
|
2022-05-20 01:55:48 -07:00
|
|
|
|
|
|
|
let loan_origination_fee = info.loan_amount * bank.loan_origination_fee_rate;
|
|
|
|
bank.collected_fees_native += loan_origination_fee;
|
|
|
|
|
|
|
|
let is_active = bank.change_without_fee(
|
2022-05-19 04:45:46 -07:00
|
|
|
position,
|
2022-05-20 01:55:48 -07:00
|
|
|
I80F48::from(vault.amount) - I80F48::from(info.pre_amount) - loan_origination_fee,
|
2022-05-19 04:45:46 -07:00
|
|
|
)?;
|
|
|
|
if !is_active {
|
2022-05-24 04:00:32 -07:00
|
|
|
inactive_token_raw_indexes.push(info.raw_token_index);
|
2022-03-04 05:30:53 -08:00
|
|
|
}
|
|
|
|
}
|
2022-05-19 04:45:46 -07:00
|
|
|
Ok(inactive_token_raw_indexes)
|
2022-03-02 21:15:28 -08:00
|
|
|
}
|