0.7.34
This commit is contained in:
@@ -8,9 +8,17 @@
|
||||
|
||||
const DLMM_DISCRIMINATOR_CLAIM_FEE2: [u8; 8] = [0x70, 0xbf, 0x65, 0xab, 0x1c, 0x90, 0x7f, 0xbb];
|
||||
|
||||
const DLMM_DISCRIMINATOR_INITIALIZE_BIN_ARRAY: [u8; 8] =
|
||||
[0x23, 0x56, 0x13, 0xb9, 0x4e, 0xd4, 0x4b, 0xd3];
|
||||
|
||||
const DLMM_DISCRIMINATOR_INITIALIZE_POSITION: [u8; 8] =
|
||||
[0xdb, 0xc0, 0xea, 0x47, 0xbe, 0xbf, 0x66, 0x50];
|
||||
|
||||
const DLMM_DISCRIMINATOR_ADD_LIQUIDITY: [u8; 8] = [0xb5, 0x9d, 0x59, 0x43, 0x8f, 0xb6, 0x34, 0x48];
|
||||
|
||||
const DLMM_DISCRIMINATOR_REMOVE_LIQUIDITY: [u8; 8] =
|
||||
[0x50, 0x55, 0xd1, 0x48, 0x18, 0xce, 0xb1, 0x6c];
|
||||
|
||||
const DLMM_DISCRIMINATOR_INITIALIZE_LB_PAIR: [u8; 8] =
|
||||
[0x2d, 0x9a, 0xed, 0xd2, 0xdd, 0x0f, 0xa6, 0x5c];
|
||||
|
||||
@@ -89,6 +97,60 @@ pub struct MeteoraDlmmSwapDecoded {
|
||||
pub payload_json: serde_json::Value,
|
||||
}
|
||||
|
||||
/// Decoded Meteora DLMM liquidity lifecycle event.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct MeteoraDlmmLiquidityDecoded {
|
||||
/// 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,
|
||||
/// Normalized decoded event kind.
|
||||
pub event_kind: std::string::String,
|
||||
/// Optional DLMM pair/pool account.
|
||||
pub pool_account: std::option::Option<std::string::String>,
|
||||
/// Optional token X/base mint.
|
||||
pub token_a_mint: std::option::Option<std::string::String>,
|
||||
/// Optional token Y/quote mint.
|
||||
pub token_b_mint: std::option::Option<std::string::String>,
|
||||
/// Optional actor wallet or owner account.
|
||||
pub actor_wallet: std::option::Option<std::string::String>,
|
||||
/// Optional decoded base/token-X amount.
|
||||
pub base_amount_raw: std::option::Option<std::string::String>,
|
||||
/// Optional decoded quote/token-Y amount.
|
||||
pub quote_amount_raw: std::option::Option<std::string::String>,
|
||||
/// Optional decoded liquidity amount.
|
||||
pub liquidity_amount_raw: std::option::Option<std::string::String>,
|
||||
/// Decoded payload.
|
||||
pub payload_json: serde_json::Value,
|
||||
}
|
||||
|
||||
/// Decoded Meteora DLMM pool lifecycle event.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct MeteoraDlmmPoolLifecycleDecoded {
|
||||
/// 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,
|
||||
/// Normalized decoded event kind.
|
||||
pub event_kind: std::string::String,
|
||||
/// Optional DLMM pair/pool account.
|
||||
pub pool_account: std::option::Option<std::string::String>,
|
||||
/// Optional token X/base mint.
|
||||
pub token_a_mint: std::option::Option<std::string::String>,
|
||||
/// Optional token Y/quote mint.
|
||||
pub token_b_mint: std::option::Option<std::string::String>,
|
||||
/// Decoded payload.
|
||||
pub payload_json: serde_json::Value,
|
||||
}
|
||||
|
||||
/// Decoded Meteora DLMM event.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub enum MeteoraDlmmDecodedEvent {
|
||||
@@ -96,12 +158,20 @@ pub enum MeteoraDlmmDecodedEvent {
|
||||
CreatePool(MeteoraDlmmCreatePoolDecoded),
|
||||
/// DLMM swap.
|
||||
Swap(MeteoraDlmmSwapDecoded),
|
||||
/// DLMM liquidity lifecycle event.
|
||||
Liquidity(MeteoraDlmmLiquidityDecoded),
|
||||
/// DLMM pool lifecycle event that is not the canonical create-pool event.
|
||||
PoolLifecycle(MeteoraDlmmPoolLifecycleDecoded),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum MeteoraDlmmInstructionKind {
|
||||
CreatePool,
|
||||
Swap,
|
||||
LiquidityAdd,
|
||||
LiquidityRemove,
|
||||
PositionOpen,
|
||||
PoolLifecycle,
|
||||
Ignore,
|
||||
Unknown,
|
||||
}
|
||||
@@ -117,6 +187,9 @@ enum MeteoraDlmmInstructionName {
|
||||
SwapExactOut,
|
||||
SwapExactOut2,
|
||||
SwapWithPriceImpact,
|
||||
InitializeBinArray,
|
||||
AddLiquidity,
|
||||
RemoveLiquidity,
|
||||
ClaimFee2,
|
||||
InitializePosition,
|
||||
Unknown,
|
||||
@@ -138,6 +211,9 @@ impl MeteoraDlmmInstructionName {
|
||||
Self::SwapExactOut => return "swap_exact_out",
|
||||
Self::SwapExactOut2 => return "swap_exact_out2",
|
||||
Self::SwapWithPriceImpact => return "swap_with_price_impact",
|
||||
Self::InitializeBinArray => return "initialize_bin_array",
|
||||
Self::AddLiquidity => return "add_liquidity",
|
||||
Self::RemoveLiquidity => return "remove_liquidity",
|
||||
Self::ClaimFee2 => return "claim_fee2",
|
||||
Self::InitializePosition => return "initialize_position",
|
||||
Self::Unknown => return "unknown",
|
||||
@@ -157,8 +233,12 @@ impl MeteoraDlmmInstructionName {
|
||||
| Self::SwapExactOut
|
||||
| Self::SwapExactOut2
|
||||
| Self::SwapWithPriceImpact => return MeteoraDlmmInstructionKind::Swap,
|
||||
Self::AddLiquidity => return MeteoraDlmmInstructionKind::LiquidityAdd,
|
||||
Self::RemoveLiquidity => return MeteoraDlmmInstructionKind::LiquidityRemove,
|
||||
Self::InitializePosition => return MeteoraDlmmInstructionKind::PositionOpen,
|
||||
Self::InitializeBinArray => return MeteoraDlmmInstructionKind::PoolLifecycle,
|
||||
Self::Unknown => return MeteoraDlmmInstructionKind::Unknown,
|
||||
Self::ClaimFee2 | Self::InitializePosition => {
|
||||
Self::ClaimFee2 => {
|
||||
return MeteoraDlmmInstructionKind::Ignore;
|
||||
},
|
||||
}
|
||||
@@ -249,12 +329,15 @@ impl MeteoraDlmmDecoder {
|
||||
resolve_dlmm_token_x_mint(instruction_name, parsed_json.as_ref(), &accounts);
|
||||
let token_b_mint =
|
||||
resolve_dlmm_token_y_mint(instruction_name, parsed_json.as_ref(), &accounts);
|
||||
if pool_account.is_none() || token_a_mint.is_none() || token_b_mint.is_none() {
|
||||
if pool_account.is_none() {
|
||||
continue;
|
||||
}
|
||||
let config_account =
|
||||
resolve_dlmm_config_account(instruction_name, parsed_json.as_ref(), &accounts);
|
||||
if instruction_kind == MeteoraDlmmInstructionKind::CreatePool {
|
||||
if token_a_mint.is_none() || token_b_mint.is_none() {
|
||||
continue;
|
||||
}
|
||||
let payload_json = serde_json::json!({
|
||||
"decoder": "meteora_dlmm",
|
||||
"eventKind": "create_pool",
|
||||
@@ -293,6 +376,9 @@ impl MeteoraDlmmDecoder {
|
||||
continue;
|
||||
}
|
||||
if instruction_kind == MeteoraDlmmInstructionKind::Swap {
|
||||
if token_a_mint.is_none() || token_b_mint.is_none() {
|
||||
continue;
|
||||
}
|
||||
let reserve_x_account = resolve_dlmm_reserve_x_account(
|
||||
instruction_name,
|
||||
parsed_json.as_ref(),
|
||||
@@ -357,6 +443,127 @@ impl MeteoraDlmmDecoder {
|
||||
payload_json,
|
||||
},
|
||||
));
|
||||
continue;
|
||||
}
|
||||
if instruction_kind == MeteoraDlmmInstructionKind::LiquidityAdd
|
||||
|| instruction_kind == MeteoraDlmmInstructionKind::LiquidityRemove
|
||||
|| instruction_kind == MeteoraDlmmInstructionKind::PositionOpen
|
||||
{
|
||||
let event_kind = format!("meteora_dlmm.{}", instruction_name.as_str());
|
||||
let actor_wallet =
|
||||
resolve_dlmm_actor_wallet(instruction_name, parsed_json.as_ref(), &accounts);
|
||||
let base_amount_raw = extract_amount_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"baseAmountRaw",
|
||||
"base_amount_raw",
|
||||
"tokenXAmount",
|
||||
"token_x_amount",
|
||||
"amountX",
|
||||
"amount_x",
|
||||
],
|
||||
);
|
||||
let quote_amount_raw = extract_amount_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"quoteAmountRaw",
|
||||
"quote_amount_raw",
|
||||
"tokenYAmount",
|
||||
"token_y_amount",
|
||||
"amountY",
|
||||
"amount_y",
|
||||
],
|
||||
);
|
||||
let liquidity_amount_raw = extract_amount_string_by_candidate_keys(
|
||||
parsed_json.as_ref(),
|
||||
&[
|
||||
"liquidity",
|
||||
"liquidityAmount",
|
||||
"liquidity_amount",
|
||||
"binLiquidity",
|
||||
"bin_liquidity",
|
||||
],
|
||||
);
|
||||
let payload_json = serde_json::json!({
|
||||
"decoder": "meteora_dlmm",
|
||||
"eventKind": instruction_name.as_str(),
|
||||
"decodedInstructionName": instruction_name.as_str(),
|
||||
"dataDiscriminatorHex": instruction_data
|
||||
.as_ref()
|
||||
.and_then(|data| return first_8_bytes_hex(data.as_slice())),
|
||||
"classifiedInstructionKind": crate::classify_dex_event_lifecycle_kind_code(event_kind.as_str()),
|
||||
"signature": transaction.signature,
|
||||
"instructionId": instruction_id,
|
||||
"parentInstructionId": instruction.parent_instruction_id,
|
||||
"instructionIndex": instruction.instruction_index,
|
||||
"innerInstructionIndex": instruction.inner_instruction_index,
|
||||
"stackHeight": instruction.stack_height,
|
||||
"accounts": accounts,
|
||||
"parsed": parsed_json,
|
||||
"logMessages": log_messages,
|
||||
"poolAccount": pool_account,
|
||||
"tokenAMint": token_a_mint,
|
||||
"tokenBMint": token_b_mint,
|
||||
"actorWallet": actor_wallet,
|
||||
"baseAmountRaw": base_amount_raw,
|
||||
"quoteAmountRaw": quote_amount_raw,
|
||||
"liquidityAmountRaw": liquidity_amount_raw
|
||||
});
|
||||
decoded_events.push(crate::MeteoraDlmmDecodedEvent::Liquidity(
|
||||
crate::MeteoraDlmmLiquidityDecoded {
|
||||
transaction_id,
|
||||
instruction_id,
|
||||
signature: transaction.signature.clone(),
|
||||
program_id: program_id.to_string(),
|
||||
event_kind,
|
||||
pool_account,
|
||||
token_a_mint,
|
||||
token_b_mint,
|
||||
actor_wallet,
|
||||
base_amount_raw,
|
||||
quote_amount_raw,
|
||||
liquidity_amount_raw,
|
||||
payload_json,
|
||||
},
|
||||
));
|
||||
continue;
|
||||
}
|
||||
if instruction_kind == MeteoraDlmmInstructionKind::PoolLifecycle {
|
||||
let event_kind = format!("meteora_dlmm.{}", instruction_name.as_str());
|
||||
let payload_json = serde_json::json!({
|
||||
"decoder": "meteora_dlmm",
|
||||
"eventKind": instruction_name.as_str(),
|
||||
"decodedInstructionName": instruction_name.as_str(),
|
||||
"dataDiscriminatorHex": instruction_data
|
||||
.as_ref()
|
||||
.and_then(|data| return first_8_bytes_hex(data.as_slice())),
|
||||
"classifiedInstructionKind": crate::classify_dex_event_lifecycle_kind_code(event_kind.as_str()),
|
||||
"signature": transaction.signature,
|
||||
"instructionId": instruction_id,
|
||||
"parentInstructionId": instruction.parent_instruction_id,
|
||||
"instructionIndex": instruction.instruction_index,
|
||||
"innerInstructionIndex": instruction.inner_instruction_index,
|
||||
"stackHeight": instruction.stack_height,
|
||||
"accounts": accounts,
|
||||
"parsed": parsed_json,
|
||||
"logMessages": log_messages,
|
||||
"poolAccount": pool_account,
|
||||
"tokenAMint": token_a_mint,
|
||||
"tokenBMint": token_b_mint
|
||||
});
|
||||
decoded_events.push(crate::MeteoraDlmmDecodedEvent::PoolLifecycle(
|
||||
crate::MeteoraDlmmPoolLifecycleDecoded {
|
||||
transaction_id,
|
||||
instruction_id,
|
||||
signature: transaction.signature.clone(),
|
||||
program_id: program_id.to_string(),
|
||||
event_kind,
|
||||
pool_account,
|
||||
token_a_mint,
|
||||
token_b_mint,
|
||||
payload_json,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
return Ok(decoded_events);
|
||||
@@ -382,6 +589,18 @@ fn classify_instruction_name(
|
||||
if contains_swap_hint(parsed_type) {
|
||||
return MeteoraDlmmInstructionName::Swap;
|
||||
}
|
||||
if contains_add_liquidity_hint(parsed_type) {
|
||||
return MeteoraDlmmInstructionName::AddLiquidity;
|
||||
}
|
||||
if contains_remove_liquidity_hint(parsed_type) {
|
||||
return MeteoraDlmmInstructionName::RemoveLiquidity;
|
||||
}
|
||||
if contains_initialize_position_hint(parsed_type) {
|
||||
return MeteoraDlmmInstructionName::InitializePosition;
|
||||
}
|
||||
if contains_initialize_bin_array_hint(parsed_type) {
|
||||
return MeteoraDlmmInstructionName::InitializeBinArray;
|
||||
}
|
||||
if parsed_type.is_some() {
|
||||
return MeteoraDlmmInstructionName::Unknown;
|
||||
}
|
||||
@@ -392,6 +611,18 @@ fn classify_instruction_name(
|
||||
if contains_swap_hint_in_value(parsed_json) {
|
||||
return MeteoraDlmmInstructionName::Swap;
|
||||
}
|
||||
if contains_add_liquidity_hint_in_value(parsed_json) {
|
||||
return MeteoraDlmmInstructionName::AddLiquidity;
|
||||
}
|
||||
if contains_remove_liquidity_hint_in_value(parsed_json) {
|
||||
return MeteoraDlmmInstructionName::RemoveLiquidity;
|
||||
}
|
||||
if contains_initialize_position_hint_in_value(parsed_json) {
|
||||
return MeteoraDlmmInstructionName::InitializePosition;
|
||||
}
|
||||
if contains_initialize_bin_array_hint_in_value(parsed_json) {
|
||||
return MeteoraDlmmInstructionName::InitializeBinArray;
|
||||
}
|
||||
return MeteoraDlmmInstructionName::Unknown;
|
||||
}
|
||||
for log_message in log_messages {
|
||||
@@ -401,6 +632,18 @@ fn classify_instruction_name(
|
||||
if contains_swap_hint(Some(log_message.as_str())) {
|
||||
return MeteoraDlmmInstructionName::Swap;
|
||||
}
|
||||
if contains_add_liquidity_hint(Some(log_message.as_str())) {
|
||||
return MeteoraDlmmInstructionName::AddLiquidity;
|
||||
}
|
||||
if contains_remove_liquidity_hint(Some(log_message.as_str())) {
|
||||
return MeteoraDlmmInstructionName::RemoveLiquidity;
|
||||
}
|
||||
if contains_initialize_position_hint(Some(log_message.as_str())) {
|
||||
return MeteoraDlmmInstructionName::InitializePosition;
|
||||
}
|
||||
if contains_initialize_bin_array_hint(Some(log_message.as_str())) {
|
||||
return MeteoraDlmmInstructionName::InitializeBinArray;
|
||||
}
|
||||
}
|
||||
return MeteoraDlmmInstructionName::Unknown;
|
||||
}
|
||||
@@ -452,6 +695,15 @@ fn classify_instruction_name_from_data(
|
||||
if discriminator == DLMM_DISCRIMINATOR_SWAP_WITH_PRICE_IMPACT {
|
||||
return MeteoraDlmmInstructionName::SwapWithPriceImpact;
|
||||
}
|
||||
if discriminator == DLMM_DISCRIMINATOR_INITIALIZE_BIN_ARRAY {
|
||||
return MeteoraDlmmInstructionName::InitializeBinArray;
|
||||
}
|
||||
if discriminator == DLMM_DISCRIMINATOR_ADD_LIQUIDITY {
|
||||
return MeteoraDlmmInstructionName::AddLiquidity;
|
||||
}
|
||||
if discriminator == DLMM_DISCRIMINATOR_REMOVE_LIQUIDITY {
|
||||
return MeteoraDlmmInstructionName::RemoveLiquidity;
|
||||
}
|
||||
if discriminator == DLMM_DISCRIMINATOR_CLAIM_FEE2 {
|
||||
return MeteoraDlmmInstructionName::ClaimFee2;
|
||||
}
|
||||
@@ -490,12 +742,19 @@ fn resolve_dlmm_pool_account(
|
||||
| MeteoraDlmmInstructionName::Swap2
|
||||
| MeteoraDlmmInstructionName::SwapExactOut
|
||||
| MeteoraDlmmInstructionName::SwapExactOut2
|
||||
| MeteoraDlmmInstructionName::SwapWithPriceImpact => {
|
||||
| MeteoraDlmmInstructionName::SwapWithPriceImpact
|
||||
| MeteoraDlmmInstructionName::InitializeBinArray => {
|
||||
return extract_account(accounts, 0);
|
||||
},
|
||||
MeteoraDlmmInstructionName::ClaimFee2
|
||||
| MeteoraDlmmInstructionName::InitializePosition
|
||||
| MeteoraDlmmInstructionName::Unknown => return None,
|
||||
MeteoraDlmmInstructionName::AddLiquidity | MeteoraDlmmInstructionName::RemoveLiquidity => {
|
||||
return extract_account(accounts, 1);
|
||||
},
|
||||
MeteoraDlmmInstructionName::InitializePosition => {
|
||||
return extract_account(accounts, 2);
|
||||
},
|
||||
MeteoraDlmmInstructionName::ClaimFee2 | MeteoraDlmmInstructionName::Unknown => {
|
||||
return None;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,7 +794,11 @@ fn resolve_dlmm_token_x_mint(
|
||||
| MeteoraDlmmInstructionName::SwapWithPriceImpact => {
|
||||
return extract_account(accounts, 6);
|
||||
},
|
||||
MeteoraDlmmInstructionName::AddLiquidity | MeteoraDlmmInstructionName::RemoveLiquidity => {
|
||||
return extract_account(accounts, 7);
|
||||
},
|
||||
MeteoraDlmmInstructionName::ClaimFee2
|
||||
| MeteoraDlmmInstructionName::InitializeBinArray
|
||||
| MeteoraDlmmInstructionName::InitializePosition
|
||||
| MeteoraDlmmInstructionName::Unknown => return None,
|
||||
}
|
||||
@@ -577,7 +840,11 @@ fn resolve_dlmm_token_y_mint(
|
||||
| MeteoraDlmmInstructionName::SwapWithPriceImpact => {
|
||||
return extract_account(accounts, 7);
|
||||
},
|
||||
MeteoraDlmmInstructionName::AddLiquidity | MeteoraDlmmInstructionName::RemoveLiquidity => {
|
||||
return extract_account(accounts, 8);
|
||||
},
|
||||
MeteoraDlmmInstructionName::ClaimFee2
|
||||
| MeteoraDlmmInstructionName::InitializeBinArray
|
||||
| MeteoraDlmmInstructionName::InitializePosition
|
||||
| MeteoraDlmmInstructionName::Unknown => return None,
|
||||
}
|
||||
@@ -715,6 +982,86 @@ fn resolve_dlmm_config_account(
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_dlmm_actor_wallet(
|
||||
instruction_name: MeteoraDlmmInstructionName,
|
||||
parsed_json: std::option::Option<&serde_json::Value>,
|
||||
accounts: &[std::string::String],
|
||||
) -> std::option::Option<std::string::String> {
|
||||
let parsed_value = extract_string_by_candidate_keys(
|
||||
parsed_json,
|
||||
&["owner", "payer", "sender", "user", "authority", "liquidityProvider"],
|
||||
);
|
||||
if parsed_value.is_some() {
|
||||
return parsed_value;
|
||||
}
|
||||
match instruction_name {
|
||||
MeteoraDlmmInstructionName::AddLiquidity | MeteoraDlmmInstructionName::RemoveLiquidity => {
|
||||
return extract_account(accounts, 9);
|
||||
},
|
||||
MeteoraDlmmInstructionName::InitializePosition => {
|
||||
return extract_account(accounts, 3);
|
||||
},
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_amount_string_by_candidate_keys(
|
||||
value: std::option::Option<&serde_json::Value>,
|
||||
candidate_keys: &[&str],
|
||||
) -> std::option::Option<std::string::String> {
|
||||
let value = match value {
|
||||
Some(value) => value,
|
||||
None => return None,
|
||||
};
|
||||
if let Some(text) = extract_string_by_candidate_keys(Some(value), candidate_keys) {
|
||||
return Some(text);
|
||||
}
|
||||
return extract_number_by_candidate_keys(Some(value), candidate_keys);
|
||||
}
|
||||
|
||||
fn extract_number_by_candidate_keys(
|
||||
value: std::option::Option<&serde_json::Value>,
|
||||
candidate_keys: &[&str],
|
||||
) -> std::option::Option<std::string::String> {
|
||||
let value = match value {
|
||||
Some(value) => value,
|
||||
None => return None,
|
||||
};
|
||||
match value {
|
||||
serde_json::Value::Object(object) => {
|
||||
for candidate_key in candidate_keys {
|
||||
if let Some(candidate) = object.get(*candidate_key) {
|
||||
if let Some(number) = candidate.as_i64() {
|
||||
return Some(number.to_string());
|
||||
}
|
||||
if let Some(number) = candidate.as_u64() {
|
||||
return Some(number.to_string());
|
||||
}
|
||||
if let Some(number) = candidate.as_f64() {
|
||||
return Some(number.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
for nested in object.values() {
|
||||
let result = extract_number_by_candidate_keys(Some(nested), candidate_keys);
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
},
|
||||
serde_json::Value::Array(values) => {
|
||||
for nested in values {
|
||||
let result = extract_number_by_candidate_keys(Some(nested), candidate_keys);
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn contains_create_pool_hint(value: std::option::Option<&str>) -> bool {
|
||||
let value = match value {
|
||||
Some(value) => value.to_ascii_lowercase(),
|
||||
@@ -755,6 +1102,62 @@ fn contains_swap_hint(value: std::option::Option<&str>) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn contains_add_liquidity_hint(value: std::option::Option<&str>) -> bool {
|
||||
let value = match value {
|
||||
Some(value) => value.to_ascii_lowercase(),
|
||||
None => return false,
|
||||
};
|
||||
if value.contains("addliquidity") {
|
||||
return true;
|
||||
}
|
||||
if value.contains("add_liquidity") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn contains_remove_liquidity_hint(value: std::option::Option<&str>) -> bool {
|
||||
let value = match value {
|
||||
Some(value) => value.to_ascii_lowercase(),
|
||||
None => return false,
|
||||
};
|
||||
if value.contains("removeliquidity") {
|
||||
return true;
|
||||
}
|
||||
if value.contains("remove_liquidity") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn contains_initialize_position_hint(value: std::option::Option<&str>) -> bool {
|
||||
let value = match value {
|
||||
Some(value) => value.to_ascii_lowercase(),
|
||||
None => return false,
|
||||
};
|
||||
if value.contains("initializeposition") {
|
||||
return true;
|
||||
}
|
||||
if value.contains("initialize_position") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn contains_initialize_bin_array_hint(value: std::option::Option<&str>) -> bool {
|
||||
let value = match value {
|
||||
Some(value) => value.to_ascii_lowercase(),
|
||||
None => return false,
|
||||
};
|
||||
if value.contains("initializebinarray") {
|
||||
return true;
|
||||
}
|
||||
if value.contains("initialize_bin_array") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn contains_create_pool_hint_in_value(value: &serde_json::Value) -> bool {
|
||||
return contains_string_hint_in_value(value, contains_create_pool_hint);
|
||||
}
|
||||
@@ -763,6 +1166,22 @@ fn contains_swap_hint_in_value(value: &serde_json::Value) -> bool {
|
||||
return contains_string_hint_in_value(value, contains_swap_hint);
|
||||
}
|
||||
|
||||
fn contains_add_liquidity_hint_in_value(value: &serde_json::Value) -> bool {
|
||||
return contains_string_hint_in_value(value, contains_add_liquidity_hint);
|
||||
}
|
||||
|
||||
fn contains_remove_liquidity_hint_in_value(value: &serde_json::Value) -> bool {
|
||||
return contains_string_hint_in_value(value, contains_remove_liquidity_hint);
|
||||
}
|
||||
|
||||
fn contains_initialize_position_hint_in_value(value: &serde_json::Value) -> bool {
|
||||
return contains_string_hint_in_value(value, contains_initialize_position_hint);
|
||||
}
|
||||
|
||||
fn contains_initialize_bin_array_hint_in_value(value: &serde_json::Value) -> bool {
|
||||
return contains_string_hint_in_value(value, contains_initialize_bin_array_hint);
|
||||
}
|
||||
|
||||
fn contains_string_hint_in_value(
|
||||
value: &serde_json::Value,
|
||||
predicate: fn(std::option::Option<&str>) -> bool,
|
||||
@@ -1118,6 +1537,10 @@ mod tests {
|
||||
crate::MeteoraDlmmDecodedEvent::Swap(_) => {
|
||||
panic!("unexpected swap event");
|
||||
},
|
||||
crate::MeteoraDlmmDecodedEvent::Liquidity(_)
|
||||
| crate::MeteoraDlmmDecodedEvent::PoolLifecycle(_) => {
|
||||
panic!("unexpected non-trade event");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1144,11 +1567,15 @@ mod tests {
|
||||
crate::MeteoraDlmmDecodedEvent::CreatePool(_) => {
|
||||
panic!("unexpected create event");
|
||||
},
|
||||
crate::MeteoraDlmmDecodedEvent::Liquidity(_)
|
||||
| crate::MeteoraDlmmDecodedEvent::PoolLifecycle(_) => {
|
||||
panic!("unexpected non-trade event");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn meteora_dlmm_ignores_unclear_instruction() {
|
||||
fn meteora_dlmm_initialize_bin_array_hint_is_decoded_as_pool_lifecycle() {
|
||||
let decoder = crate::MeteoraDlmmDecoder::new();
|
||||
let transaction = make_swap_transaction();
|
||||
let mut instruction = make_swap_instruction();
|
||||
@@ -1159,7 +1586,14 @@ mod tests {
|
||||
Ok(decoded) => decoded,
|
||||
Err(error) => panic!("decode must succeed: {}", error),
|
||||
};
|
||||
assert_eq!(decoded.len(), 0);
|
||||
assert_eq!(decoded.len(), 1);
|
||||
match &decoded[0] {
|
||||
crate::MeteoraDlmmDecodedEvent::PoolLifecycle(event) => {
|
||||
assert_eq!(event.event_kind, "meteora_dlmm.initialize_bin_array");
|
||||
assert_eq!(event.pool_account, Some("DlmmPairSwap111".to_string()));
|
||||
},
|
||||
_ => panic!("expected pool lifecycle event"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1294,17 +1728,89 @@ mod tests {
|
||||
crate::MeteoraDlmmDecodedEvent::CreatePool(_) => {
|
||||
panic!("unexpected create event");
|
||||
},
|
||||
crate::MeteoraDlmmDecodedEvent::Liquidity(_)
|
||||
| crate::MeteoraDlmmDecodedEvent::PoolLifecycle(_) => {
|
||||
panic!("unexpected non-trade event");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn meteora_dlmm_initialize_position_discriminator_is_ignored() {
|
||||
fn meteora_dlmm_initialize_position_discriminator_is_non_trade_position_open() {
|
||||
let instruction_data = [0xdb, 0xc0, 0xea, 0x47, 0xbe, 0xbf, 0x66, 0x50, 0x01, 0x02, 0x03];
|
||||
let log_messages = vec!["Program log: Instruction: Swap".to_string()];
|
||||
let name =
|
||||
super::classify_instruction_name(None, None, Some(&instruction_data), &log_messages);
|
||||
assert_eq!(name, super::MeteoraDlmmInstructionName::InitializePosition);
|
||||
assert_eq!(name.kind(), super::MeteoraDlmmInstructionKind::Ignore);
|
||||
assert_eq!(name.kind(), super::MeteoraDlmmInstructionKind::PositionOpen);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn meteora_dlmm_add_remove_liquidity_discriminators_are_non_trade_liquidity() {
|
||||
let add_data = [0xb5, 0x9d, 0x59, 0x43, 0x8f, 0xb6, 0x34, 0x48, 0x01];
|
||||
let remove_data = [0x50, 0x55, 0xd1, 0x48, 0x18, 0xce, 0xb1, 0x6c, 0x01];
|
||||
let add_name = super::classify_instruction_name_from_data(Some(&add_data));
|
||||
let remove_name = super::classify_instruction_name_from_data(Some(&remove_data));
|
||||
assert_eq!(add_name, super::MeteoraDlmmInstructionName::AddLiquidity);
|
||||
assert_eq!(add_name.kind(), super::MeteoraDlmmInstructionKind::LiquidityAdd);
|
||||
assert_eq!(remove_name, super::MeteoraDlmmInstructionName::RemoveLiquidity);
|
||||
assert_eq!(remove_name.kind(), super::MeteoraDlmmInstructionKind::LiquidityRemove);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn meteora_dlmm_initialize_bin_array_is_pool_lifecycle() {
|
||||
let data = [0x23, 0x56, 0x13, 0xb9, 0x4e, 0xd4, 0x4b, 0xd3, 0x01];
|
||||
let name = super::classify_instruction_name_from_data(Some(&data));
|
||||
assert_eq!(name, super::MeteoraDlmmInstructionName::InitializeBinArray);
|
||||
assert_eq!(name.kind(), super::MeteoraDlmmInstructionKind::PoolLifecycle);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn meteora_dlmm_add_liquidity_is_decoded_as_non_trade_event() {
|
||||
let decoder = crate::MeteoraDlmmDecoder::new();
|
||||
let transaction = make_swap_transaction();
|
||||
let mut instruction = crate::ChainInstructionDto::new(
|
||||
403,
|
||||
None,
|
||||
0,
|
||||
None,
|
||||
Some(crate::METEORA_DLMM_PROGRAM_ID.to_string()),
|
||||
Some("meteora-dlmm".to_string()),
|
||||
Some(1),
|
||||
serde_json::json!([
|
||||
"Position111",
|
||||
"DlmmPairSwap111",
|
||||
"Bitmap111",
|
||||
"UserTokenX111",
|
||||
"UserTokenY111",
|
||||
"ReserveX111",
|
||||
"ReserveY111",
|
||||
"DlmmSwapTokenX111",
|
||||
crate::WSOL_MINT_ID,
|
||||
"Owner111"
|
||||
])
|
||||
.to_string(),
|
||||
Some("\"3K5citUwVB6uv\"".to_string()),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
instruction.id = Some(405);
|
||||
let decoded_result = decoder.decode_transaction(&transaction, &[instruction]);
|
||||
let decoded = match decoded_result {
|
||||
Ok(decoded) => decoded,
|
||||
Err(error) => panic!("decode must succeed: {}", error),
|
||||
};
|
||||
assert_eq!(decoded.len(), 1);
|
||||
match &decoded[0] {
|
||||
crate::MeteoraDlmmDecodedEvent::Liquidity(event) => {
|
||||
assert_eq!(event.event_kind, "meteora_dlmm.add_liquidity");
|
||||
assert_eq!(event.pool_account, Some("DlmmPairSwap111".to_string()));
|
||||
assert_eq!(event.token_a_mint, Some("DlmmSwapTokenX111".to_string()));
|
||||
assert_eq!(event.token_b_mint, Some(crate::WSOL_MINT_ID.to_string()));
|
||||
assert_eq!(event.actor_wallet, Some("Owner111".to_string()));
|
||||
},
|
||||
_ => panic!("expected liquidity event"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user