0.7.24-pre.2
This commit is contained in:
@@ -5,6 +5,9 @@
|
||||
/// Pump.fun program id.
|
||||
pub const KB_PUMP_FUN_PROGRAM_ID: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
|
||||
|
||||
const KB_PUMP_FUN_BUY_DISCRIMINATOR: [u8; 8] = [102, 6, 61, 18, 1, 218, 235, 234];
|
||||
const KB_PUMP_FUN_SELL_DISCRIMINATOR: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173];
|
||||
|
||||
/// Decoded Pump.fun `create_v2` token event.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct KbPumpFunCreateV2TokenDecoded {
|
||||
@@ -28,11 +31,46 @@ pub struct KbPumpFunCreateV2TokenDecoded {
|
||||
pub payload_json: serde_json::Value,
|
||||
}
|
||||
|
||||
/// Decoded Pump.fun bonding-curve trade event.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct KbPumpFunTradeDecoded {
|
||||
/// Parent transaction id.
|
||||
pub transaction_id: i64,
|
||||
/// Parent instruction id.
|
||||
pub instruction_id: i64,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Program id.
|
||||
pub program_id: std::string::String,
|
||||
/// Trade side.
|
||||
pub trade_side: crate::KbSwapTradeSide,
|
||||
/// Token mint.
|
||||
pub mint: std::option::Option<std::string::String>,
|
||||
/// Bonding curve account.
|
||||
pub bonding_curve: std::option::Option<std::string::String>,
|
||||
/// Associated bonding curve token account.
|
||||
pub associated_bonding_curve: std::option::Option<std::string::String>,
|
||||
/// User token account.
|
||||
pub associated_user: std::option::Option<std::string::String>,
|
||||
/// User wallet account.
|
||||
pub user: std::option::Option<std::string::String>,
|
||||
/// Decoded instruction amount, when available.
|
||||
pub amount_raw: std::option::Option<std::string::String>,
|
||||
/// Decoded SOL limit/threshold argument, when available.
|
||||
pub sol_limit_raw: std::option::Option<std::string::String>,
|
||||
/// Decoded payload.
|
||||
pub payload_json: serde_json::Value,
|
||||
}
|
||||
|
||||
/// Decoded Pump.fun event.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub enum KbPumpFunDecodedEvent {
|
||||
/// `create_v2` token creation.
|
||||
CreateV2Token(KbPumpFunCreateV2TokenDecoded),
|
||||
/// Buy trade.
|
||||
BuyTrade(KbPumpFunTradeDecoded),
|
||||
/// Sell trade.
|
||||
SellTrade(KbPumpFunTradeDecoded),
|
||||
}
|
||||
|
||||
/// Pump.fun decoder.
|
||||
@@ -61,6 +99,9 @@ impl KbPumpFunDecoder {
|
||||
)));
|
||||
}
|
||||
};
|
||||
if transaction.err_json.is_some() {
|
||||
return Ok(std::vec::Vec::new());
|
||||
}
|
||||
let transaction_json_result =
|
||||
serde_json::from_str::<serde_json::Value>(transaction.transaction_json.as_str());
|
||||
let transaction_json = match transaction_json_result {
|
||||
@@ -88,9 +129,6 @@ impl KbPumpFunDecoder {
|
||||
if program_id.as_str() != crate::KB_PUMP_FUN_PROGRAM_ID {
|
||||
continue;
|
||||
}
|
||||
if !has_create_v2_log {
|
||||
continue;
|
||||
}
|
||||
let instruction_id_option = instruction.id;
|
||||
let instruction_id = match instruction_id_option {
|
||||
Some(instruction_id) => instruction_id,
|
||||
@@ -101,6 +139,83 @@ impl KbPumpFunDecoder {
|
||||
Ok(accounts) => accounts,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let instruction_data =
|
||||
kb_decode_optional_instruction_data(instruction.data_json.as_ref());
|
||||
let is_buy = kb_instruction_data_starts_with(
|
||||
instruction_data.as_deref(),
|
||||
&KB_PUMP_FUN_BUY_DISCRIMINATOR,
|
||||
);
|
||||
let is_sell = kb_instruction_data_starts_with(
|
||||
instruction_data.as_deref(),
|
||||
&KB_PUMP_FUN_SELL_DISCRIMINATOR,
|
||||
);
|
||||
if is_buy || is_sell {
|
||||
if accounts.len() < 7 {
|
||||
continue;
|
||||
}
|
||||
let amount_raw = kb_extract_u64_argument(instruction_data.as_deref(), 8);
|
||||
let sol_limit_raw = kb_extract_u64_argument(instruction_data.as_deref(), 16);
|
||||
let mint = kb_extract_account(&accounts, 2);
|
||||
let bonding_curve = kb_extract_account(&accounts, 3);
|
||||
let associated_bonding_curve = kb_extract_account(&accounts, 4);
|
||||
let associated_user = kb_extract_account(&accounts, 5);
|
||||
let user = kb_extract_account(&accounts, 6);
|
||||
let fee_recipient = kb_extract_account(&accounts, 1);
|
||||
let token_program = kb_extract_account(&accounts, 8);
|
||||
let creator_vault = kb_extract_account(&accounts, 9);
|
||||
let payload_json = serde_json::json!({
|
||||
"decoder": "pump_fun",
|
||||
"signature": transaction.signature,
|
||||
"instructionId": instruction_id,
|
||||
"instructionIndex": instruction.instruction_index,
|
||||
"accounts": accounts,
|
||||
"logMessages": log_messages,
|
||||
"eventKind": if is_buy { "buy" } else { "sell" },
|
||||
"poolAccount": bonding_curve,
|
||||
"tokenAMint": mint,
|
||||
"tokenBMint": crate::WSOL_MINT_ID,
|
||||
"bondingCurve": bonding_curve,
|
||||
"associatedBondingCurve": associated_bonding_curve,
|
||||
"associatedUser": associated_user,
|
||||
"user": user,
|
||||
"feeRecipient": fee_recipient,
|
||||
"tokenProgram": token_program,
|
||||
"creatorVault": creator_vault,
|
||||
"poolBaseTokenAccount": associated_bonding_curve,
|
||||
"poolQuoteNativeAccount": bonding_curve,
|
||||
"amountRaw": amount_raw,
|
||||
"solLimitRaw": sol_limit_raw,
|
||||
"tradeSide": if is_buy { "BuyBase" } else { "SellBase" }
|
||||
});
|
||||
let event = crate::KbPumpFunTradeDecoded {
|
||||
transaction_id,
|
||||
instruction_id,
|
||||
signature: transaction.signature.clone(),
|
||||
program_id: program_id.clone(),
|
||||
trade_side: if is_buy {
|
||||
crate::KbSwapTradeSide::BuyBase
|
||||
} else {
|
||||
crate::KbSwapTradeSide::SellBase
|
||||
},
|
||||
mint,
|
||||
bonding_curve,
|
||||
associated_bonding_curve,
|
||||
associated_user,
|
||||
user,
|
||||
amount_raw,
|
||||
sol_limit_raw,
|
||||
payload_json,
|
||||
};
|
||||
if is_buy {
|
||||
decoded_events.push(crate::KbPumpFunDecodedEvent::BuyTrade(event));
|
||||
} else {
|
||||
decoded_events.push(crate::KbPumpFunDecodedEvent::SellTrade(event));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if !has_create_v2_log {
|
||||
continue;
|
||||
}
|
||||
if accounts.len() < 6 {
|
||||
continue;
|
||||
}
|
||||
@@ -139,6 +254,59 @@ impl KbPumpFunDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
fn kb_decode_optional_instruction_data(
|
||||
data_json: std::option::Option<&std::string::String>,
|
||||
) -> std::option::Option<std::vec::Vec<u8>> {
|
||||
let data_json = match data_json {
|
||||
Some(data_json) => data_json,
|
||||
None => return None,
|
||||
};
|
||||
let parsed_result = serde_json::from_str::<serde_json::Value>(data_json.as_str());
|
||||
let encoded = match parsed_result {
|
||||
Ok(parsed) => match parsed.as_str() {
|
||||
Some(text) => text.to_string(),
|
||||
None => data_json.clone(),
|
||||
},
|
||||
Err(_) => data_json.clone(),
|
||||
};
|
||||
let decode_result = bs58::decode(encoded.as_str()).into_vec();
|
||||
match decode_result {
|
||||
Ok(decoded) => Some(decoded),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn kb_instruction_data_starts_with(
|
||||
instruction_data: std::option::Option<&[u8]>,
|
||||
discriminator: &[u8; 8],
|
||||
) -> bool {
|
||||
let instruction_data = match instruction_data {
|
||||
Some(instruction_data) => instruction_data,
|
||||
None => return false,
|
||||
};
|
||||
if instruction_data.len() < discriminator.len() {
|
||||
return false;
|
||||
}
|
||||
&instruction_data[0..discriminator.len()] == discriminator
|
||||
}
|
||||
|
||||
fn kb_extract_u64_argument(
|
||||
instruction_data: std::option::Option<&[u8]>,
|
||||
offset: usize,
|
||||
) -> std::option::Option<std::string::String> {
|
||||
let instruction_data = match instruction_data {
|
||||
Some(instruction_data) => instruction_data,
|
||||
None => return None,
|
||||
};
|
||||
let end = offset + 8;
|
||||
if instruction_data.len() < end {
|
||||
return None;
|
||||
}
|
||||
let mut bytes = [0u8; 8];
|
||||
bytes.copy_from_slice(&instruction_data[offset..end]);
|
||||
Some(u64::from_le_bytes(bytes).to_string())
|
||||
}
|
||||
|
||||
fn kb_extract_log_messages(
|
||||
transaction_json: &serde_json::Value,
|
||||
) -> std::vec::Vec<std::string::String> {
|
||||
@@ -167,10 +335,7 @@ fn kb_extract_log_messages(
|
||||
messages
|
||||
}
|
||||
|
||||
fn kb_log_messages_contain_keyword(
|
||||
log_messages: &[std::string::String],
|
||||
keyword: &str,
|
||||
) -> bool {
|
||||
fn kb_log_messages_contain_keyword(log_messages: &[std::string::String], keyword: &str) -> bool {
|
||||
let keyword_normalized = kb_normalize_log_text(keyword);
|
||||
for log_message in log_messages {
|
||||
let log_normalized = kb_normalize_log_text(log_message.as_str());
|
||||
@@ -306,6 +471,12 @@ mod tests {
|
||||
);
|
||||
assert_eq!(event.creator, Some("Creator111".to_string()));
|
||||
}
|
||||
crate::KbPumpFunDecodedEvent::BuyTrade(_) => {
|
||||
panic!("unexpected pump_fun buy trade event");
|
||||
}
|
||||
crate::KbPumpFunDecodedEvent::SellTrade(_) => {
|
||||
panic!("unexpected pump_fun sell trade event");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user