0.7.27 +Refactor

This commit is contained in:
2026-05-10 00:33:01 +02:00
parent cb2e8e7096
commit 1f0137b9de
261 changed files with 12308 additions and 8928 deletions

View File

@@ -5,29 +5,24 @@
//! This module decodes Raydium constant product swap instructions from
//! already-projected Solana transaction instructions.
/// Raydium CPMM mainnet program id.
pub const KB_RAYDIUM_CPMM_PROGRAM_ID: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C";
/// Raydium CPMM `swap_base_input` discriminator.
const KB_RAYDIUM_CPMM_SWAP_BASE_INPUT_DISCRIMINATOR: [u8; 8] =
[143, 190, 90, 218, 196, 30, 51, 222];
const RAYDIUM_CPMM_SWAP_BASE_INPUT_DISCRIMINATOR: [u8; 8] = [143, 190, 90, 218, 196, 30, 51, 222];
/// Raydium CPMM `swap_base_output` discriminator.
const KB_RAYDIUM_CPMM_SWAP_BASE_OUTPUT_DISCRIMINATOR: [u8; 8] =
[55, 217, 98, 86, 163, 74, 180, 173];
const RAYDIUM_CPMM_SWAP_BASE_OUTPUT_DISCRIMINATOR: [u8; 8] = [55, 217, 98, 86, 163, 74, 180, 173];
/// Raydium CPMM decoded event.
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)]
pub enum KbRaydiumCpmmDecodedEvent {
pub enum RaydiumCpmmDecodedEvent {
/// Swap where the user fixes the input amount.
SwapBaseInput(KbRaydiumCpmmSwapDecoded),
SwapBaseInput(RaydiumCpmmSwapDecoded),
/// Swap where the user fixes the output amount.
SwapBaseOutput(KbRaydiumCpmmSwapDecoded),
SwapBaseOutput(RaydiumCpmmSwapDecoded),
}
/// Raydium CPMM swap mode.
#[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)]
pub enum KbRaydiumCpmmSwapMode {
pub enum RaydiumCpmmSwapMode {
/// Fixed input swap.
BaseInput,
/// Fixed output swap.
@@ -36,9 +31,9 @@ pub enum KbRaydiumCpmmSwapMode {
/// Raydium CPMM decoded swap.
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)]
pub struct KbRaydiumCpmmSwapDecoded {
pub struct RaydiumCpmmSwapDecoded {
/// Instruction mode.
pub swap_mode: KbRaydiumCpmmSwapMode,
pub swap_mode: RaydiumCpmmSwapMode,
/// User or payer account.
pub payer: std::string::String,
/// Raydium authority account.
@@ -87,50 +82,50 @@ pub struct KbRaydiumCpmmSwapDecoded {
pub amount_out_raw: std::option::Option<std::string::String>,
}
impl KbRaydiumCpmmDecodedEvent {
impl RaydiumCpmmDecodedEvent {
/// Returns the storage event kind.
pub fn event_kind(&self) -> &'static str {
match self {
KbRaydiumCpmmDecodedEvent::SwapBaseInput(_) => return "raydium_cpmm.swap_base_input",
KbRaydiumCpmmDecodedEvent::SwapBaseOutput(_) => return "raydium_cpmm.swap_base_output",
RaydiumCpmmDecodedEvent::SwapBaseInput(_) => return "raydium_cpmm.swap_base_input",
RaydiumCpmmDecodedEvent::SwapBaseOutput(_) => return "raydium_cpmm.swap_base_output",
}
}
/// Returns the pool account.
pub fn pool_account(&self) -> &str {
match self {
KbRaydiumCpmmDecodedEvent::SwapBaseInput(event) => return event.pool_state.as_str(),
KbRaydiumCpmmDecodedEvent::SwapBaseOutput(event) => return event.pool_state.as_str(),
RaydiumCpmmDecodedEvent::SwapBaseInput(event) => return event.pool_state.as_str(),
RaydiumCpmmDecodedEvent::SwapBaseOutput(event) => return event.pool_state.as_str(),
}
}
/// Returns the normalized base mint.
pub fn base_mint(&self) -> &str {
match self {
KbRaydiumCpmmDecodedEvent::SwapBaseInput(event) => return event.base_mint.as_str(),
KbRaydiumCpmmDecodedEvent::SwapBaseOutput(event) => return event.base_mint.as_str(),
RaydiumCpmmDecodedEvent::SwapBaseInput(event) => return event.base_mint.as_str(),
RaydiumCpmmDecodedEvent::SwapBaseOutput(event) => return event.base_mint.as_str(),
}
}
/// Returns the normalized quote mint.
pub fn quote_mint(&self) -> &str {
match self {
KbRaydiumCpmmDecodedEvent::SwapBaseInput(event) => return event.quote_mint.as_str(),
KbRaydiumCpmmDecodedEvent::SwapBaseOutput(event) => return event.quote_mint.as_str(),
RaydiumCpmmDecodedEvent::SwapBaseInput(event) => return event.quote_mint.as_str(),
RaydiumCpmmDecodedEvent::SwapBaseOutput(event) => return event.quote_mint.as_str(),
}
}
/// Converts the decoded event to JSON payload.
pub fn to_payload_json(&self) -> std::option::Option<std::string::String> {
match self {
crate::KbRaydiumCpmmDecodedEvent::SwapBaseInput(event) => {
crate::RaydiumCpmmDecodedEvent::SwapBaseInput(event) => {
let result = serde_json::to_string(event);
match result {
Ok(payload) => return Some(payload),
Err(_) => return None,
}
},
crate::KbRaydiumCpmmDecodedEvent::SwapBaseOutput(event) => {
crate::RaydiumCpmmDecodedEvent::SwapBaseOutput(event) => {
let result = serde_json::to_string(event);
match result {
Ok(payload) => return Some(payload),
@@ -142,15 +137,15 @@ impl KbRaydiumCpmmDecodedEvent {
}
/// Decodes one Raydium CPMM instruction from projected instruction fields.
pub fn kb_decode_raydium_cpmm_instruction(
pub fn decode_raydium_cpmm_instruction(
accounts_json: &str,
data_json: &str,
) -> std::vec::Vec<KbRaydiumCpmmDecodedEvent> {
let accounts = match kb_parse_accounts_json(accounts_json) {
) -> std::vec::Vec<RaydiumCpmmDecodedEvent> {
let accounts = match parse_accounts_json(accounts_json) {
Some(accounts) => accounts,
None => return std::vec::Vec::new(),
};
let data_base58 = match kb_parse_data_json_as_base58(data_json) {
let data_base58 = match parse_data_json_as_base58(data_json) {
Some(data_base58) => data_base58,
None => return std::vec::Vec::new(),
};
@@ -162,17 +157,17 @@ pub fn kb_decode_raydium_cpmm_instruction(
return std::vec::Vec::new();
}
let discriminator = [data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]];
if discriminator == KB_RAYDIUM_CPMM_SWAP_BASE_INPUT_DISCRIMINATOR {
let amount_in = match kb_read_u64_le(data.as_slice(), 8) {
if discriminator == RAYDIUM_CPMM_SWAP_BASE_INPUT_DISCRIMINATOR {
let amount_in = match read_u64_le(data.as_slice(), 8) {
Some(value) => value,
None => return std::vec::Vec::new(),
};
let minimum_amount_out = match kb_read_u64_le(data.as_slice(), 16) {
let minimum_amount_out = match read_u64_le(data.as_slice(), 16) {
Some(value) => value,
None => return std::vec::Vec::new(),
};
let swap = match kb_build_raydium_cpmm_swap(
KbRaydiumCpmmSwapMode::BaseInput,
let swap = match build_raydium_cpmm_swap(
RaydiumCpmmSwapMode::BaseInput,
accounts.as_slice(),
Some(amount_in.to_string()),
Some(minimum_amount_out.to_string()),
@@ -182,19 +177,19 @@ pub fn kb_decode_raydium_cpmm_instruction(
Some(swap) => swap,
None => return std::vec::Vec::new(),
};
return vec![KbRaydiumCpmmDecodedEvent::SwapBaseInput(swap)];
return vec![RaydiumCpmmDecodedEvent::SwapBaseInput(swap)];
}
if discriminator == KB_RAYDIUM_CPMM_SWAP_BASE_OUTPUT_DISCRIMINATOR {
let max_amount_in = match kb_read_u64_le(data.as_slice(), 8) {
if discriminator == RAYDIUM_CPMM_SWAP_BASE_OUTPUT_DISCRIMINATOR {
let max_amount_in = match read_u64_le(data.as_slice(), 8) {
Some(value) => value,
None => return std::vec::Vec::new(),
};
let amount_out = match kb_read_u64_le(data.as_slice(), 16) {
let amount_out = match read_u64_le(data.as_slice(), 16) {
Some(value) => value,
None => return std::vec::Vec::new(),
};
let swap = match kb_build_raydium_cpmm_swap(
KbRaydiumCpmmSwapMode::BaseOutput,
let swap = match build_raydium_cpmm_swap(
RaydiumCpmmSwapMode::BaseOutput,
accounts.as_slice(),
None,
None,
@@ -204,19 +199,19 @@ pub fn kb_decode_raydium_cpmm_instruction(
Some(swap) => swap,
None => return std::vec::Vec::new(),
};
return vec![KbRaydiumCpmmDecodedEvent::SwapBaseOutput(swap)];
return vec![RaydiumCpmmDecodedEvent::SwapBaseOutput(swap)];
}
return std::vec::Vec::new();
}
fn kb_build_raydium_cpmm_swap(
swap_mode: KbRaydiumCpmmSwapMode,
fn build_raydium_cpmm_swap(
swap_mode: RaydiumCpmmSwapMode,
accounts: &[std::string::String],
amount_in_raw: std::option::Option<std::string::String>,
minimum_amount_out_raw: std::option::Option<std::string::String>,
max_amount_in_raw: std::option::Option<std::string::String>,
amount_out_raw: std::option::Option<std::string::String>,
) -> std::option::Option<KbRaydiumCpmmSwapDecoded> {
) -> std::option::Option<RaydiumCpmmSwapDecoded> {
if accounts.len() < 13 {
return None;
}
@@ -224,7 +219,7 @@ fn kb_build_raydium_cpmm_swap(
let output_token_mint = accounts[11].clone();
let input_vault = accounts[6].clone();
let output_vault = accounts[7].clone();
let normalized = kb_normalize_raydium_cpmm_pair(
let normalized = normalize_raydium_cpmm_pair(
input_token_mint.as_str(),
output_token_mint.as_str(),
input_vault.as_str(),
@@ -232,7 +227,7 @@ fn kb_build_raydium_cpmm_swap(
);
let input_is_base = normalized.input_is_base;
let trade_side = if input_is_base { "sell".to_string() } else { "buy".to_string() };
return Some(KbRaydiumCpmmSwapDecoded {
return Some(RaydiumCpmmSwapDecoded {
swap_mode,
payer: accounts[0].clone(),
authority: accounts[1].clone(),
@@ -260,7 +255,7 @@ fn kb_build_raydium_cpmm_swap(
});
}
struct KbRaydiumCpmmNormalizedPair {
struct RaydiumCpmmNormalizedPair {
base_mint: std::string::String,
quote_mint: std::string::String,
base_vault: std::string::String,
@@ -268,14 +263,14 @@ struct KbRaydiumCpmmNormalizedPair {
input_is_base: bool,
}
fn kb_normalize_raydium_cpmm_pair(
fn normalize_raydium_cpmm_pair(
input_mint: &str,
output_mint: &str,
input_vault: &str,
output_vault: &str,
) -> KbRaydiumCpmmNormalizedPair {
if kb_is_quote_mint(output_mint) && !kb_is_quote_mint(input_mint) {
return KbRaydiumCpmmNormalizedPair {
) -> RaydiumCpmmNormalizedPair {
if is_quote_mint(output_mint) && !is_quote_mint(input_mint) {
return RaydiumCpmmNormalizedPair {
base_mint: input_mint.to_string(),
quote_mint: output_mint.to_string(),
base_vault: input_vault.to_string(),
@@ -283,8 +278,8 @@ fn kb_normalize_raydium_cpmm_pair(
input_is_base: true,
};
}
if kb_is_quote_mint(input_mint) && !kb_is_quote_mint(output_mint) {
return KbRaydiumCpmmNormalizedPair {
if is_quote_mint(input_mint) && !is_quote_mint(output_mint) {
return RaydiumCpmmNormalizedPair {
base_mint: output_mint.to_string(),
quote_mint: input_mint.to_string(),
base_vault: output_vault.to_string(),
@@ -293,7 +288,7 @@ fn kb_normalize_raydium_cpmm_pair(
};
}
if input_mint <= output_mint {
return KbRaydiumCpmmNormalizedPair {
return RaydiumCpmmNormalizedPair {
base_mint: input_mint.to_string(),
quote_mint: output_mint.to_string(),
base_vault: input_vault.to_string(),
@@ -301,7 +296,7 @@ fn kb_normalize_raydium_cpmm_pair(
input_is_base: true,
};
}
return KbRaydiumCpmmNormalizedPair {
return RaydiumCpmmNormalizedPair {
base_mint: output_mint.to_string(),
quote_mint: input_mint.to_string(),
base_vault: output_vault.to_string(),
@@ -310,8 +305,8 @@ fn kb_normalize_raydium_cpmm_pair(
};
}
fn kb_is_quote_mint(mint: &str) -> bool {
if mint == "So11111111111111111111111111111111111111112" {
fn is_quote_mint(mint: &str) -> bool {
if mint == crate::WSOL_MINT_ID {
return true;
}
if mint == "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" {
@@ -326,7 +321,7 @@ fn kb_is_quote_mint(mint: &str) -> bool {
return false;
}
fn kb_parse_accounts_json(
fn parse_accounts_json(
accounts_json: &str,
) -> std::option::Option<std::vec::Vec<std::string::String>> {
let result = serde_json::from_str::<std::vec::Vec<std::string::String>>(accounts_json);
@@ -336,7 +331,7 @@ fn kb_parse_accounts_json(
}
}
fn kb_parse_data_json_as_base58(data_json: &str) -> std::option::Option<std::string::String> {
fn parse_data_json_as_base58(data_json: &str) -> std::option::Option<std::string::String> {
let json_string_result = serde_json::from_str::<std::string::String>(data_json);
if let Ok(value) = json_string_result {
return Some(value);
@@ -352,7 +347,7 @@ fn kb_parse_data_json_as_base58(data_json: &str) -> std::option::Option<std::str
return Some(without_quotes.to_string());
}
fn kb_read_u64_le(data: &[u8], offset: usize) -> std::option::Option<u64> {
fn read_u64_le(data: &[u8], offset: usize) -> std::option::Option<u64> {
if data.len() < offset + 8 {
return None;
}
@@ -388,13 +383,13 @@ mod tests {
"USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB",
"9fzQ17bEnqJSsyHL5CodJqY2sdjZJBQCQLbgFZ1BYTWn"
]"#;
let events = crate::kb_decode_raydium_cpmm_instruction(
let events = crate::decode_raydium_cpmm_instruction(
accounts_json,
r#""E73fXHPWvSRF4Q2ZQFvPoeJBVGDUEMmxB""#,
);
assert_eq!(events.len(), 1);
match &events[0] {
crate::KbRaydiumCpmmDecodedEvent::SwapBaseInput(event) => {
crate::RaydiumCpmmDecodedEvent::SwapBaseInput(event) => {
assert_eq!(event.pool_state, "2ErXvV1tKtG3wiHqdofDjMou7Jusdsfasvfh8HrTj5oV");
assert_eq!(event.base_mint, "Pf9aSicGu3g6tTUBqrRbjNsGape9HopibspX5KSbonk");
assert_eq!(event.quote_mint, "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB");
@@ -425,13 +420,13 @@ mod tests {
"Pf9aSicGu3g6tTUBqrRbjNsGape9HopibspX5KSbonk",
"9fzQ17bEnqJSsyHL5CodJqY2sdjZJBQCQLbgFZ1BYTWn"
]"#;
let events = crate::kb_decode_raydium_cpmm_instruction(
let events = crate::decode_raydium_cpmm_instruction(
accounts_json,
r#""66JafaVu7KNEtTngqQyD7ETVurF3rxJ47""#,
);
assert_eq!(events.len(), 1);
match &events[0] {
crate::KbRaydiumCpmmDecodedEvent::SwapBaseOutput(event) => {
crate::RaydiumCpmmDecodedEvent::SwapBaseOutput(event) => {
assert_eq!(event.pool_state, "2ErXvV1tKtG3wiHqdofDjMou7Jusdsfasvfh8HrTj5oV");
assert_eq!(event.base_mint, "Pf9aSicGu3g6tTUBqrRbjNsGape9HopibspX5KSbonk");
assert_eq!(event.quote_mint, "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB");