use solana_sdk::pubkey::Pubkey;
use crate::constants::*;
use crate::types::*;
fn read_pubkey(data: &[u8], offset: usize) -> Pubkey {
Pubkey::new_from_array(data[offset..offset + 32].try_into().unwrap())
}
fn read_u64_le(data: &[u8], offset: usize) -> u64 {
u64::from_le_bytes(data[offset..offset + 8].try_into().unwrap())
}
fn read_u32_le(data: &[u8], offset: usize) -> u32 {
u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap())
}
fn read_u16_le(data: &[u8], offset: usize) -> u16 {
u16::from_le_bytes(data[offset..offset + 2].try_into().unwrap())
}
pub fn decode_config(data: &[u8]) -> Result<DecodedConfig, HadronSdkError> {
if data.len() < CONFIG_SIZE {
return Err(HadronSdkError::DataTooShort {
expected: CONFIG_SIZE,
actual: data.len(),
});
}
let mut offset = 0;
let state = PoolState::try_from(data[offset])?;
offset += 1;
let seed = read_u64_le(data, offset);
offset += 8;
let authority = read_pubkey(data, offset);
offset += 32;
let mint_x = read_pubkey(data, offset);
offset += 32;
let mint_y = read_pubkey(data, offset);
offset += 32;
let config_bump = data[offset];
offset += 1;
let curve_meta = read_pubkey(data, offset);
offset += 32;
let spread_config_initialized = data[offset] != 0;
offset += 1;
let delta_staleness = data[offset];
offset += 1;
let oracle_mode = OracleMode::try_from(data[offset])?;
offset += 1;
let has_pool_fee = data[offset] != 0;
offset += 1;
offset += 2;
let pending_authority = read_pubkey(data, offset);
offset += 32;
let nomination_expiry = read_u64_le(data, offset);
offset += 8;
let token_program_x = read_pubkey(data, offset);
offset += 32;
let token_program_y = read_pubkey(data, offset);
let _ = offset;
Ok(DecodedConfig {
state,
seed,
authority,
mint_x,
mint_y,
config_bump,
curve_meta,
spread_config_initialized,
delta_staleness,
oracle_mode,
has_pool_fee,
pending_authority,
nomination_expiry,
token_program_x,
token_program_y,
})
}
pub fn decode_midprice_oracle(data: &[u8]) -> Result<DecodedMidpriceOracle, HadronSdkError> {
if data.len() < MIDPRICE_ORACLE_SIZE {
return Err(HadronSdkError::DataTooShort {
expected: MIDPRICE_ORACLE_SIZE,
actual: data.len(),
});
}
Ok(DecodedMidpriceOracle {
authority: read_pubkey(data, 0),
sequence: read_u64_le(data, 32),
midprice_q32: read_u64_le(data, 40),
spread_factor_q32: read_u64_le(data, 48),
last_update_slot: read_u64_le(data, 56),
})
}
pub fn decode_curve_meta(data: &[u8]) -> Result<DecodedCurveMeta, HadronSdkError> {
if data.len() < CURVE_META_SIZE {
return Err(HadronSdkError::DataTooShort {
expected: CURVE_META_SIZE,
actual: data.len(),
});
}
let mut initialized_slots = [0u8; 8];
initialized_slots.copy_from_slice(&data[36..44]);
Ok(DecodedCurveMeta {
authority: read_pubkey(data, 0),
active_price_bid_slot: data[32],
active_price_ask_slot: data[33],
active_risk_bid_slot: data[34],
active_risk_ask_slot: data[35],
initialized_slots,
max_prefab_slots: data[44],
max_curve_points: data[45],
})
}
pub fn is_slot_initialized(
initialized_slots: &[u8; 8],
curve_type: CurveType,
slot: u8,
max_slots: u8,
) -> bool {
let bit_index = (curve_type as u32) * (max_slots as u32) + (slot as u32);
let byte_index = (bit_index / 8) as usize;
let bit_offset = bit_index % 8;
if byte_index >= 8 {
return false;
}
(initialized_slots[byte_index] & (1 << bit_offset)) != 0
}
pub fn decode_fee_config(data: &[u8]) -> Result<DecodedFeeConfig, HadronSdkError> {
if data.len() < FEE_CONFIG_SIZE {
return Err(HadronSdkError::DataTooShort {
expected: FEE_CONFIG_SIZE,
actual: data.len(),
});
}
Ok(DecodedFeeConfig {
initialized: data[0] != 0,
fee_ppm: read_u32_le(data, 1),
bump: data[5],
fee_admin: read_pubkey(data, 8),
fee_recipient: read_pubkey(data, 40),
})
}
const SPREAD_TRIGGER_LEN: usize = 40;
pub fn decode_spread_config(data: &[u8]) -> Result<DecodedSpreadConfig, HadronSdkError> {
if data.len() < 72 {
return Err(HadronSdkError::DataTooShort {
expected: 72,
actual: data.len(),
});
}
let initialized = data[0] != 0;
let bump = data[1];
let num_triggers = data[2];
let admin = read_pubkey(data, 8);
let config = read_pubkey(data, 40);
let mut triggers = Vec::with_capacity(num_triggers as usize);
let triggers_start = 72;
for i in 0..num_triggers as usize {
let off = triggers_start + i * SPREAD_TRIGGER_LEN;
if off + SPREAD_TRIGGER_LEN > data.len() {
break;
}
triggers.push(SpreadTriggerInput {
account: read_pubkey(data, off),
spread_bps: read_u16_le(data, off + 32),
});
}
Ok(DecodedSpreadConfig {
initialized,
bump,
num_triggers,
admin,
config,
triggers,
})
}
pub fn decode_curve_updates(data: &[u8]) -> Result<DecodedCurveUpdates, HadronSdkError> {
if data.len() < CURVE_UPDATES_SIZE {
return Err(HadronSdkError::DataTooShort {
expected: CURVE_UPDATES_SIZE,
actual: data.len(),
});
}
let authority = read_pubkey(data, 0);
let curve_meta = read_pubkey(data, 32);
let num_ops = data[64];
let ops_start = 66;
let mut ops = Vec::with_capacity(num_ops.min(MAX_CURVE_UPDATE_OPS) as usize);
for i in 0..num_ops.min(MAX_CURVE_UPDATE_OPS) as usize {
let off = ops_start + i * CURVE_UPDATE_OP_SIZE;
ops.push(CurveUpdateOp {
curve_type: CurveType::try_from(data[off])?,
op_kind: CurveUpdateOpKind::try_from(data[off + 1])?,
point_index: data[off + 2],
interpolation: Interpolation::try_from(data[off + 3])?,
amount_in: read_u64_le(data, off + 4),
price_factor_q32: read_u64_le(data, off + 12),
params: data[off + 20..off + 24].try_into().unwrap(),
});
}
Ok(DecodedCurveUpdates {
authority,
curve_meta,
num_ops,
ops,
})
}
pub fn decode_curve_side(
data: &[u8],
curve_type: CurveType,
slot: u8,
max_slots: u8,
max_points: u8,
) -> Result<CurveSide, HadronSdkError> {
let side_size = CURVE_SIDE_HEADER + (max_points as usize) * CURVE_POINT_LEN;
let offset =
32 + ((curve_type as usize) * (max_slots as usize) + (slot as usize)) * side_size;
let end = offset + side_size;
if data.len() < end {
return Err(HadronSdkError::DataTooShort {
expected: end,
actual: data.len(),
});
}
let num_points = data[offset];
let default_interpolation = Interpolation::try_from(data[offset + 1])?;
let x_mode = CurveXMode::try_from(data[offset + 2])?;
let risk_mode = RiskMode::try_from(data[offset + 3])?;
let points_start = offset + CURVE_SIDE_HEADER;
let n = (num_points as usize).min(max_points as usize);
let mut points = Vec::with_capacity(n);
for i in 0..n {
let p_off = points_start + i * CURVE_POINT_LEN;
points.push(DecodedCurvePoint {
amount_in: read_u64_le(data, p_off),
price_factor_q32: read_u64_le(data, p_off + 8),
interpolation: Interpolation::try_from(data[p_off + 16])?,
params: data[p_off + 17..p_off + 21].try_into().unwrap(),
});
}
Ok(CurveSide {
num_points,
default_interpolation,
x_mode,
risk_mode,
points,
})
}
pub fn decode_active_curves(
prefabs_data: &[u8],
meta: &DecodedCurveMeta,
) -> Result<ActiveCurves, HadronSdkError> {
let ms = meta.max_prefab_slots;
let mp = meta.max_curve_points;
Ok(ActiveCurves {
price_bid: decode_curve_side(
prefabs_data,
CurveType::PriceBid,
meta.active_price_bid_slot,
ms,
mp,
)?,
price_ask: decode_curve_side(
prefabs_data,
CurveType::PriceAsk,
meta.active_price_ask_slot,
ms,
mp,
)?,
risk_bid: decode_curve_side(
prefabs_data,
CurveType::RiskBid,
meta.active_risk_bid_slot,
ms,
mp,
)?,
risk_ask: decode_curve_side(
prefabs_data,
CurveType::RiskAsk,
meta.active_risk_ask_slot,
ms,
mp,
)?,
})
}