use super::program_ids;
use super::utils::*;
use crate::core::events::*;
use solana_sdk::{pubkey::Pubkey, signature::Signature};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OrcaWhirlpoolInstruction {
InitializeConfig = 0,
InitializePool = 1,
InitializeTickArray = 2,
InitializeFeeTier = 3,
InitializeReward = 4,
SetRewardEmissions = 5,
OpenPosition = 6,
OpenPositionWithMetadata = 7,
IncreaseLiquidity = 8,
DecreaseLiquidity = 9,
UpdateFeesAndRewards = 10,
CollectFees = 11,
CollectReward = 12,
CollectProtocolFees = 13,
Swap = 14,
ClosePosition = 15,
SetDefaultFeeRate = 16,
SetDefaultProtocolFeeRate = 17,
SetFeeRate = 18,
SetProtocolFeeRate = 19,
SetFeeAuthority = 20,
SetCollectProtocolFeesAuthority = 21,
SetRewardAuthority = 22,
SetRewardAuthorityBySuperAuthority = 23,
SetRewardEmissionsSuperAuthority = 24,
TwoHopSwap = 25,
InitializePositionBundle = 26,
InitializePositionBundleWithMetadata = 27,
DeletePositionBundle = 28,
OpenBundledPosition = 29,
CloseBundledPosition = 30,
CollectFeesV2 = 31,
CollectProtocolFeesV2 = 32,
CollectRewardV2 = 33,
DecreaseLiquidityV2 = 34,
IncreaseLiquidityV2 = 35,
InitializePoolV2 = 36,
InitializeRewardV2 = 37,
SetRewardEmissionsV2 = 38,
SwapV2 = 39,
TwoHopSwapV2 = 40,
}
impl OrcaWhirlpoolInstruction {
pub fn from_discriminator(discriminator: &[u8; 8]) -> Option<Self> {
match discriminator {
&[208, 127, 21, 1, 194, 190, 196, 70] => Some(Self::InitializeConfig),
&[17, 43, 80, 74, 168, 202, 6, 113] => Some(Self::InitializePool),
&[214, 27, 15, 109, 164, 252, 221, 253] => Some(Self::InitializeTickArray),
&[183, 74, 156, 160, 112, 2, 42, 30] => Some(Self::InitializeFeeTier),
&[95, 135, 192, 196, 242, 129, 230, 68] => Some(Self::InitializeReward),
&[13, 197, 86, 168, 109, 176, 27, 244] => Some(Self::SetRewardEmissions),
&[87, 190, 72, 189, 204, 203, 226, 66] => Some(Self::OpenPosition),
&[78, 217, 28, 185, 88, 104, 255, 231] => Some(Self::OpenPositionWithMetadata),
&[46, 156, 243, 118, 13, 205, 251, 178] => Some(Self::IncreaseLiquidity),
&[160, 38, 208, 111, 104, 91, 44, 1] => Some(Self::DecreaseLiquidity),
&[173, 178, 66, 24, 33, 156, 204, 31] => Some(Self::UpdateFeesAndRewards),
&[164, 152, 207, 99, 30, 186, 19, 182] => Some(Self::CollectFees),
&[206, 68, 114, 253, 168, 177, 245, 180] => Some(Self::CollectReward),
&[22, 67, 23, 98, 150, 178, 70, 220] => Some(Self::CollectProtocolFees),
&[248, 198, 158, 145, 225, 117, 135, 200] => Some(Self::Swap),
&[123, 134, 81, 0, 49, 68, 98, 98] => Some(Self::ClosePosition),
&[43, 4, 237, 11, 26, 201, 30, 98] => Some(Self::SwapV2),
&[195, 96, 237, 108, 68, 162, 219, 230] => Some(Self::TwoHopSwap),
&[186, 143, 209, 29, 254, 2, 194, 117] => Some(Self::TwoHopSwapV2),
_ => None,
}
}
}
pub mod discriminators {
pub const INITIALIZE_CONFIG: [u8; 8] = [208, 127, 21, 1, 194, 190, 196, 70];
pub const INITIALIZE_POOL: [u8; 8] = [17, 43, 80, 74, 168, 202, 6, 113];
pub const INITIALIZE_TICK_ARRAY: [u8; 8] = [214, 27, 15, 109, 164, 252, 221, 253];
pub const INITIALIZE_FEE_TIER: [u8; 8] = [183, 74, 156, 160, 112, 2, 42, 30];
pub const INITIALIZE_REWARD: [u8; 8] = [95, 135, 192, 196, 242, 129, 230, 68];
pub const SET_REWARD_EMISSIONS: [u8; 8] = [13, 197, 86, 168, 109, 176, 27, 244];
pub const OPEN_POSITION: [u8; 8] = [87, 190, 72, 189, 204, 203, 226, 66];
pub const OPEN_POSITION_WITH_METADATA: [u8; 8] = [78, 217, 28, 185, 88, 104, 255, 231];
pub const INCREASE_LIQUIDITY: [u8; 8] = [46, 156, 243, 118, 13, 205, 251, 178];
pub const DECREASE_LIQUIDITY: [u8; 8] = [160, 38, 208, 111, 104, 91, 44, 1];
pub const UPDATE_FEES_AND_REWARDS: [u8; 8] = [173, 178, 66, 24, 33, 156, 204, 31];
pub const COLLECT_FEES: [u8; 8] = [164, 152, 207, 99, 30, 186, 19, 182];
pub const COLLECT_REWARD: [u8; 8] = [206, 68, 114, 253, 168, 177, 245, 180];
pub const COLLECT_PROTOCOL_FEES: [u8; 8] = [22, 67, 23, 98, 150, 178, 70, 220];
pub const SWAP: [u8; 8] = [248, 198, 158, 145, 225, 117, 135, 200];
pub const CLOSE_POSITION: [u8; 8] = [123, 134, 81, 0, 49, 68, 98, 98];
pub const SWAP_V2: [u8; 8] = [43, 4, 237, 11, 26, 201, 30, 98];
pub const TWO_HOP_SWAP: [u8; 8] = [195, 96, 237, 108, 68, 162, 219, 230];
pub const TWO_HOP_SWAP_V2: [u8; 8] = [186, 143, 209, 29, 254, 2, 194, 117];
}
pub const PROGRAM_ID_PUBKEY: Pubkey = program_ids::ORCA_WHIRLPOOL_PROGRAM_ID;
pub fn parse_instruction(
instruction_data: &[u8],
accounts: &[Pubkey],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
) -> Option<DexEvent> {
if instruction_data.len() < 8 {
return None;
}
let discriminator: [u8; 8] = instruction_data[0..8].try_into().ok()?;
let instruction_type = OrcaWhirlpoolInstruction::from_discriminator(&discriminator)?;
let data = &instruction_data[8..];
match instruction_type {
OrcaWhirlpoolInstruction::Swap | OrcaWhirlpoolInstruction::SwapV2 => {
parse_swap_instruction(data, accounts, signature, slot, tx_index, block_time_us)
}
OrcaWhirlpoolInstruction::IncreaseLiquidity
| OrcaWhirlpoolInstruction::IncreaseLiquidityV2 => parse_increase_liquidity_instruction(
data,
accounts,
signature,
slot,
tx_index,
block_time_us,
),
OrcaWhirlpoolInstruction::DecreaseLiquidity
| OrcaWhirlpoolInstruction::DecreaseLiquidityV2 => parse_decrease_liquidity_instruction(
data,
accounts,
signature,
slot,
tx_index,
block_time_us,
),
OrcaWhirlpoolInstruction::InitializePool | OrcaWhirlpoolInstruction::InitializePoolV2 => {
parse_initialize_pool_instruction(
data,
accounts,
signature,
slot,
tx_index,
block_time_us,
)
}
_ => None, }
}
fn parse_swap_instruction(
data: &[u8],
accounts: &[Pubkey],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
) -> Option<DexEvent> {
let mut offset = 0;
let amount = read_u64_le(data, offset)?;
offset += 8;
let other_amount_threshold = read_u64_le(data, offset)?;
offset += 8;
let sqrt_price_limit = read_u128_le(data, offset)?;
offset += 16;
let amount_specified_is_input = read_bool(data, offset)?;
offset += 1;
let a_to_b = read_bool(data, offset)?;
let whirlpool = get_account(accounts, 1)?;
let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, whirlpool);
Some(DexEvent::OrcaWhirlpoolSwap(OrcaWhirlpoolSwapEvent {
metadata,
whirlpool,
a_to_b,
pre_sqrt_price: sqrt_price_limit, post_sqrt_price: 0, input_amount: if amount_specified_is_input { amount } else { 0 },
output_amount: if !amount_specified_is_input {
amount
} else {
other_amount_threshold },
input_transfer_fee: 0, output_transfer_fee: 0, lp_fee: 0, protocol_fee: 0,
}))
}
fn parse_increase_liquidity_instruction(
data: &[u8],
accounts: &[Pubkey],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
) -> Option<DexEvent> {
let mut offset = 0;
let liquidity_amount = read_u128_le(data, offset)?;
offset += 16;
let token_max_a = read_u64_le(data, offset)?;
offset += 8;
let token_max_b = read_u64_le(data, offset)?;
let whirlpool = get_account(accounts, 1)?;
let position = get_account(accounts, 3)?;
let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, whirlpool);
Some(DexEvent::OrcaWhirlpoolLiquidityIncreased(OrcaWhirlpoolLiquidityIncreasedEvent {
metadata,
whirlpool,
position,
tick_lower_index: 0, tick_upper_index: 0, liquidity: liquidity_amount,
token_a_amount: token_max_a, token_b_amount: token_max_b, token_a_transfer_fee: 0, token_b_transfer_fee: 0, }))
}
fn parse_decrease_liquidity_instruction(
data: &[u8],
accounts: &[Pubkey],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
) -> Option<DexEvent> {
let mut offset = 0;
let liquidity_amount = read_u128_le(data, offset)?;
offset += 16;
let token_min_a = read_u64_le(data, offset)?;
offset += 8;
let token_min_b = read_u64_le(data, offset)?;
let whirlpool = get_account(accounts, 1)?;
let position = get_account(accounts, 3)?;
let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, whirlpool);
Some(DexEvent::OrcaWhirlpoolLiquidityDecreased(OrcaWhirlpoolLiquidityDecreasedEvent {
metadata,
whirlpool,
position,
tick_lower_index: 0, tick_upper_index: 0, liquidity: liquidity_amount,
token_a_amount: token_min_a, token_b_amount: token_min_b, token_a_transfer_fee: 0, token_b_transfer_fee: 0, }))
}
fn parse_initialize_pool_instruction(
data: &[u8],
accounts: &[Pubkey],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
) -> Option<DexEvent> {
let mut offset = 0;
let tick_spacing = read_u16_le(data, offset)?;
offset += 2;
let initial_sqrt_price = read_u128_le(data, offset)?;
let whirlpool = get_account(accounts, 1)?;
let whirlpools_config = get_account(accounts, 2)?;
let token_mint_a = get_account(accounts, 3)?;
let token_mint_b = get_account(accounts, 4)?;
let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, whirlpool);
Some(DexEvent::OrcaWhirlpoolPoolInitialized(OrcaWhirlpoolPoolInitializedEvent {
metadata,
whirlpool,
whirlpools_config,
token_mint_a,
token_mint_b,
tick_spacing,
token_program_a: get_account(accounts, 8).unwrap_or_default(),
token_program_b: get_account(accounts, 9).unwrap_or_default(),
decimals_a: 0, decimals_b: 0, initial_sqrt_price,
}))
}