use solana_sdk::{instruction::Instruction, pubkey::Pubkey};
use crate::accounts::*;
use crate::constants::*;
use crate::helpers::derive::*;
use crate::helpers::math::from_q32;
use crate::helpers::sol::{is_native_mint, wrap_sol_instructions, create_wsol_ata_instruction, unwrap_sol_instruction};
use crate::instructions::*;
use crate::types::*;
pub struct Hadron {
pub program_id: Pubkey,
pub pool_address: Pubkey,
pub addresses: PoolAddresses,
pub config: DecodedConfig,
pub oracle: DecodedMidpriceOracle,
pub curve_meta: DecodedCurveMeta,
pub curve_prefabs_data: Vec<u8>,
}
impl Hadron {
pub fn from_accounts(
pool_address: Pubkey,
config_data: &[u8],
oracle_data: &[u8],
curve_meta_data: &[u8],
curve_prefabs_data: &[u8],
) -> Result<Self, HadronSdkError> {
let program_id = HADRON_PROGRAM_ID;
let config = decode_config(config_data)?;
let oracle = decode_midprice_oracle(oracle_data)?;
let curve_meta = decode_curve_meta(curve_meta_data)?;
let addresses = derive_pool_addresses(
config.seed,
&config.mint_x,
&config.mint_y,
&config.token_program_x,
&config.token_program_y,
&program_id,
);
Ok(Self {
program_id,
pool_address,
addresses,
config,
oracle,
curve_meta,
curve_prefabs_data: curve_prefabs_data.to_vec(),
})
}
pub fn initialize(
payer: &Pubkey,
params: &InitializeParams,
program_id: &Pubkey,
) -> (Vec<Instruction>, Pubkey, u64) {
let seed = params.seed.unwrap_or_else(random_seed);
let max_prefab_slots = params.max_prefab_slots.unwrap_or(DEFAULT_MAX_PREFAB_SLOTS);
let max_curve_points = params.max_curve_points.unwrap_or(DEFAULT_MAX_CURVE_POINTS);
let (pool_address, _) =
get_config_address(seed, ¶ms.mint_x, ¶ms.mint_y, program_id);
let allocate_params = AllocateCurvePrefabsParams {
seed,
mint_x: params.mint_x,
mint_y: params.mint_y,
max_prefab_slots: Some(max_prefab_slots),
max_curve_points: Some(max_curve_points),
authority: params.authority,
};
let mut instructions = Vec::new();
let size = curve_prefabs_size(max_prefab_slots, max_curve_points);
let allocate_calls = if size > 10_240 { 2 } else { 1 };
for _ in 0..allocate_calls {
instructions.push(build_allocate_curve_prefabs(
payer,
&allocate_params,
program_id,
));
}
let init_params = InitializeParams {
seed: Some(seed),
mint_x: params.mint_x,
mint_y: params.mint_y,
authority: params.authority,
initial_midprice_q32: params.initial_midprice_q32,
oracle_mode: params.oracle_mode,
max_prefab_slots: params.max_prefab_slots,
max_curve_points: params.max_curve_points,
token_program_x: params.token_program_x,
token_program_y: params.token_program_y,
};
instructions.push(build_initialize(payer, &init_params, program_id));
(instructions, pool_address, seed)
}
pub fn get_midprice(&self) -> f64 {
from_q32(self.oracle.midprice_q32)
}
pub fn get_spread_factor(&self) -> f64 {
from_q32(self.oracle.spread_factor_q32)
}
pub fn get_spread_bps(&self) -> f64 {
(1.0 - from_q32(self.oracle.spread_factor_q32)) * 10_000.0
}
pub fn get_active_curve_slots(&self) -> ActiveCurveSlots {
ActiveCurveSlots {
price_bid: self.curve_meta.active_price_bid_slot,
price_ask: self.curve_meta.active_price_ask_slot,
risk_bid: self.curve_meta.active_risk_bid_slot,
risk_ask: self.curve_meta.active_risk_ask_slot,
}
}
pub fn get_active_curves(&self) -> Result<ActiveCurves, HadronSdkError> {
decode_active_curves(&self.curve_prefabs_data, &self.curve_meta)
}
pub fn get_curve_slot(
&self,
curve_type: CurveType,
slot: u8,
) -> Result<CurveSide, HadronSdkError> {
decode_curve_side(
&self.curve_prefabs_data,
curve_type,
slot,
self.curve_meta.max_prefab_slots,
self.curve_meta.max_curve_points,
)
}
pub fn deposit(&self, user: &Pubkey, params: &DepositParams) -> Instruction {
build_deposit(
user,
&self.pool_address,
&self.config.mint_x,
&self.config.mint_y,
&self.config.token_program_x,
&self.config.token_program_y,
params,
&self.program_id,
)
}
pub fn withdraw(&self, user: &Pubkey, params: &WithdrawParams) -> Instruction {
build_withdraw(
user,
&self.pool_address,
&self.config.mint_x,
&self.config.mint_y,
&self.config.token_program_x,
&self.config.token_program_y,
params,
&self.program_id,
)
}
pub fn swap(&self, user: &Pubkey, params: &SwapParams) -> Instruction {
build_swap_exact_in(
user,
&self.addresses,
&self.config.mint_x,
&self.config.mint_y,
&self.config.token_program_x,
&self.config.token_program_y,
params,
&self.program_id,
self.config.spread_config_initialized,
None,
)
}
pub fn set_curve(&self, authority: &Pubkey, params: &SetCurveParams) -> Instruction {
build_set_curve(
authority,
&self.addresses.curve_meta,
&self.addresses.curve_prefabs,
params,
&self.program_id,
)
}
pub fn set_risk_curve(
&self,
authority: &Pubkey,
params: &SetRiskCurveParams,
) -> Instruction {
build_set_risk_curve(
authority,
&self.addresses.curve_meta,
&self.addresses.curve_prefabs,
params,
&self.program_id,
)
}
pub fn set_risk_curve_absolute(
&self,
authority: &Pubkey,
params: &SetRiskCurveAbsoluteParams,
) -> Instruction {
build_set_risk_curve_absolute(
authority,
&self.addresses.curve_meta,
&self.addresses.curve_prefabs,
params,
&self.program_id,
)
}
pub fn set_curve_both(
&self,
authority: &Pubkey,
params: &SetCurveBothParams,
) -> [Instruction; 2] {
build_set_curve_both(
authority,
&self.addresses.curve_meta,
&self.addresses.curve_prefabs,
params,
&self.program_id,
)
}
pub fn set_risk_curve_both(
&self,
authority: &Pubkey,
params: &SetRiskCurveBothParams,
) -> [Instruction; 2] {
build_set_risk_curve_both(
authority,
&self.addresses.curve_meta,
&self.addresses.curve_prefabs,
params,
&self.program_id,
)
}
pub fn set_risk_curve_absolute_both(
&self,
authority: &Pubkey,
params: &SetRiskCurveAbsoluteBothParams,
) -> [Instruction; 2] {
build_set_risk_curve_absolute_both(
authority,
&self.addresses.curve_meta,
&self.addresses.curve_prefabs,
params,
&self.program_id,
)
}
pub fn set_quoting_authority(
&self,
authority: &Pubkey,
params: &SetQuotingAuthorityParams,
) -> Instruction {
build_set_quoting_authority(
authority,
&self.pool_address,
&self.addresses.midprice_oracle,
&self.addresses.curve_meta,
&self.addresses.curve_updates,
params,
&self.program_id,
)
}
pub fn update_midprice(
&self,
authority: &Pubkey,
params: &UpdateMidpriceParams,
) -> Instruction {
build_update_midprice(
authority,
&self.addresses.midprice_oracle,
params,
&self.program_id,
)
}
pub fn update_base_spread(
&self,
authority: &Pubkey,
params: &UpdateBaseSpreadParams,
) -> Instruction {
build_update_base_spread(
authority,
&self.addresses.midprice_oracle,
params,
&self.program_id,
)
}
pub fn update_midprice_and_base_spread(
&self,
authority: &Pubkey,
params: &UpdateMidpriceAndBaseSpreadParams,
) -> Instruction {
build_update_midprice_and_base_spread(
authority,
&self.addresses.midprice_oracle,
params,
&self.program_id,
)
}
pub fn switch_price_curve(
&self,
authority: &Pubkey,
params: &SwitchCurveParams,
) -> Instruction {
build_switch_price_curve(
authority,
&self.addresses.curve_meta,
params,
&self.program_id,
)
}
pub fn switch_risk_curve(
&self,
authority: &Pubkey,
params: &SwitchCurveParams,
) -> Instruction {
build_switch_risk_curve(
authority,
&self.addresses.curve_meta,
params,
&self.program_id,
)
}
pub fn submit_curve_updates(
&self,
authority: &Pubkey,
ops: &[CurveUpdateOp],
) -> Instruction {
build_submit_curve_updates(
authority,
&self.addresses.curve_updates,
ops,
&self.program_id,
)
}
pub fn apply_curve_updates(&self, authority: &Pubkey) -> Instruction {
build_apply_curve_updates(
authority,
&self.addresses.curve_meta,
&self.addresses.curve_prefabs,
&self.addresses.curve_updates,
&self.program_id,
)
}
pub fn nominate_authority(
&self,
authority: &Pubkey,
params: &NominateAuthorityParams,
) -> Instruction {
build_nominate_authority(authority, &self.pool_address, params, &self.program_id)
}
pub fn accept_authority(&self, new_authority: &Pubkey) -> Instruction {
build_accept_authority(new_authority, &self.pool_address, &self.program_id)
}
pub fn set_pool_state(
&self,
authority: &Pubkey,
params: &SetPoolStateParams,
) -> Instruction {
build_set_pool_state(authority, &self.pool_address, params, &self.program_id)
}
pub fn update_delta_staleness(
&self,
authority: &Pubkey,
params: &UpdateDeltaStalenessParams,
) -> Instruction {
build_update_delta_staleness(
authority,
&self.pool_address,
params,
&self.program_id,
)
}
pub fn initialize_spread_config(
&self,
payer: &Pubkey,
authority: &Pubkey,
params: &InitializeSpreadConfigParams,
) -> Instruction {
build_initialize_spread_config(
payer,
authority,
&self.pool_address,
params,
&self.program_id,
)
}
pub fn update_spread_config(
&self,
admin: &Pubkey,
params: &UpdateSpreadConfigParams,
) -> Instruction {
build_update_spread_config(admin, &self.pool_address, params, &self.program_id)
}
pub fn close_pool(
&self,
authority: &Pubkey,
authority_ata_x: Option<&Pubkey>,
authority_ata_y: Option<&Pubkey>,
) -> Instruction {
build_close_pool(
authority,
&self.pool_address,
&self.addresses.midprice_oracle,
&self.addresses.curve_meta,
&self.addresses.curve_prefabs,
&self.addresses.curve_updates,
&self.addresses.vault_x,
&self.addresses.vault_y,
&self.config.token_program_x,
&self.config.token_program_y,
self.config.spread_config_initialized,
authority_ata_x,
authority_ata_y,
&self.program_id,
)
}
pub fn deposit_sol(&self, user: &Pubkey, params: &DepositParams) -> Vec<Instruction> {
let mut ixs = Vec::new();
if is_native_mint(&self.config.mint_x) && params.amount_x > 0 {
ixs.extend(wrap_sol_instructions(user, params.amount_x));
}
if is_native_mint(&self.config.mint_y) && params.amount_y > 0 {
ixs.extend(wrap_sol_instructions(user, params.amount_y));
}
ixs.push(self.deposit(user, params));
ixs
}
pub fn withdraw_sol(&self, user: &Pubkey, params: &WithdrawParams) -> Vec<Instruction> {
let has_sol = is_native_mint(&self.config.mint_x) || is_native_mint(&self.config.mint_y);
let mut ixs = Vec::new();
if has_sol {
ixs.push(create_wsol_ata_instruction(user));
}
ixs.push(self.withdraw(user, params));
if has_sol {
ixs.push(unwrap_sol_instruction(user));
}
ixs
}
pub fn swap_sol(&self, user: &Pubkey, params: &SwapParams) -> Vec<Instruction> {
let mut ixs = Vec::new();
let (input_mint, output_mint) = if params.is_x {
(&self.config.mint_x, &self.config.mint_y)
} else {
(&self.config.mint_y, &self.config.mint_x)
};
if is_native_mint(input_mint) {
ixs.extend(wrap_sol_instructions(user, params.amount_in));
}
if is_native_mint(output_mint) {
ixs.push(create_wsol_ata_instruction(user));
}
ixs.push(self.swap(user, params));
if is_native_mint(output_mint) {
ixs.push(unwrap_sol_instruction(user));
}
ixs
}
}
#[derive(Debug, Clone)]
pub struct ActiveCurveSlots {
pub price_bid: u8,
pub price_ask: u8,
pub risk_bid: u8,
pub risk_ask: u8,
}
fn random_seed() -> u64 {
use std::time::{SystemTime, UNIX_EPOCH};
let duration = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default();
duration.as_nanos() as u64 ^ ((&duration as *const _ as u64).wrapping_mul(6364136223846793005))
}