use {
crate::{pack::*, string::ArrayString64, traits::*},
arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs},
num_enum::TryFromPrimitive,
serde::{Deserialize, Serialize},
serde_json::to_string,
solana_program::{program_error::ProgramError, pubkey::Pubkey},
};
#[allow(clippy::large_enum_variant)]
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, PartialEq)]
pub enum FarmRoute {
Raydium {
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
farm_id: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
farm_authority: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
farm_lp_token_account: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
farm_first_reward_token_account: Pubkey,
#[serde(
deserialize_with = "optional_pubkey_deserialize",
serialize_with = "optional_pubkey_serialize"
)]
farm_second_reward_token_account: Option<Pubkey>,
},
Saber {
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
quarry: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
rewarder: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
redeemer: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
redeemer_program: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
minter: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
mint_wrapper: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
mint_wrapper_program: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
iou_fees_account: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
sbr_vault: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
mint_proxy_program: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
mint_proxy_authority: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
mint_proxy_state: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
minter_info: Pubkey,
},
Orca {
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
farm_id: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
farm_authority: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
farm_token_ref: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
base_token_vault: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
reward_token_vault: Pubkey,
},
}
#[repr(u8)]
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
pub enum FarmRouteType {
Raydium,
Saber,
Orca,
}
#[repr(u8)]
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
pub enum FarmType {
SingleReward,
DualReward,
ProtocolTokenStake,
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, PartialEq)]
pub struct Farm {
#[serde(
serialize_with = "as64_serialize",
deserialize_with = "as64_deserialize"
)]
pub name: ArrayString64,
pub version: u16,
pub farm_type: FarmType,
pub official: bool,
pub refdb_index: Option<u32>,
pub refdb_counter: u16,
#[serde(
deserialize_with = "optional_pubkey_deserialize",
serialize_with = "optional_pubkey_serialize"
)]
pub lp_token_ref: Option<Pubkey>,
#[serde(
deserialize_with = "optional_pubkey_deserialize",
serialize_with = "optional_pubkey_serialize"
)]
pub first_reward_token_ref: Option<Pubkey>,
#[serde(
deserialize_with = "optional_pubkey_deserialize",
serialize_with = "optional_pubkey_serialize"
)]
pub second_reward_token_ref: Option<Pubkey>,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
pub router_program_id: Pubkey,
#[serde(
deserialize_with = "pubkey_deserialize",
serialize_with = "pubkey_serialize"
)]
pub farm_program_id: Pubkey,
pub route: FarmRoute,
}
impl Named for Farm {
fn name(&self) -> ArrayString64 {
self.name
}
}
impl Versioned for Farm {
fn version(&self) -> u16 {
self.version
}
}
impl Farm {
pub const MAX_LEN: usize = 655;
pub const RAYDIUM_FARM_LEN: usize = 400;
pub const SABER_FARM_LEN: usize = 655;
pub const ORCA_FARM_LEN: usize = 399;
fn pack_raydium(&self, output: &mut [u8]) -> Result<usize, ProgramError> {
check_data_len(output, Farm::RAYDIUM_FARM_LEN)?;
if let FarmRoute::Raydium {
farm_id,
farm_authority,
farm_lp_token_account,
farm_first_reward_token_account,
farm_second_reward_token_account,
} = self.route
{
let output = array_mut_ref![output, 0, Farm::RAYDIUM_FARM_LEN];
let (
farm_route_type_out,
name_out,
version_out,
farm_type_out,
official_out,
refdb_index_out,
refdb_counter_out,
lp_token_ref_out,
first_reward_token_ref_out,
second_reward_token_ref_out,
router_program_id_out,
farm_program_id_out,
farm_id_out,
farm_authority_out,
farm_lp_token_account_out,
farm_first_reward_token_account_out,
farm_second_reward_token_account_out,
) = mut_array_refs![
output, 1, 64, 2, 1, 1, 5, 2, 33, 33, 33, 32, 32, 32, 32, 32, 32, 33
];
farm_route_type_out[0] = FarmRouteType::Raydium as u8;
pack_array_string64(&self.name, name_out);
*version_out = self.version.to_le_bytes();
farm_type_out[0] = self.farm_type as u8;
official_out[0] = self.official as u8;
pack_option_u32(self.refdb_index, refdb_index_out);
*refdb_counter_out = self.refdb_counter.to_le_bytes();
pack_option_key(&self.lp_token_ref, lp_token_ref_out);
pack_option_key(&self.first_reward_token_ref, first_reward_token_ref_out);
pack_option_key(&self.second_reward_token_ref, second_reward_token_ref_out);
router_program_id_out.copy_from_slice(self.router_program_id.as_ref());
farm_program_id_out.copy_from_slice(self.farm_program_id.as_ref());
farm_id_out.copy_from_slice(farm_id.as_ref());
farm_authority_out.copy_from_slice(farm_authority.as_ref());
farm_lp_token_account_out.copy_from_slice(farm_lp_token_account.as_ref());
farm_first_reward_token_account_out
.copy_from_slice(farm_first_reward_token_account.as_ref());
pack_option_key(
&farm_second_reward_token_account,
farm_second_reward_token_account_out,
);
Ok(Farm::RAYDIUM_FARM_LEN)
} else {
Err(ProgramError::InvalidAccountData)
}
}
fn pack_saber(&self, output: &mut [u8]) -> Result<usize, ProgramError> {
check_data_len(output, Farm::SABER_FARM_LEN)?;
if let FarmRoute::Saber {
quarry,
rewarder,
redeemer,
redeemer_program,
minter,
mint_wrapper,
mint_wrapper_program,
iou_fees_account,
sbr_vault,
mint_proxy_program,
mint_proxy_authority,
mint_proxy_state,
minter_info,
} = self.route
{
let output = array_mut_ref![output, 0, Farm::SABER_FARM_LEN];
let (
farm_route_type_out,
name_out,
version_out,
farm_type_out,
official_out,
refdb_index_out,
refdb_counter_out,
lp_token_ref_out,
first_reward_token_ref_out,
second_reward_token_ref_out,
router_program_id_out,
farm_program_id_out,
quarry_out,
rewarder_out,
redeemer_out,
redeemer_program_out,
minter_out,
mint_wrapper_out,
mint_wrapper_program_out,
iou_fees_account_out,
sbr_vault_out,
mint_proxy_program_out,
mint_proxy_authority_out,
mint_proxy_state_out,
minter_info_out,
) = mut_array_refs![
output, 1, 64, 2, 1, 1, 5, 2, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32
];
farm_route_type_out[0] = FarmRouteType::Saber as u8;
pack_array_string64(&self.name, name_out);
*version_out = self.version.to_le_bytes();
farm_type_out[0] = self.farm_type as u8;
official_out[0] = self.official as u8;
pack_option_u32(self.refdb_index, refdb_index_out);
*refdb_counter_out = self.refdb_counter.to_le_bytes();
pack_option_key(&self.lp_token_ref, lp_token_ref_out);
pack_option_key(&self.first_reward_token_ref, first_reward_token_ref_out);
pack_option_key(&self.second_reward_token_ref, second_reward_token_ref_out);
router_program_id_out.copy_from_slice(self.router_program_id.as_ref());
farm_program_id_out.copy_from_slice(self.farm_program_id.as_ref());
quarry_out.copy_from_slice(quarry.as_ref());
rewarder_out.copy_from_slice(rewarder.as_ref());
redeemer_out.copy_from_slice(redeemer.as_ref());
redeemer_program_out.copy_from_slice(redeemer_program.as_ref());
minter_out.copy_from_slice(minter.as_ref());
mint_wrapper_out.copy_from_slice(mint_wrapper.as_ref());
mint_wrapper_program_out.copy_from_slice(mint_wrapper_program.as_ref());
iou_fees_account_out.copy_from_slice(iou_fees_account.as_ref());
sbr_vault_out.copy_from_slice(sbr_vault.as_ref());
mint_proxy_program_out.copy_from_slice(mint_proxy_program.as_ref());
mint_proxy_authority_out.copy_from_slice(mint_proxy_authority.as_ref());
mint_proxy_state_out.copy_from_slice(mint_proxy_state.as_ref());
minter_info_out.copy_from_slice(minter_info.as_ref());
Ok(Farm::SABER_FARM_LEN)
} else {
Err(ProgramError::InvalidAccountData)
}
}
fn pack_orca(&self, output: &mut [u8]) -> Result<usize, ProgramError> {
check_data_len(output, Farm::ORCA_FARM_LEN)?;
if let FarmRoute::Orca {
farm_id,
farm_authority,
farm_token_ref,
base_token_vault,
reward_token_vault,
} = self.route
{
let output = array_mut_ref![output, 0, Farm::ORCA_FARM_LEN];
let (
farm_route_type_out,
name_out,
version_out,
farm_type_out,
official_out,
refdb_index_out,
refdb_counter_out,
lp_token_ref_out,
first_reward_token_ref_out,
second_reward_token_ref_out,
router_program_id_out,
farm_program_id_out,
farm_id_out,
farm_authority_out,
farm_token_ref_out,
base_token_vault_out,
reward_token_vault_out,
) = mut_array_refs![
output, 1, 64, 2, 1, 1, 5, 2, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32
];
farm_route_type_out[0] = FarmRouteType::Orca as u8;
pack_array_string64(&self.name, name_out);
*version_out = self.version.to_le_bytes();
farm_type_out[0] = self.farm_type as u8;
official_out[0] = self.official as u8;
pack_option_u32(self.refdb_index, refdb_index_out);
*refdb_counter_out = self.refdb_counter.to_le_bytes();
pack_option_key(&self.lp_token_ref, lp_token_ref_out);
pack_option_key(&self.first_reward_token_ref, first_reward_token_ref_out);
pack_option_key(&self.second_reward_token_ref, second_reward_token_ref_out);
router_program_id_out.copy_from_slice(self.router_program_id.as_ref());
farm_program_id_out.copy_from_slice(self.farm_program_id.as_ref());
farm_id_out.copy_from_slice(farm_id.as_ref());
farm_authority_out.copy_from_slice(farm_authority.as_ref());
farm_token_ref_out.copy_from_slice(farm_token_ref.as_ref());
base_token_vault_out.copy_from_slice(base_token_vault.as_ref());
reward_token_vault_out.copy_from_slice(reward_token_vault.as_ref());
Ok(Farm::ORCA_FARM_LEN)
} else {
Err(ProgramError::InvalidAccountData)
}
}
fn unpack_raydium(input: &[u8]) -> Result<Farm, ProgramError> {
check_data_len(input, Farm::RAYDIUM_FARM_LEN)?;
let input = array_ref![input, 1, Farm::RAYDIUM_FARM_LEN - 1];
#[allow(clippy::ptr_offset_with_cast)]
let (
name,
version,
farm_type,
official,
refdb_index,
refdb_counter,
lp_token_ref,
first_reward_token_ref,
second_reward_token_ref,
router_program_id,
farm_program_id,
farm_id,
farm_authority,
farm_lp_token_account,
farm_first_reward_token_account,
farm_second_reward_token_account,
) = array_refs![input, 64, 2, 1, 1, 5, 2, 33, 33, 33, 32, 32, 32, 32, 32, 32, 33];
Ok(Self {
name: unpack_array_string64(name)?,
version: u16::from_le_bytes(*version),
farm_type: FarmType::try_from_primitive(farm_type[0])
.or(Err(ProgramError::InvalidAccountData))?,
official: unpack_bool(official)?,
refdb_index: unpack_option_u32(refdb_index)?,
refdb_counter: u16::from_le_bytes(*refdb_counter),
lp_token_ref: unpack_option_key(lp_token_ref)?,
first_reward_token_ref: unpack_option_key(first_reward_token_ref)?,
second_reward_token_ref: unpack_option_key(second_reward_token_ref)?,
router_program_id: Pubkey::new_from_array(*router_program_id),
farm_program_id: Pubkey::new_from_array(*farm_program_id),
route: FarmRoute::Raydium {
farm_id: Pubkey::new_from_array(*farm_id),
farm_authority: Pubkey::new_from_array(*farm_authority),
farm_lp_token_account: Pubkey::new_from_array(*farm_lp_token_account),
farm_first_reward_token_account: Pubkey::new_from_array(
*farm_first_reward_token_account,
),
farm_second_reward_token_account: unpack_option_key(
farm_second_reward_token_account,
)?,
},
})
}
fn unpack_saber(input: &[u8]) -> Result<Farm, ProgramError> {
check_data_len(input, Farm::SABER_FARM_LEN)?;
let input = array_ref![input, 1, Farm::SABER_FARM_LEN - 1];
#[allow(clippy::ptr_offset_with_cast)]
let (
name,
version,
farm_type,
official,
refdb_index,
refdb_counter,
lp_token_ref,
first_reward_token_ref,
second_reward_token_ref,
router_program_id,
farm_program_id,
quarry,
rewarder,
redeemer,
redeemer_program,
minter,
mint_wrapper,
mint_wrapper_program,
iou_fees_account,
sbr_vault,
mint_proxy_program,
mint_proxy_authority,
mint_proxy_state,
minter_info,
) = array_refs![
input, 64, 2, 1, 1, 5, 2, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32
];
Ok(Self {
name: unpack_array_string64(name)?,
version: u16::from_le_bytes(*version),
farm_type: FarmType::try_from_primitive(farm_type[0])
.or(Err(ProgramError::InvalidAccountData))?,
official: unpack_bool(official)?,
refdb_index: unpack_option_u32(refdb_index)?,
refdb_counter: u16::from_le_bytes(*refdb_counter),
lp_token_ref: unpack_option_key(lp_token_ref)?,
first_reward_token_ref: unpack_option_key(first_reward_token_ref)?,
second_reward_token_ref: unpack_option_key(second_reward_token_ref)?,
router_program_id: Pubkey::new_from_array(*router_program_id),
farm_program_id: Pubkey::new_from_array(*farm_program_id),
route: FarmRoute::Saber {
quarry: Pubkey::new_from_array(*quarry),
rewarder: Pubkey::new_from_array(*rewarder),
redeemer: Pubkey::new_from_array(*redeemer),
redeemer_program: Pubkey::new_from_array(*redeemer_program),
minter: Pubkey::new_from_array(*minter),
mint_wrapper: Pubkey::new_from_array(*mint_wrapper),
mint_wrapper_program: Pubkey::new_from_array(*mint_wrapper_program),
iou_fees_account: Pubkey::new_from_array(*iou_fees_account),
sbr_vault: Pubkey::new_from_array(*sbr_vault),
mint_proxy_program: Pubkey::new_from_array(*mint_proxy_program),
mint_proxy_authority: Pubkey::new_from_array(*mint_proxy_authority),
mint_proxy_state: Pubkey::new_from_array(*mint_proxy_state),
minter_info: Pubkey::new_from_array(*minter_info),
},
})
}
fn unpack_orca(input: &[u8]) -> Result<Farm, ProgramError> {
check_data_len(input, Farm::ORCA_FARM_LEN)?;
let input = array_ref![input, 1, Farm::ORCA_FARM_LEN - 1];
#[allow(clippy::ptr_offset_with_cast)]
let (
name,
version,
farm_type,
official,
refdb_index,
refdb_counter,
lp_token_ref,
first_reward_token_ref,
second_reward_token_ref,
router_program_id,
farm_program_id,
farm_id,
farm_authority,
farm_token_ref,
base_token_vault,
reward_token_vault,
) = array_refs![input, 64, 2, 1, 1, 5, 2, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32];
Ok(Self {
name: unpack_array_string64(name)?,
version: u16::from_le_bytes(*version),
farm_type: FarmType::try_from_primitive(farm_type[0])
.or(Err(ProgramError::InvalidAccountData))?,
official: unpack_bool(official)?,
refdb_index: unpack_option_u32(refdb_index)?,
refdb_counter: u16::from_le_bytes(*refdb_counter),
lp_token_ref: unpack_option_key(lp_token_ref)?,
first_reward_token_ref: unpack_option_key(first_reward_token_ref)?,
second_reward_token_ref: unpack_option_key(second_reward_token_ref)?,
router_program_id: Pubkey::new_from_array(*router_program_id),
farm_program_id: Pubkey::new_from_array(*farm_program_id),
route: FarmRoute::Orca {
farm_id: Pubkey::new_from_array(*farm_id),
farm_authority: Pubkey::new_from_array(*farm_authority),
farm_token_ref: Pubkey::new_from_array(*farm_token_ref),
base_token_vault: Pubkey::new_from_array(*base_token_vault),
reward_token_vault: Pubkey::new_from_array(*reward_token_vault),
},
})
}
}
impl Packed for Farm {
fn get_size(&self) -> usize {
match self.route {
FarmRoute::Raydium { .. } => Farm::RAYDIUM_FARM_LEN,
FarmRoute::Saber { .. } => Farm::SABER_FARM_LEN,
FarmRoute::Orca { .. } => Farm::ORCA_FARM_LEN,
}
}
fn pack(&self, output: &mut [u8]) -> Result<usize, ProgramError> {
match self.route {
FarmRoute::Raydium { .. } => self.pack_raydium(output),
FarmRoute::Saber { .. } => self.pack_saber(output),
FarmRoute::Orca { .. } => self.pack_orca(output),
}
}
fn to_vec(&self) -> Result<Vec<u8>, ProgramError> {
let mut output: [u8; Farm::MAX_LEN] = [0; Farm::MAX_LEN];
if let Ok(len) = self.pack(&mut output[..]) {
Ok(output[..len].to_vec())
} else {
Err(ProgramError::InvalidAccountData)
}
}
fn unpack(input: &[u8]) -> Result<Farm, ProgramError> {
check_data_len(input, 1)?;
let farm_route_type = FarmRouteType::try_from_primitive(input[0])
.or(Err(ProgramError::InvalidAccountData))?;
match farm_route_type {
FarmRouteType::Raydium => Farm::unpack_raydium(input),
FarmRouteType::Saber => Farm::unpack_saber(input),
FarmRouteType::Orca => Farm::unpack_orca(input),
}
}
}
impl std::fmt::Display for FarmType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
FarmType::SingleReward => write!(f, "SingleReward"),
FarmType::DualReward => write!(f, "DualReward"),
FarmType::ProtocolTokenStake => write!(f, "ProtocolTokenStake"),
}
}
}
impl std::fmt::Display for Farm {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", to_string(&self).unwrap())
}
}