use super::{pda, FLOASHLOAN_PROGRAM_ID};
use crate::{
liquidity::{self, LIQUIDITY_PROGRAM_ID},
math::{casting::Cast, safe_math::SafeMath},
programs::flashloan::{
accounts::FlashloanAdmin,
client::{accounts, args},
},
};
use anchor_client::{
solana_sdk::{
commitment_config::CommitmentConfig, instruction::Instruction, signature::Keypair,
},
Client, Cluster,
};
use anchor_lang::{prelude::Pubkey, solana_program, system_program};
use spl_associated_token_account::{
get_associated_token_address_with_program_id, ID as ASSOCIATED_TOKEN_PROGRAM_ID,
};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct FlashloanParams {
pub amount: u64,
pub asset: Pubkey,
pub cluster: Cluster,
pub signer: Pubkey,
}
#[derive(Debug, Clone)]
pub struct Flashloan {
pub borrow_ix: Instruction,
pub payback_ix: Instruction,
}
pub fn get_flashloan_ix(params: FlashloanParams) -> anyhow::Result<Flashloan> {
let provider = Client::new_with_options(
params.cluster.clone(),
Arc::new(Keypair::new()),
CommitmentConfig::confirmed(),
);
let program = provider.program(FLOASHLOAN_PROGRAM_ID)?;
let token_program = {
let account = program
.rpc()
.get_account(¶ms.asset)
.expect("Failed to get asset account");
let program_id = account.owner;
program_id
};
let flashloan_admin: FlashloanAdmin = program.account(pda::get_flashloan_admin())?;
let borrow_ix = program
.request()
.accounts(accounts::FlashloanBorrow {
signer: params.signer,
flashloan_admin: pda::get_flashloan_admin(),
signer_borrow_token_account: get_associated_token_address_with_program_id(
¶ms.signer,
¶ms.asset,
&token_program,
),
mint: params.asset,
flashloan_token_reserves_liquidity: liquidity::pda::get_token_reserve(params.asset),
flashloan_borrow_position_on_liquidity: liquidity::pda::get_user_borrow_position(
params.asset,
pda::get_flashloan_admin(),
),
rate_model: liquidity::pda::get_rate_model(params.asset),
vault: get_associated_token_address_with_program_id(
&liquidity::pda::get_liquidity(),
¶ms.asset,
&token_program,
),
liquidity: liquidity::pda::get_liquidity(),
liquidity_program: LIQUIDITY_PROGRAM_ID,
token_program,
associated_token_program: ASSOCIATED_TOKEN_PROGRAM_ID,
system_program: system_program::ID,
instruction_sysvar: solana_program::sysvar::instructions::ID,
})
.args(args::FlashloanBorrow {
amount: params.amount,
})
.instructions()?
.remove(0);
let flashloan_fee: u128 = flashloan_admin
.flashloan_fee
.cast()
.map_err(|err| anyhow::Error::msg(err.to_string()))?;
let flashloan_fee_amount = params
.amount
.cast::<u128>()
.map_err(|err| anyhow::Error::msg(err.to_string()))?
.safe_mul(flashloan_fee)
.map_err(|err| anyhow::Error::msg(err.to_string()))?
.safe_div_ceil(10000)
.map_err(|err| anyhow::Error::msg(err.to_string()))?
.cast::<u64>()
.map_err(|err| anyhow::Error::msg(err.to_string()))?;
let payback_amount = params
.amount
.safe_add(flashloan_fee_amount)
.map_err(|err| anyhow::Error::msg(err.to_string()))?;
let payback_ix = program
.request()
.accounts(accounts::FlashloanPayback {
signer: params.signer,
flashloan_admin: pda::get_flashloan_admin(),
signer_borrow_token_account: get_associated_token_address_with_program_id(
¶ms.signer,
¶ms.asset,
&token_program,
),
mint: params.asset,
flashloan_token_reserves_liquidity: liquidity::pda::get_token_reserve(params.asset),
flashloan_borrow_position_on_liquidity: liquidity::pda::get_user_borrow_position(
params.asset,
pda::get_flashloan_admin(),
),
rate_model: liquidity::pda::get_rate_model(params.asset),
vault: get_associated_token_address_with_program_id(
&liquidity::pda::get_liquidity(),
¶ms.asset,
&token_program,
),
liquidity: liquidity::pda::get_liquidity(),
liquidity_program: LIQUIDITY_PROGRAM_ID,
token_program,
associated_token_program: ASSOCIATED_TOKEN_PROGRAM_ID,
system_program: system_program::ID,
instruction_sysvar: solana_program::sysvar::instructions::ID,
})
.args(args::FlashloanPayback {
amount: payback_amount,
})
.instructions()?
.remove(0);
Ok(Flashloan {
borrow_ix,
payback_ix,
})
}