#![deny(unused_must_use)]
#![deny(clippy::all)]
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::extra_unused_lifetimes)]
#![allow(clippy::field_reassign_with_default)]
use self::vault_base::VaultBaseV1;
use anchor_lang::prelude::*;
use anchor_spl::token::Mint;
use tulipv2_sdk_common::traits::vault::TokenizedShares;
use tulipv2_sdk_farms::Farm;
pub mod atrix_vault;
pub mod lending_optimizer;
pub mod multi_optimizer;
pub mod orca_vault;
pub mod quarry_vault;
pub mod raydium_vault;
pub mod tracking;
pub mod vault_base;
pub trait Base {
fn base(&self) -> VaultBaseV1;
fn shares_mut(&mut self) -> &mut dyn TokenizedShares;
fn shares(&self) -> &dyn TokenizedShares;
fn init(&mut self, args: &InitVaultArgsV1);
fn farm(&self) -> Farm;
fn sync_shares(&mut self, mint: &Mint);
}
#[derive(Default)]
pub struct InitVaultArgsV1;
#[derive(AnchorSerialize, AnchorDeserialize, Default, Clone)]
pub struct InitFeeArgsV1;
pub fn derive_vault_address(farm: &tulipv2_sdk_farms::Farm, tag: [u8; 32]) -> (Pubkey, u8) {
let parts: [u64; 2] = (*farm).into();
Pubkey::find_program_address(
&[
b"v1",
&parts[0].to_le_bytes()[..],
&parts[1].to_le_bytes()[..],
&tag[..],
],
&crate::ID,
)
}
pub fn derive_pda_address(vault: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(&[vault.as_ref()], &crate::ID)
}
pub fn derive_shares_mint_address(vault: &Pubkey, underlying_mint: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(&[vault.as_ref(), underlying_mint.as_ref()], &crate::ID)
}
pub fn derive_withdraw_queue_address(vault: &Pubkey, underlying_mint: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[b"withdrawqueue", vault.as_ref(), underlying_mint.as_ref()],
&crate::ID,
)
}
pub fn derive_compound_queue_address(vault: &Pubkey, underlying_mint: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[b"compoundqueue", vault.as_ref(), underlying_mint.as_ref()],
&crate::ID,
)
}
pub fn derive_tracking_address(
vault: &Pubkey,
owner: &Pubkey,
program_id: &Pubkey,
) -> (Pubkey, u8) {
Pubkey::find_program_address(&[b"tracking", vault.as_ref(), owner.as_ref()], program_id)
}
pub fn derive_ephemeral_tracking_address(
vault: &Pubkey,
owner: &Pubkey,
program_id: &Pubkey,
) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[b"ephemeraltracking", vault.as_ref(), owner.as_ref()],
program_id,
)
}
pub fn derive_tracking_pda_address(tracking_account: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(&[tracking_account.as_ref()], program_id)
}
pub fn derive_tracking_queue_address(tracking_pda: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(&[b"queue", tracking_pda.as_ref()], program_id)
}
pub fn derive_lending_platform_config_data_address(platform_address: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(&[b"v1", platform_address.as_ref()], &crate::ID)
}
pub fn derive_lending_platform_information_account(vault: &Pubkey, index: u64) -> (Pubkey, u8) {
Pubkey::find_program_address(&[vault.as_ref(), &index.to_le_bytes()[..]], &crate::ID)
}
pub fn derive_mango_account_address(vault: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(&[vault.as_ref(), b"mango"], &crate::ID)
}
pub fn derive_tracking_orca_dd_queue_address(
tracking_pda: &Pubkey,
vault: &Pubkey,
) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[b"ddwithdrawqueue", tracking_pda.as_ref(), vault.as_ref()],
&crate::ID,
)
}
pub fn derive_multi_deposit_state_transition_address(vault: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(&[b"statetransition", vault.as_ref()], &crate::ID)
}
pub fn derive_quarry_vault_config_data_address(vault: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(&[b"config", vault.as_ref()], &crate::ID)
}
pub fn parse_formatted_name(formatted_name: &str) -> (String, String) {
let parts: Vec<_> = formatted_name.split('-').collect();
let mut farm_name_parts = Vec::with_capacity(parts.len());
let mut tag = String::new();
for (_idx, part) in parts.iter().enumerate() {
if (*part).contains("tag") {
tag = match part.to_string().strip_prefix("tag(") {
Some(part) => match part.strip_suffix(')') {
Some(part) => part.to_owned(),
None => panic!("failed to parse tag"),
},
None => panic!("failed to parse tag"),
};
break;
} else {
farm_name_parts.push(*part);
}
}
let mut farm_name = String::with_capacity(std::mem::size_of_val(&farm_name_parts));
for (idx, part) in farm_name_parts.iter().enumerate() {
farm_name.push_str(*part);
if idx < farm_name_parts.len() - 1 {
farm_name.push('-');
}
}
(farm_name, tag)
}
use bytemuck::{cast_slice, from_bytes, try_cast_slice, Pod};
pub fn load<T: Pod>(data: &[u8]) -> Result<&T> {
let size = std::mem::size_of::<T>();
let sliced_data = if let Ok(sliced_data) = try_cast_slice(&data[0..size]) {
sliced_data
} else {
return Err(
anchor_lang::solana_program::program_error::ProgramError::InvalidAccountData.into(),
);
};
Ok(from_bytes(cast_slice::<u8, u8>(sliced_data)))
}
#[cfg(test)]
mod test {
use std::str::FromStr;
use tulipv2_sdk_common::DEFAULT_KEY;
use tulipv2_sdk_farms::Farm;
use super::*;
#[test]
fn test_derive_usdc_multi_deposit_optimizer() {
let vault_address = derive_vault_address(
&tulipv2_sdk_farms::Farm::Lending {
name: tulipv2_sdk_farms::lending::Lending::MULTI_DEPOSIT,
},
tulipv2_sdk_common::tag::tag("usdcv1").unwrap(),
)
.0;
assert_eq!(
vault_address.to_string(),
"3wPiV9inTGexMZjp6x5Amqwp2sRNtpSheG8Hbv2rgq8W".to_string()
);
}
#[test]
fn test_usdc_standalone_optimizers() {
let vault_address = derive_vault_address(
&tulipv2_sdk_farms::Farm::Lending {
name: tulipv2_sdk_farms::lending::Lending::USDC,
},
tulipv2_sdk_common::tag::tag("solend").unwrap(),
)
.0;
assert_eq!(
vault_address.to_string(),
"85JXjDiyianDpvz8y8efkRyFsxpnSJJpmyxrJ7bncKHM".to_string()
);
let vault_address = derive_vault_address(
&tulipv2_sdk_farms::Farm::Lending {
name: tulipv2_sdk_farms::lending::Lending::USDC,
},
tulipv2_sdk_common::tag::tag("mango").unwrap(),
)
.0;
assert_eq!(
vault_address.to_string(),
"ZH9GWNBtwxcWeU9kHk77DSciwQnoJcSm8VVvYfmHXfe".to_string()
);
let vault_address = derive_vault_address(
&tulipv2_sdk_farms::Farm::Lending {
name: tulipv2_sdk_farms::lending::Lending::USDC,
},
tulipv2_sdk_common::tag::tag("tulip").unwrap(),
)
.0;
assert_eq!(
vault_address.to_string(),
"8KLrrsnUv3DjC9Q89xSQDVdiGLZHUEUuyPedfHrtuVRr".to_string()
);
}
#[test]
fn tracking_addresses() {
let vault = DEFAULT_KEY;
let owner = DEFAULT_KEY;
let pid = Pubkey::from_str("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS").unwrap();
let (track, track_nonce) = derive_tracking_address(&vault, &owner, &pid);
let (ephtrack, ephtrack_nonce) = derive_ephemeral_tracking_address(&vault, &owner, &pid);
println!("track {}, track nonce {}", track, track_nonce);
println!("ephtrack {}, ephtrack_nonce {}", ephtrack, ephtrack_nonce);
assert_eq!(
track.to_string(),
"AMfs9gQR74UyJ52oMmpuxw2eL1qjLF5M87cFZTX2sQxH".to_string()
);
assert_eq!(track_nonce, 255);
assert_eq!(
ephtrack.to_string(),
"2eMFBzgUq7sKuvvs8MZphyz3KUPAjC5o3R3vMMMTeMft".to_string()
);
assert_eq!(ephtrack_nonce, 255,);
let (track_pda, track_pda_nonce) = derive_tracking_pda_address(&track, &pid);
println!(
"track pda {}, track pda nonce {}",
track_pda, track_pda_nonce
);
assert_eq!(
track_pda.to_string(),
"6riZUKJKjGgic92kso1azrgUsjwRj8yfPwgg4PEZEcnt".to_string()
);
assert_eq!(track_pda_nonce, 255);
let (track_queue, track_queue_nonce) = derive_tracking_queue_address(&track_pda, &pid);
println!(
"track queue {}, track queue nonce {}",
track_queue, track_queue_nonce
);
assert_eq!(
track_queue.to_string(),
"JCtJ5HEvXpmWWsFPTVRACfwBiEMQV7r1gbFR57DNRmkS".to_string()
);
assert_eq!(track_queue_nonce, 255);
}
#[test]
fn vault_addresses() {
let farm = Farm::Raydium {
name: tulipv2_sdk_farms::raydium::Raydium::ALEPHUSDC,
};
let (vault, vault_nonce) = derive_vault_address(&farm, [0_u8; 32]);
println!("vault {}, vault nonce {}", vault, vault_nonce);
assert_eq!(
vault.to_string(),
"8gjgbcRew7zZJkQ1cdHxMjGHzm5yXwtkgyGtFLTD6f1e".to_string()
);
assert_eq!(vault_nonce, 255);
let (pda, pda_nonce) = derive_pda_address(&vault);
println!("pda {}, pda nonce {}", pda, pda_nonce);
assert_eq!(
pda.to_string(),
"3jk82ez7bVnxCCiRVV6qXGHh72Y2Q7LJdSGcAsyCf8jv".to_string()
);
assert_eq!(pda_nonce, 255);
let (shares, shares_nonce) = derive_shares_mint_address(&vault, &DEFAULT_KEY);
println!("shares {}, shares nonce {}", shares, shares_nonce);
assert_eq!(
shares.to_string(),
"2Ldw8EAsKa7WiX4YdMQRx8SBCx6HRk8wfzRrBZcKhTmL".to_string()
);
assert_eq!(shares_nonce, 253);
let (queue, queue_nonce) = derive_withdraw_queue_address(&vault, &DEFAULT_KEY);
println!("queue {}, queue nonce {}", queue, queue_nonce);
assert_eq!(
queue.to_string(),
"3NFBCpxhfGACbGL8GFf5RMhSbdaEjGiqzMpS3vC8sjAH".to_string()
);
assert_eq!(queue_nonce, 253);
let (c_queue, c_queue_nonce) = derive_compound_queue_address(&vault, &DEFAULT_KEY);
println!("c queue {}, c queue nonce {}", c_queue, c_queue_nonce);
assert_eq!(
c_queue.to_string(),
"4x6xMfuuBzvHTFz18N3oB7TviwJQ1JRhQEewnsuUWqrF".to_string()
);
assert_eq!(c_queue_nonce, 255);
let (state_addr, state_nonce) = derive_multi_deposit_state_transition_address(&vault);
assert_eq!(
state_addr.to_string(),
"CTt8rN8UhatQXnj7MfXZjw7ZzTsvpr2HYse5p2BgQb31".to_string(),
);
println!("addr {}, nonce {}", state_addr, state_nonce);
let (config_addr, nonce) = derive_quarry_vault_config_data_address(&vault);
println!("addr {}, nonce {}", config_addr, nonce);
assert_eq!(
config_addr.to_string(),
"9mjm5ryiWg94qsd4Chz8v7RDKRQrswj7u653C6xehMXs".to_string()
);
assert_eq!(nonce, 255);
}
#[test]
fn test_parse_formatted_name() {
let with_tag_1 = "LENDING-MULTI_DEPOSIT-tag(usdcv1)".to_string();
let with_tag_2 = "LENDING-MULTI_DEPOSIT-tag(rayv1)".to_string();
let with_no_tag_1 = "RAYDIUM-TULIP-USDC-tag()".to_string();
let with_no_tag_2 = "RAYDIUM-RAY-USDC-tag()".to_string();
let tests = vec![
("LENDING-MULTI_DEPOSIT", "usdcv1", with_tag_1),
("LENDING-MULTI_DEPOSIT", "rayv1", with_tag_2),
("RAYDIUM-TULIP-USDC", "", with_no_tag_1),
("RAYDIUM-RAY-USDC", "", with_no_tag_2),
];
for test in tests {
let (got_farm_name, got_tag) = parse_formatted_name(&test.2);
assert_eq!(&got_farm_name, test.0);
assert_eq!(&got_tag, test.1);
}
}
}