Created
May 15, 2024 22:22
-
-
Save mgild/26aa544e4d2184206b389a440d31a721 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use crate::protos::oracle_job::*; | |
use crate::TaskOutput; | |
use crate::TaskResult; | |
use crate::TaskRunnerContext; | |
use anyhow_ext::anyhow; | |
use anyhow_ext::Result; | |
use async_recursion::async_recursion; | |
use jupiter_amm_interface::Amm; | |
use jupiter_amm_interface::SwapMode; | |
use lazy_static::lazy_static; | |
use rust_decimal::Decimal; | |
use s_controller_lib; | |
use s_jup_interface::SPoolJup; | |
use s_sol_val_calc_prog_aggregate::KnownLstSolValCalc; | |
use s_sol_val_calc_prog_aggregate::LidoLstSolValCalc; | |
use s_sol_val_calc_prog_aggregate::LstSolValCalc; | |
use s_sol_val_calc_prog_aggregate::MarinadeLstSolValCalc; | |
use s_sol_val_calc_prog_aggregate::MutableLstSolValCalc; | |
use s_sol_val_calc_prog_aggregate::SanctumSplLstSolValCalc; | |
use s_sol_val_calc_prog_aggregate::SanctumSplMultiLstSolValCalc; | |
use s_sol_val_calc_prog_aggregate::SplLstSolValCalc; | |
use s_sol_val_calc_prog_aggregate::SplLstSolValCalcInitKeys; | |
use s_sol_val_calc_prog_aggregate::WsolLstSolValCalc; | |
use sanctum_lst_list::PoolInfo::SPool; | |
use sanctum_lst_list::PoolInfo::SanctumSpl; | |
use sanctum_lst_list::PoolInfo::SanctumSplMulti; | |
use sanctum_lst_list::PoolInfo::Spl; | |
use sanctum_lst_list::SanctumLst; | |
use sanctum_lst_list::SanctumLstList; | |
use sanctum_lst_list::SplPoolAccounts; | |
use solana_client::nonblocking::rpc_client::RpcClient; | |
use solana_sdk::account::Account; | |
use solana_sdk::account_info::AccountInfo; | |
use solana_sdk::native_token::LAMPORTS_PER_SOL; | |
use solana_sdk::pubkey::Pubkey; | |
use std::cell::RefCell; | |
use std::collections::HashMap; | |
use std::rc::Rc; | |
use std::str::FromStr; | |
lazy_static! { | |
static ref INF_MINT: Pubkey = | |
Pubkey::from_str("5oVNBeEEQvYi1cX3ir8Dx5n1P7pdxydbGF2X4TxVusJm").unwrap(); | |
static ref SOL_MINT: Pubkey = | |
Pubkey::from_str("So11111111111111111111111111111111111111112").unwrap(); | |
} | |
fn to_account_info<'a>(key: &'a Pubkey, data: &'a Account) -> AccountInfo<'a> { | |
let data = data as *const Account as *mut Account; | |
unsafe { | |
AccountInfo { | |
key: &key, | |
lamports: Rc::new(RefCell::new(&mut (*data).lamports)), | |
data: Rc::new(RefCell::new((*data).data.as_mut_slice())), | |
owner: &(*data).owner, | |
rent_epoch: (*data).rent_epoch, | |
executable: (*data).executable, | |
is_signer: false, | |
is_writable: false, | |
} | |
} | |
} | |
async fn get_accounts(client: &RpcClient, keys: Vec<Pubkey>) -> Result<HashMap<Pubkey, Account>> { | |
let mut account_datas = Vec::new(); | |
for key_chunk in keys.chunks(100) { | |
let chunk: Vec<Option<Account>> = client | |
.get_multiple_accounts(&key_chunk) | |
.await | |
.unwrap(); | |
account_datas.extend(chunk); | |
} | |
let mut data_map = HashMap::new(); | |
for i in 0..keys.len() { | |
let key = &keys[i]; | |
let account = &account_datas[i]; | |
if let Some(account) = account { | |
data_map.insert(key.clone(), account.clone()); | |
} else { | |
println!("Failed to get SPool required account {key}"); | |
} | |
} | |
Ok(data_map) | |
} | |
pub async fn calc_from_calculator( | |
client: &RpcClient, | |
mut calc: KnownLstSolValCalc, | |
) -> Result<Decimal> { | |
let accounts = calc.get_accounts_to_update(); | |
let account_datas: Vec<Account> = client | |
.get_multiple_accounts(&accounts.clone()) | |
.await | |
.map_err(|_| anyhow!("Failed to get accounts"))? | |
.into_iter() | |
.filter_map(|x| x) | |
.collect::<Vec<_>>(); | |
if accounts.len() != account_datas.len() { | |
return Err(anyhow!( | |
"Account data mismatch, failed to fetch all needed calc accounts" | |
)); | |
} | |
let mut data_map = HashMap::new(); | |
for i in 0..accounts.len() { | |
let key = &accounts[i]; | |
data_map.insert(key.clone(), to_account_info(key, &account_datas[i])); | |
} | |
calc.update(&data_map) | |
.map_err(|_| anyhow!("Failed to update calc"))?; | |
let range = calc | |
.lst_to_sol(LAMPORTS_PER_SOL) | |
.map_err(|_| anyhow!("Failed to calculate lst_to_sol"))?; | |
// Allow up to 1 for rounding | |
if range.get_max() - range.get_min() > 1 { | |
return Err(anyhow!("SanctumCalcError: Unexpected range")); | |
} | |
Ok(Decimal::from(range.get_max()) / Decimal::from(LAMPORTS_PER_SOL)) | |
} | |
pub async fn calc_from_lst(client: &RpcClient, lst: Option<&SanctumLst>) -> Result<Decimal> { | |
let lst = lst.ok_or(anyhow!("SanctumLstNotFound"))?; | |
let calc; | |
if let SanctumSplMulti(SplPoolAccounts { pool, .. }) = lst.pool { | |
calc = KnownLstSolValCalc::SanctumSplMulti(SanctumSplMultiLstSolValCalc::from_keys( | |
SplLstSolValCalcInitKeys { | |
lst_mint: lst.mint, | |
stake_pool_addr: pool, | |
}, | |
)); | |
} else if let SanctumSpl(SplPoolAccounts { pool, .. }) = lst.pool { | |
calc = KnownLstSolValCalc::SanctumSpl(SanctumSplLstSolValCalc::from_keys( | |
SplLstSolValCalcInitKeys { | |
lst_mint: lst.mint, | |
stake_pool_addr: pool, | |
}, | |
)); | |
} else if let Spl(SplPoolAccounts { pool, .. }) = lst.pool { | |
calc = KnownLstSolValCalc::Spl(SplLstSolValCalc::from_keys(SplLstSolValCalcInitKeys { | |
lst_mint: lst.mint, | |
stake_pool_addr: pool, | |
})); | |
} else if let sanctum_lst_list::PoolInfo::Marinade = lst.pool { | |
calc = MarinadeLstSolValCalc::default().into(); | |
} else if let sanctum_lst_list::PoolInfo::Lido = lst.pool { | |
calc = LidoLstSolValCalc::default().into(); | |
} else if let sanctum_lst_list::PoolInfo::ReservePool = lst.pool { | |
calc = WsolLstSolValCalc::default().into(); | |
} else if let SPool(_) = lst.pool { | |
if lst.mint != *INF_MINT { | |
return Err(anyhow!("InvalidSpool")); | |
} | |
return spool_quote(client).await; | |
} else { | |
return Err(anyhow!("SanctumLstNotSpl")); | |
} | |
calc_from_calculator(client, calc).await | |
} | |
pub async fn spool_quote(client: &RpcClient) -> Result<Decimal> { | |
let lst_state_list_pubkey = s_controller_lib::program::LST_STATE_LIST_ID; | |
let pid = s_controller_lib::program::ID; | |
let lst_state_list_account = client.get_account(&lst_state_list_pubkey).await?; | |
let SanctumLstList { sanctum_lst_list } = SanctumLstList::load(); | |
let mut spool: SPoolJup = | |
SPoolJup::from_lst_state_list_account(pid, lst_state_list_account, &sanctum_lst_list)?; | |
let keys = spool.get_accounts_to_update(); | |
let data_map = get_accounts(&client, keys).await?; | |
spool.update_full(&data_map)?; | |
let keys = spool.get_accounts_to_update(); | |
let data_map = get_accounts(&client, keys).await?; | |
spool.update_full(&data_map)?; | |
let quote = spool.quote(&jupiter_amm_interface::QuoteParams { | |
amount: 10_u64.pow(9), | |
input_mint: *INF_MINT, | |
output_mint: *SOL_MINT, | |
swap_mode: SwapMode::ExactIn, | |
})?; | |
let out_amount = Decimal::new(quote.out_amount.try_into().unwrap(), 9); | |
Ok(out_amount) | |
} | |
pub fn to_lst<'a>(lsts: &'a Vec<SanctumLst>, key: &'a Pubkey) -> Option<&'a SanctumLst> { | |
lsts.into_iter().find(|x| x.mint == *key) | |
} | |
#[async_recursion] | |
pub async fn sanctum_task( | |
ctx: &TaskRunnerContext, | |
task: &SanctumLstPriceTask, | |
) -> TaskResult<TaskOutput> { | |
let lst_mint = Pubkey::from_str(&task.lst_mint.clone().unwrap_or_default()) | |
.map_err(|_| anyhow!("SanctumPriceTask:InvalidMint"))?; | |
let SanctumLstList { | |
sanctum_lst_list: lsts, | |
} = SanctumLstList::load(); | |
let client = ctx | |
.anchor_client | |
.program(Pubkey::default()) | |
.map_err(|_| anyhow!("Failed to get program"))? | |
.async_rpc(); | |
let price = calc_from_lst(&client, to_lst(&lsts, &lst_mint)).await?; | |
Ok(TaskOutput::Num(price)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment