2497 lines
92 KiB
Rust
2497 lines
92 KiB
Rust
// file: kb_lib/src/dex/pump_fees.rs
|
|
|
|
//! Pump Fees fee/config/accounting transaction decoder.
|
|
|
|
const PUMP_FEES_ANCHOR_SELF_CPI_LOG_DISCRIMINATOR: [u8; 8] = [228, 69, 165, 46, 81, 203, 154, 29];
|
|
const PUMP_FEES_CLAIM_SOCIAL_FEE_PDA_DISCRIMINATOR: [u8; 8] =
|
|
[225, 21, 251, 133, 161, 30, 199, 226];
|
|
const PUMP_FEES_CLAIM_SOCIAL_FEE_PDA_V2_DISCRIMINATOR: [u8; 8] =
|
|
[17, 77, 240, 134, 58, 188, 53, 149];
|
|
const PUMP_FEES_CRANK_DONATION_FEE_PDA_DISCRIMINATOR: [u8; 8] =
|
|
[220, 10, 189, 167, 169, 17, 25, 69];
|
|
const PUMP_FEES_CREATE_DONATION_FEE_PDA_DISCRIMINATOR: [u8; 8] =
|
|
[244, 139, 16, 88, 14, 255, 122, 26];
|
|
const PUMP_FEES_CREATE_FEE_SHARING_CONFIG_DISCRIMINATOR: [u8; 8] =
|
|
[195, 78, 86, 76, 111, 52, 251, 213];
|
|
const PUMP_FEES_CREATE_SOCIAL_FEE_PDA_DISCRIMINATOR: [u8; 8] =
|
|
[144, 224, 59, 211, 78, 248, 202, 220];
|
|
const PUMP_FEES_EXTEND_FEE_CONFIG_DISCRIMINATOR: [u8; 8] = [68, 179, 244, 90, 173, 56, 17, 217];
|
|
const PUMP_FEES_GET_FEES_DISCRIMINATOR: [u8; 8] = [231, 37, 126, 85, 207, 91, 63, 52];
|
|
const PUMP_FEES_INITIALIZE_BUYBACK_DISCRIMINATOR: [u8; 8] = [250, 129, 236, 160, 227, 36, 103, 134];
|
|
const PUMP_FEES_INITIALIZE_FEE_CONFIG_DISCRIMINATOR: [u8; 8] = [62, 162, 20, 133, 121, 65, 145, 27];
|
|
const PUMP_FEES_INITIALIZE_FEE_PROGRAM_GLOBAL_DISCRIMINATOR: [u8; 8] =
|
|
[35, 215, 130, 84, 233, 56, 124, 167];
|
|
const PUMP_FEES_RESET_FEE_SHARING_CONFIG_DISCRIMINATOR: [u8; 8] =
|
|
[10, 2, 182, 95, 16, 127, 129, 186];
|
|
const PUMP_FEES_RESET_FEE_SHARING_CONFIG_V2_DISCRIMINATOR: [u8; 8] =
|
|
[169, 245, 17, 209, 94, 91, 248, 128];
|
|
const PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY_DISCRIMINATOR: [u8; 8] =
|
|
[18, 233, 158, 39, 185, 207, 58, 104];
|
|
const PUMP_FEES_SET_AUTHORITY_DISCRIMINATOR: [u8; 8] = [133, 250, 37, 21, 110, 163, 26, 121];
|
|
const PUMP_FEES_SET_CLAIM_RATE_LIMIT_DISCRIMINATOR: [u8; 8] = [185, 211, 159, 174, 212, 49, 88, 4];
|
|
const PUMP_FEES_SET_DISABLE_FLAGS_DISCRIMINATOR: [u8; 8] = [194, 217, 112, 35, 114, 222, 51, 190];
|
|
const PUMP_FEES_SET_SOCIAL_CLAIM_AUTHORITY_DISCRIMINATOR: [u8; 8] =
|
|
[147, 54, 184, 154, 136, 237, 185, 153];
|
|
const PUMP_FEES_SWEEP_BUYBACK_DISCRIMINATOR: [u8; 8] = [138, 33, 204, 38, 207, 161, 159, 226];
|
|
const PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY_DISCRIMINATOR: [u8; 8] =
|
|
[202, 10, 75, 200, 164, 34, 210, 96];
|
|
const PUMP_FEES_UPDATE_ADMIN_DISCRIMINATOR: [u8; 8] = [161, 176, 40, 213, 60, 184, 179, 228];
|
|
const PUMP_FEES_UPDATE_BUYBACK_AUTHORITY_DISCRIMINATOR: [u8; 8] =
|
|
[66, 98, 113, 202, 121, 37, 219, 107];
|
|
const PUMP_FEES_UPDATE_BUYBACK_CLAIM_RATE_LIMIT_DISCRIMINATOR: [u8; 8] =
|
|
[186, 95, 135, 190, 255, 199, 137, 170];
|
|
const PUMP_FEES_UPDATE_FEE_CONFIG_DISCRIMINATOR: [u8; 8] = [104, 184, 103, 242, 88, 151, 107, 20];
|
|
const PUMP_FEES_UPDATE_FEE_SHARES_DISCRIMINATOR: [u8; 8] = [189, 13, 136, 99, 187, 164, 237, 35];
|
|
const PUMP_FEES_UPDATE_FEE_SHARES_V2_DISCRIMINATOR: [u8; 8] = [111, 251, 49, 6, 78, 78, 106, 18];
|
|
const PUMP_FEES_UPDATE_STABLE_FEE_CONFIG_DISCRIMINATOR: [u8; 8] =
|
|
[107, 169, 100, 179, 134, 155, 146, 221];
|
|
const PUMP_FEES_UPSERT_FEE_TIERS_DISCRIMINATOR: [u8; 8] = [227, 23, 150, 12, 77, 86, 94, 4];
|
|
const PUMP_FEES_UPSERT_STABLE_FEE_TIERS_DISCRIMINATOR: [u8; 8] =
|
|
[181, 160, 162, 252, 74, 76, 224, 221];
|
|
const PUMP_FEES_CREATE_FEE_SHARING_CONFIG_EVENT_DISCRIMINATOR: [u8; 8] =
|
|
[133, 105, 170, 200, 184, 116, 251, 88];
|
|
const PUMP_FEES_DONATION_FEE_PDA_CRANKED_DISCRIMINATOR: [u8; 8] =
|
|
[30, 208, 107, 93, 177, 0, 223, 78];
|
|
const PUMP_FEES_DONATION_FEE_PDA_CREATED_DISCRIMINATOR: [u8; 8] =
|
|
[94, 20, 137, 239, 35, 77, 225, 235];
|
|
const PUMP_FEES_EXTEND_FEE_CONFIG_EVENT_DISCRIMINATOR: [u8; 8] =
|
|
[226, 203, 224, 35, 153, 10, 88, 51];
|
|
const PUMP_FEES_INITIALIZE_FEE_CONFIG_EVENT_DISCRIMINATOR: [u8; 8] =
|
|
[89, 138, 244, 230, 10, 56, 226, 126];
|
|
const PUMP_FEES_INITIALIZE_FEE_PROGRAM_GLOBAL_EVENT_DISCRIMINATOR: [u8; 8] =
|
|
[40, 233, 156, 78, 95, 0, 8, 199];
|
|
const PUMP_FEES_RESET_FEE_SHARING_CONFIG_EVENT_DISCRIMINATOR: [u8; 8] =
|
|
[203, 204, 151, 226, 120, 55, 214, 243];
|
|
const PUMP_FEES_SET_AUTHORITY_EVENT_DISCRIMINATOR: [u8; 8] = [18, 175, 132, 66, 208, 201, 87, 242];
|
|
const PUMP_FEES_SET_CLAIM_RATE_LIMIT_EVENT_DISCRIMINATOR: [u8; 8] =
|
|
[13, 143, 143, 235, 181, 19, 51, 40];
|
|
const PUMP_FEES_SET_DISABLE_FLAGS_EVENT_DISCRIMINATOR: [u8; 8] = [5, 8, 179, 65, 49, 55, 145, 126];
|
|
const PUMP_FEES_SET_SOCIAL_CLAIM_AUTHORITY_EVENT_DISCRIMINATOR: [u8; 8] =
|
|
[60, 118, 127, 132, 239, 52, 254, 14];
|
|
const PUMP_FEES_SOCIAL_FEE_PDA_CLAIMED_DISCRIMINATOR: [u8; 8] =
|
|
[50, 18, 193, 65, 237, 210, 234, 236];
|
|
const PUMP_FEES_SOCIAL_FEE_PDA_CREATED_DISCRIMINATOR: [u8; 8] =
|
|
[183, 183, 218, 147, 24, 124, 137, 169];
|
|
const PUMP_FEES_SWEEP_BUYBACK_EVENT_DISCRIMINATOR: [u8; 8] = [43, 56, 42, 214, 153, 57, 166, 137];
|
|
const PUMP_FEES_UPDATE_ADMIN_EVENT_DISCRIMINATOR: [u8; 8] = [225, 152, 171, 87, 246, 63, 66, 234];
|
|
const PUMP_FEES_UPDATE_FEE_CONFIG_EVENT_DISCRIMINATOR: [u8; 8] =
|
|
[90, 23, 65, 35, 62, 244, 188, 208];
|
|
const PUMP_FEES_UPDATE_FEE_SHARES_EVENT_DISCRIMINATOR: [u8; 8] =
|
|
[21, 186, 196, 184, 91, 228, 225, 203];
|
|
const PUMP_FEES_UPDATE_STABLE_FEE_CONFIG_EVENT_DISCRIMINATOR: [u8; 8] =
|
|
[94, 5, 43, 237, 103, 147, 232, 245];
|
|
const PUMP_FEES_UPSERT_FEE_TIERS_EVENT_DISCRIMINATOR: [u8; 8] =
|
|
[171, 89, 169, 187, 122, 186, 33, 204];
|
|
const PUMP_FEES_UPSERT_STABLE_FEE_TIERS_EVENT_DISCRIMINATOR: [u8; 8] =
|
|
[232, 237, 237, 52, 98, 146, 73, 243];
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
enum PumpFeesBorshFieldKind {
|
|
U8,
|
|
U64,
|
|
I64,
|
|
U128,
|
|
Bool,
|
|
Pubkey,
|
|
String,
|
|
OptionBool,
|
|
OptionPubkey,
|
|
Fees,
|
|
ConfigStatus,
|
|
ShareholderVec,
|
|
FeeTierVec,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
struct PumpFeesBorshFieldDescriptor {
|
|
name: &'static str,
|
|
kind: PumpFeesBorshFieldKind,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
struct PumpFeesInstructionSpec {
|
|
name: &'static str,
|
|
fields: &'static [PumpFeesBorshFieldDescriptor],
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
struct PumpFeesAnchorEventSpec {
|
|
name: &'static str,
|
|
event_kind: &'static str,
|
|
fields: &'static [PumpFeesBorshFieldDescriptor],
|
|
}
|
|
|
|
const PUMP_FEES_INSTRUCTION_CLAIM_SOCIAL_FEE_PDA_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "user_id",
|
|
kind: PumpFeesBorshFieldKind::String,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "platform",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_INSTRUCTION_CLAIM_SOCIAL_FEE_PDA_V2_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "user_id",
|
|
kind: PumpFeesBorshFieldKind::String,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "platform",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_INSTRUCTION_CRANK_DONATION_FEE_PDA_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[];
|
|
|
|
const PUMP_FEES_INSTRUCTION_CREATE_DONATION_FEE_PDA_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[];
|
|
|
|
const PUMP_FEES_INSTRUCTION_CREATE_FEE_SHARING_CONFIG_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[];
|
|
|
|
const PUMP_FEES_INSTRUCTION_CREATE_SOCIAL_FEE_PDA_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "user_id",
|
|
kind: PumpFeesBorshFieldKind::String,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "platform",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_INSTRUCTION_EXTEND_FEE_CONFIG_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[];
|
|
|
|
const PUMP_FEES_INSTRUCTION_GET_FEES_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "is_pump_pool",
|
|
kind: PumpFeesBorshFieldKind::Bool,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "market_cap_lamports",
|
|
kind: PumpFeesBorshFieldKind::U128,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "trade_size_lamports",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "is_new_quote_mint",
|
|
kind: PumpFeesBorshFieldKind::OptionBool,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_INSTRUCTION_INITIALIZE_BUYBACK_FIELDS: &[PumpFeesBorshFieldDescriptor] =
|
|
&[PumpFeesBorshFieldDescriptor {
|
|
name: "index",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
}];
|
|
|
|
const PUMP_FEES_INSTRUCTION_INITIALIZE_FEE_CONFIG_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[];
|
|
|
|
const PUMP_FEES_INSTRUCTION_INITIALIZE_FEE_PROGRAM_GLOBAL_FIELDS:
|
|
&[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "social_claim_authority",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "disable_flags",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "claim_rate_limit",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_INSTRUCTION_RESET_FEE_SHARING_CONFIG_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[];
|
|
|
|
const PUMP_FEES_INSTRUCTION_RESET_FEE_SHARING_CONFIG_V2_FIELDS: &[PumpFeesBorshFieldDescriptor] =
|
|
&[];
|
|
|
|
const PUMP_FEES_INSTRUCTION_REVOKE_FEE_SHARING_AUTHORITY_FIELDS: &[PumpFeesBorshFieldDescriptor] =
|
|
&[];
|
|
|
|
const PUMP_FEES_INSTRUCTION_SET_AUTHORITY_FIELDS: &[PumpFeesBorshFieldDescriptor] =
|
|
&[PumpFeesBorshFieldDescriptor {
|
|
name: "new_authority",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
}];
|
|
|
|
const PUMP_FEES_INSTRUCTION_SET_CLAIM_RATE_LIMIT_FIELDS: &[PumpFeesBorshFieldDescriptor] =
|
|
&[PumpFeesBorshFieldDescriptor {
|
|
name: "claim_rate_limit",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
}];
|
|
|
|
const PUMP_FEES_INSTRUCTION_SET_DISABLE_FLAGS_FIELDS: &[PumpFeesBorshFieldDescriptor] =
|
|
&[PumpFeesBorshFieldDescriptor {
|
|
name: "disable_flags",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
}];
|
|
|
|
const PUMP_FEES_INSTRUCTION_SET_SOCIAL_CLAIM_AUTHORITY_FIELDS: &[PumpFeesBorshFieldDescriptor] =
|
|
&[PumpFeesBorshFieldDescriptor {
|
|
name: "social_claim_authority",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
}];
|
|
|
|
const PUMP_FEES_INSTRUCTION_SWEEP_BUYBACK_FIELDS: &[PumpFeesBorshFieldDescriptor] =
|
|
&[PumpFeesBorshFieldDescriptor {
|
|
name: "index",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
}];
|
|
|
|
const PUMP_FEES_INSTRUCTION_TRANSFER_FEE_SHARING_AUTHORITY_FIELDS:
|
|
&[PumpFeesBorshFieldDescriptor] = &[];
|
|
|
|
const PUMP_FEES_INSTRUCTION_UPDATE_ADMIN_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[];
|
|
|
|
const PUMP_FEES_INSTRUCTION_UPDATE_BUYBACK_AUTHORITY_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "index",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "new_authority",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_INSTRUCTION_UPDATE_BUYBACK_CLAIM_RATE_LIMIT_FIELDS:
|
|
&[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "index",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "claim_rate_limit",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_INSTRUCTION_UPDATE_FEE_CONFIG_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "fee_tiers",
|
|
kind: PumpFeesBorshFieldKind::FeeTierVec,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "flat_fees",
|
|
kind: PumpFeesBorshFieldKind::Fees,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_INSTRUCTION_UPDATE_FEE_SHARES_FIELDS: &[PumpFeesBorshFieldDescriptor] =
|
|
&[PumpFeesBorshFieldDescriptor {
|
|
name: "shareholders",
|
|
kind: PumpFeesBorshFieldKind::ShareholderVec,
|
|
}];
|
|
|
|
const PUMP_FEES_INSTRUCTION_UPDATE_FEE_SHARES_V2_FIELDS: &[PumpFeesBorshFieldDescriptor] =
|
|
&[PumpFeesBorshFieldDescriptor {
|
|
name: "shareholders",
|
|
kind: PumpFeesBorshFieldKind::ShareholderVec,
|
|
}];
|
|
|
|
const PUMP_FEES_INSTRUCTION_UPDATE_STABLE_FEE_CONFIG_FIELDS: &[PumpFeesBorshFieldDescriptor] =
|
|
&[PumpFeesBorshFieldDescriptor {
|
|
name: "fee_tiers",
|
|
kind: PumpFeesBorshFieldKind::FeeTierVec,
|
|
}];
|
|
|
|
const PUMP_FEES_INSTRUCTION_UPSERT_FEE_TIERS_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "fee_tiers",
|
|
kind: PumpFeesBorshFieldKind::FeeTierVec,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "offset",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_INSTRUCTION_UPSERT_STABLE_FEE_TIERS_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "fee_tiers",
|
|
kind: PumpFeesBorshFieldKind::FeeTierVec,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "offset",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_CREATE_FEE_SHARING_CONFIG_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "mint",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "bonding_curve",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "pool",
|
|
kind: PumpFeesBorshFieldKind::OptionPubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "sharing_config",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "admin",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "initial_shareholders",
|
|
kind: PumpFeesBorshFieldKind::ShareholderVec,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "status",
|
|
kind: PumpFeesBorshFieldKind::ConfigStatus,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_DONATION_FEE_PDA_CRANKED_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "signer",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "donation_fee_pda",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "config_id",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "base_mint",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "quote_mint",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "creator",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "amount",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_DONATION_FEE_PDA_CREATED_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "created_by",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "donation_fee_pda",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "config_id",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "base_mint",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "quote_mint",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "creator",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_EXTEND_FEE_CONFIG_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "fee_config",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "user",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "current_size",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "new_size",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_INITIALIZE_FEE_CONFIG_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "admin",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "fee_config",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_INITIALIZE_FEE_PROGRAM_GLOBAL_EVENT_FIELDS:
|
|
&[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "authority",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "social_claim_authority",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "disable_flags",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "claim_rate_limit",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_RESET_FEE_SHARING_CONFIG_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "mint",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "sharing_config",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "old_admin",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "old_shareholders",
|
|
kind: PumpFeesBorshFieldKind::ShareholderVec,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "new_admin",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "new_shareholders",
|
|
kind: PumpFeesBorshFieldKind::ShareholderVec,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "old_version",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "new_version",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_SET_AUTHORITY_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "old_authority",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "new_authority",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_SET_CLAIM_RATE_LIMIT_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "claim_rate_limit",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_SET_DISABLE_FLAGS_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "disable_flags",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_SET_SOCIAL_CLAIM_AUTHORITY_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "social_claim_authority",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_SOCIAL_FEE_PDA_CLAIMED_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "user_id",
|
|
kind: PumpFeesBorshFieldKind::String,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "platform",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "social_fee_pda",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "recipient",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "social_claim_authority",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "amount_claimed",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "claimable_before",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "lifetime_claimed",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "recipient_balance_before",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "recipient_balance_after",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "quote_mint",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_SOCIAL_FEE_PDA_CREATED_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "user_id",
|
|
kind: PumpFeesBorshFieldKind::String,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "platform",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "social_fee_pda",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "created_by",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_SWEEP_BUYBACK_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "index",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "sol_amount",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "token_amount",
|
|
kind: PumpFeesBorshFieldKind::U64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "destination",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "buyback_vault",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "mint",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_UPDATE_ADMIN_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "old_admin",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "new_admin",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_UPDATE_FEE_CONFIG_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "admin",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "fee_config",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "fee_tiers",
|
|
kind: PumpFeesBorshFieldKind::FeeTierVec,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "flat_fees",
|
|
kind: PumpFeesBorshFieldKind::Fees,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_UPDATE_FEE_SHARES_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "mint",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "sharing_config",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "admin",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "new_shareholders",
|
|
kind: PumpFeesBorshFieldKind::ShareholderVec,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "version",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_UPDATE_STABLE_FEE_CONFIG_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "admin",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "fee_config",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "stable_fee_tiers",
|
|
kind: PumpFeesBorshFieldKind::FeeTierVec,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "flat_fees",
|
|
kind: PumpFeesBorshFieldKind::Fees,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_UPSERT_FEE_TIERS_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "admin",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "fee_config",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "fee_tiers",
|
|
kind: PumpFeesBorshFieldKind::FeeTierVec,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "offset",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
];
|
|
|
|
const PUMP_FEES_EVENT_UPSERT_STABLE_FEE_TIERS_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "timestamp",
|
|
kind: PumpFeesBorshFieldKind::I64,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "admin",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "fee_config",
|
|
kind: PumpFeesBorshFieldKind::Pubkey,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "stable_fee_tiers",
|
|
kind: PumpFeesBorshFieldKind::FeeTierVec,
|
|
},
|
|
PumpFeesBorshFieldDescriptor {
|
|
name: "offset",
|
|
kind: PumpFeesBorshFieldKind::U8,
|
|
},
|
|
];
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct PumpFeesInstructionData {
|
|
spec: PumpFeesInstructionSpec,
|
|
discriminator_hex: std::string::String,
|
|
args_json: serde_json::Value,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct PumpFeesAnchorEventData {
|
|
spec: PumpFeesAnchorEventSpec,
|
|
discriminator_hex: std::string::String,
|
|
payload_size: usize,
|
|
fields_json: serde_json::Value,
|
|
}
|
|
|
|
/// Decoded Pump Fees instruction or Anchor event retained for coverage/materialization.
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct PumpFeesInstructionDecoded {
|
|
/// Parent transaction internal id.
|
|
pub transaction_id: i64,
|
|
/// Parent instruction internal id.
|
|
pub instruction_id: i64,
|
|
/// Decoded program id.
|
|
pub program_id: std::string::String,
|
|
/// Normalized event kind.
|
|
pub event_kind: std::string::String,
|
|
/// Best-effort accounting/config account.
|
|
pub pool_account: std::option::Option<std::string::String>,
|
|
/// Best-effort base/subject mint.
|
|
pub token_a_mint: std::option::Option<std::string::String>,
|
|
/// Best-effort quote/fee mint.
|
|
pub token_b_mint: std::option::Option<std::string::String>,
|
|
/// Best-effort secondary account used as lp/config surrogate.
|
|
pub lp_mint: std::option::Option<std::string::String>,
|
|
/// Decoded payload with instruction/event fields and materialization aliases.
|
|
pub payload_json: serde_json::Value,
|
|
}
|
|
|
|
/// Decoded Pump Fees event.
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub enum PumpFeesDecodedEvent {
|
|
/// Decoded on-chain instruction.
|
|
Instruction(PumpFeesInstructionDecoded),
|
|
/// Decoded Anchor `Program data` event.
|
|
AnchorEvent(PumpFeesInstructionDecoded),
|
|
}
|
|
|
|
/// Pump Fees decoder.
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct PumpFeesDecoder;
|
|
|
|
impl PumpFeesDecoder {
|
|
/// Creates a new decoder.
|
|
pub fn new() -> Self {
|
|
return Self;
|
|
}
|
|
|
|
/// Decodes one projected transaction into Pump Fees instruction and Anchor events.
|
|
pub fn decode_transaction(
|
|
&self,
|
|
transaction: &crate::ChainTransactionDto,
|
|
instructions: &[crate::ChainInstructionDto],
|
|
) -> Result<std::vec::Vec<crate::PumpFeesDecodedEvent>, crate::Error> {
|
|
let transaction_id = match transaction.id {
|
|
Some(transaction_id) => transaction_id,
|
|
None => {
|
|
return Err(crate::Error::InvalidState(format!(
|
|
"chain transaction '{}' has no internal id",
|
|
transaction.signature
|
|
)));
|
|
},
|
|
};
|
|
let transaction_json_result =
|
|
serde_json::from_str::<serde_json::Value>(transaction.transaction_json.as_str());
|
|
let transaction_json = match transaction_json_result {
|
|
Ok(transaction_json) => transaction_json,
|
|
Err(error) => {
|
|
return Err(crate::Error::Json(format!(
|
|
"cannot parse transaction_json for signature '{}': {}",
|
|
transaction.signature, error
|
|
)));
|
|
},
|
|
};
|
|
let log_messages = extract_log_messages(&transaction_json);
|
|
let anchor_events_result = decode_pump_fees_anchor_events(log_messages.as_slice());
|
|
let anchor_events = match anchor_events_result {
|
|
Ok(anchor_events) => anchor_events,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let mut decoded_events = std::vec::Vec::new();
|
|
let mut pump_instruction_links = std::vec::Vec::new();
|
|
for instruction in instructions {
|
|
let program_id = match &instruction.program_id {
|
|
Some(program_id) => program_id,
|
|
None => continue,
|
|
};
|
|
if program_id.as_str() != crate::PUMP_FEES_PROGRAM_ID {
|
|
continue;
|
|
}
|
|
let instruction_id = match instruction.id {
|
|
Some(instruction_id) => instruction_id,
|
|
None => continue,
|
|
};
|
|
let instruction_data_result =
|
|
parse_pump_fees_instruction_data(instruction.data_json.as_ref());
|
|
let instruction_data = match instruction_data_result {
|
|
Ok(instruction_data) => instruction_data,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let instruction_data = match instruction_data {
|
|
Some(instruction_data) => instruction_data,
|
|
None => continue,
|
|
};
|
|
let accounts_result = parse_accounts_json(instruction.accounts_json.as_str());
|
|
let accounts = match accounts_result {
|
|
Ok(accounts) => accounts,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let parsed_json_result = parse_optional_parsed_json(instruction.parsed_json.as_ref());
|
|
let parsed_json = match parsed_json_result {
|
|
Ok(parsed_json) => parsed_json,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let accounts_by_name = accounts_by_name_json(parsed_json.as_ref(), accounts.as_slice());
|
|
let mut payload = serde_json::Map::new();
|
|
payload
|
|
.insert("decoder".to_string(), serde_json::Value::String("pump_fees".to_string()));
|
|
payload.insert(
|
|
"signature".to_string(),
|
|
serde_json::Value::String(transaction.signature.clone()),
|
|
);
|
|
payload.insert("instructionId".to_string(), serde_json::json!(instruction_id));
|
|
payload.insert(
|
|
"instructionIndex".to_string(),
|
|
serde_json::json!(instruction.instruction_index),
|
|
);
|
|
if let Some(inner_instruction_index) = instruction.inner_instruction_index {
|
|
payload.insert(
|
|
"innerInstructionIndex".to_string(),
|
|
serde_json::json!(inner_instruction_index),
|
|
);
|
|
}
|
|
payload.insert("programId".to_string(), serde_json::Value::String(program_id.clone()));
|
|
payload.insert(
|
|
"instructionName".to_string(),
|
|
serde_json::Value::String(instruction_data.spec.name.to_string()),
|
|
);
|
|
payload.insert(
|
|
"discriminatorHex".to_string(),
|
|
serde_json::Value::String(instruction_data.discriminator_hex.clone()),
|
|
);
|
|
payload.insert("decodedArguments".to_string(), instruction_data.args_json.clone());
|
|
payload.insert("accounts".to_string(), serde_json::json!(accounts));
|
|
payload.insert("accountsByName".to_string(), accounts_by_name.clone());
|
|
add_materialization_aliases(
|
|
&mut payload,
|
|
instruction_data.spec.name,
|
|
&instruction_data.args_json,
|
|
&accounts_by_name,
|
|
);
|
|
let payload_json = serde_json::Value::Object(payload);
|
|
let pool_account = payload_string(
|
|
&payload_json,
|
|
&[
|
|
"feeConfig",
|
|
"fee_config",
|
|
"feeProgramGlobal",
|
|
"fee_program_global",
|
|
"sharingConfig",
|
|
"sharing_config",
|
|
"socialFeePda",
|
|
"social_fee_pda",
|
|
"donationFeePda",
|
|
"donation_fee_pda",
|
|
"buybackVault",
|
|
"buyback_vault",
|
|
"global",
|
|
],
|
|
);
|
|
let token_a_mint = payload_string(&payload_json, &["mint", "baseMint", "base_mint"]);
|
|
let token_b_mint = match payload_string(&payload_json, &["quoteMint", "quote_mint"]) {
|
|
Some(quote_mint) => Some(quote_mint),
|
|
None => {
|
|
if event_kind_is_buyback_or_fee(instruction_data.spec.name) {
|
|
Some(crate::WSOL_MINT_ID.to_string())
|
|
} else {
|
|
None
|
|
}
|
|
},
|
|
};
|
|
let lp_mint = payload_string(
|
|
&payload_json,
|
|
&[
|
|
"sharingConfig",
|
|
"sharing_config",
|
|
"buybackVaultAta",
|
|
"buyback_vault_ata",
|
|
"socialFeePda",
|
|
"social_fee_pda",
|
|
"donationFeePda",
|
|
"donation_fee_pda",
|
|
],
|
|
);
|
|
decoded_events.push(crate::PumpFeesDecodedEvent::Instruction(
|
|
PumpFeesInstructionDecoded {
|
|
transaction_id,
|
|
instruction_id,
|
|
program_id: program_id.clone(),
|
|
event_kind: format!("pump_fees.{}", instruction_data.spec.name),
|
|
pool_account,
|
|
token_a_mint,
|
|
token_b_mint,
|
|
lp_mint,
|
|
payload_json,
|
|
},
|
|
));
|
|
pump_instruction_links.push((
|
|
instruction.instruction_index,
|
|
instruction_id,
|
|
accounts_by_name,
|
|
));
|
|
}
|
|
for (event_index, anchor_event) in anchor_events.iter().enumerate() {
|
|
let instruction_id = match instruction_id_for_anchor_event(
|
|
event_index,
|
|
pump_instruction_links.as_slice(),
|
|
) {
|
|
Some(instruction_id) => instruction_id,
|
|
None => continue,
|
|
};
|
|
let accounts_by_name =
|
|
accounts_by_name_for_anchor_event(event_index, pump_instruction_links.as_slice());
|
|
let mut payload = serde_json::Map::new();
|
|
payload
|
|
.insert("decoder".to_string(), serde_json::Value::String("pump_fees".to_string()));
|
|
payload.insert(
|
|
"signature".to_string(),
|
|
serde_json::Value::String(transaction.signature.clone()),
|
|
);
|
|
payload.insert("instructionId".to_string(), serde_json::json!(instruction_id));
|
|
payload.insert("anchorEventIndex".to_string(), serde_json::json!(event_index));
|
|
payload.insert(
|
|
"programId".to_string(),
|
|
serde_json::Value::String(crate::PUMP_FEES_PROGRAM_ID.to_string()),
|
|
);
|
|
payload.insert(
|
|
"eventName".to_string(),
|
|
serde_json::Value::String(anchor_event.spec.name.to_string()),
|
|
);
|
|
payload.insert(
|
|
"discriminatorHex".to_string(),
|
|
serde_json::Value::String(anchor_event.discriminator_hex.clone()),
|
|
);
|
|
payload.insert(
|
|
"anchorEventPayloadSize".to_string(),
|
|
serde_json::json!(anchor_event.payload_size),
|
|
);
|
|
payload.insert("decodedEvent".to_string(), anchor_event.fields_json.clone());
|
|
add_materialization_aliases(
|
|
&mut payload,
|
|
anchor_event.spec.name,
|
|
&anchor_event.fields_json,
|
|
&accounts_by_name,
|
|
);
|
|
let payload_json = serde_json::Value::Object(payload);
|
|
let pool_account = payload_string(
|
|
&payload_json,
|
|
&[
|
|
"feeConfig",
|
|
"fee_config",
|
|
"feeProgramGlobal",
|
|
"fee_program_global",
|
|
"sharingConfig",
|
|
"sharing_config",
|
|
"socialFeePda",
|
|
"social_fee_pda",
|
|
"donationFeePda",
|
|
"donation_fee_pda",
|
|
"buybackVault",
|
|
"buyback_vault",
|
|
"global",
|
|
],
|
|
);
|
|
let token_a_mint = payload_string(&payload_json, &["mint", "baseMint", "base_mint"]);
|
|
let token_b_mint = payload_string(&payload_json, &["quoteMint", "quote_mint"]);
|
|
let lp_mint = payload_string(
|
|
&payload_json,
|
|
&[
|
|
"sharingConfig",
|
|
"sharing_config",
|
|
"socialFeePda",
|
|
"social_fee_pda",
|
|
"donationFeePda",
|
|
"donation_fee_pda",
|
|
"buybackVault",
|
|
"buyback_vault",
|
|
],
|
|
);
|
|
decoded_events.push(crate::PumpFeesDecodedEvent::AnchorEvent(
|
|
PumpFeesInstructionDecoded {
|
|
transaction_id,
|
|
instruction_id,
|
|
program_id: crate::PUMP_FEES_PROGRAM_ID.to_string(),
|
|
event_kind: anchor_event.spec.event_kind.to_string(),
|
|
pool_account,
|
|
token_a_mint,
|
|
token_b_mint,
|
|
lp_mint,
|
|
payload_json,
|
|
},
|
|
));
|
|
}
|
|
return Ok(decoded_events);
|
|
}
|
|
}
|
|
|
|
fn pump_fees_instruction_spec(
|
|
discriminator: [u8; 8],
|
|
) -> std::option::Option<PumpFeesInstructionSpec> {
|
|
match discriminator {
|
|
PUMP_FEES_CLAIM_SOCIAL_FEE_PDA_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "claim_social_fee_pda",
|
|
fields: PUMP_FEES_INSTRUCTION_CLAIM_SOCIAL_FEE_PDA_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_CLAIM_SOCIAL_FEE_PDA_V2_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "claim_social_fee_pda_v2",
|
|
fields: PUMP_FEES_INSTRUCTION_CLAIM_SOCIAL_FEE_PDA_V2_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_CRANK_DONATION_FEE_PDA_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "crank_donation_fee_pda",
|
|
fields: PUMP_FEES_INSTRUCTION_CRANK_DONATION_FEE_PDA_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_CREATE_DONATION_FEE_PDA_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "create_donation_fee_pda",
|
|
fields: PUMP_FEES_INSTRUCTION_CREATE_DONATION_FEE_PDA_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_CREATE_FEE_SHARING_CONFIG_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "create_fee_sharing_config",
|
|
fields: PUMP_FEES_INSTRUCTION_CREATE_FEE_SHARING_CONFIG_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_CREATE_SOCIAL_FEE_PDA_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "create_social_fee_pda",
|
|
fields: PUMP_FEES_INSTRUCTION_CREATE_SOCIAL_FEE_PDA_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_EXTEND_FEE_CONFIG_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "extend_fee_config",
|
|
fields: PUMP_FEES_INSTRUCTION_EXTEND_FEE_CONFIG_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_GET_FEES_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "get_fees",
|
|
fields: PUMP_FEES_INSTRUCTION_GET_FEES_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_INITIALIZE_BUYBACK_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "initialize_buyback",
|
|
fields: PUMP_FEES_INSTRUCTION_INITIALIZE_BUYBACK_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_INITIALIZE_FEE_CONFIG_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "initialize_fee_config",
|
|
fields: PUMP_FEES_INSTRUCTION_INITIALIZE_FEE_CONFIG_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_INITIALIZE_FEE_PROGRAM_GLOBAL_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "initialize_fee_program_global",
|
|
fields: PUMP_FEES_INSTRUCTION_INITIALIZE_FEE_PROGRAM_GLOBAL_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_RESET_FEE_SHARING_CONFIG_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "reset_fee_sharing_config",
|
|
fields: PUMP_FEES_INSTRUCTION_RESET_FEE_SHARING_CONFIG_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_RESET_FEE_SHARING_CONFIG_V2_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "reset_fee_sharing_config_v2",
|
|
fields: PUMP_FEES_INSTRUCTION_RESET_FEE_SHARING_CONFIG_V2_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "revoke_fee_sharing_authority",
|
|
fields: PUMP_FEES_INSTRUCTION_REVOKE_FEE_SHARING_AUTHORITY_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_SET_AUTHORITY_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "set_authority",
|
|
fields: PUMP_FEES_INSTRUCTION_SET_AUTHORITY_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_SET_CLAIM_RATE_LIMIT_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "set_claim_rate_limit",
|
|
fields: PUMP_FEES_INSTRUCTION_SET_CLAIM_RATE_LIMIT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_SET_DISABLE_FLAGS_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "set_disable_flags",
|
|
fields: PUMP_FEES_INSTRUCTION_SET_DISABLE_FLAGS_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_SET_SOCIAL_CLAIM_AUTHORITY_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "set_social_claim_authority",
|
|
fields: PUMP_FEES_INSTRUCTION_SET_SOCIAL_CLAIM_AUTHORITY_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_SWEEP_BUYBACK_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "sweep_buyback",
|
|
fields: PUMP_FEES_INSTRUCTION_SWEEP_BUYBACK_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "transfer_fee_sharing_authority",
|
|
fields: PUMP_FEES_INSTRUCTION_TRANSFER_FEE_SHARING_AUTHORITY_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPDATE_ADMIN_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "update_admin",
|
|
fields: PUMP_FEES_INSTRUCTION_UPDATE_ADMIN_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPDATE_BUYBACK_AUTHORITY_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "update_buyback_authority",
|
|
fields: PUMP_FEES_INSTRUCTION_UPDATE_BUYBACK_AUTHORITY_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPDATE_BUYBACK_CLAIM_RATE_LIMIT_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "update_buyback_claim_rate_limit",
|
|
fields: PUMP_FEES_INSTRUCTION_UPDATE_BUYBACK_CLAIM_RATE_LIMIT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPDATE_FEE_CONFIG_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "update_fee_config",
|
|
fields: PUMP_FEES_INSTRUCTION_UPDATE_FEE_CONFIG_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPDATE_FEE_SHARES_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "update_fee_shares",
|
|
fields: PUMP_FEES_INSTRUCTION_UPDATE_FEE_SHARES_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPDATE_FEE_SHARES_V2_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "update_fee_shares_v2",
|
|
fields: PUMP_FEES_INSTRUCTION_UPDATE_FEE_SHARES_V2_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPDATE_STABLE_FEE_CONFIG_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "update_stable_fee_config",
|
|
fields: PUMP_FEES_INSTRUCTION_UPDATE_STABLE_FEE_CONFIG_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPSERT_FEE_TIERS_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "upsert_fee_tiers",
|
|
fields: PUMP_FEES_INSTRUCTION_UPSERT_FEE_TIERS_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPSERT_STABLE_FEE_TIERS_DISCRIMINATOR => {
|
|
return Some(PumpFeesInstructionSpec {
|
|
name: "upsert_stable_fee_tiers",
|
|
fields: PUMP_FEES_INSTRUCTION_UPSERT_STABLE_FEE_TIERS_FIELDS,
|
|
});
|
|
},
|
|
_ => return None,
|
|
}
|
|
}
|
|
|
|
fn pump_fees_anchor_event_spec(
|
|
discriminator: [u8; 8],
|
|
) -> std::option::Option<PumpFeesAnchorEventSpec> {
|
|
match discriminator {
|
|
PUMP_FEES_CREATE_FEE_SHARING_CONFIG_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "CreateFeeSharingConfigEvent",
|
|
event_kind: "pump_fees.create_fee_sharing_config_event",
|
|
fields: PUMP_FEES_EVENT_CREATE_FEE_SHARING_CONFIG_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_DONATION_FEE_PDA_CRANKED_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "DonationFeePdaCranked",
|
|
event_kind: "pump_fees.donation_fee_pda_cranked",
|
|
fields: PUMP_FEES_EVENT_DONATION_FEE_PDA_CRANKED_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_DONATION_FEE_PDA_CREATED_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "DonationFeePdaCreated",
|
|
event_kind: "pump_fees.donation_fee_pda_created",
|
|
fields: PUMP_FEES_EVENT_DONATION_FEE_PDA_CREATED_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_EXTEND_FEE_CONFIG_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "ExtendFeeConfigEvent",
|
|
event_kind: "pump_fees.extend_fee_config_event",
|
|
fields: PUMP_FEES_EVENT_EXTEND_FEE_CONFIG_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_INITIALIZE_FEE_CONFIG_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "InitializeFeeConfigEvent",
|
|
event_kind: "pump_fees.initialize_fee_config_event",
|
|
fields: PUMP_FEES_EVENT_INITIALIZE_FEE_CONFIG_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_INITIALIZE_FEE_PROGRAM_GLOBAL_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "InitializeFeeProgramGlobalEvent",
|
|
event_kind: "pump_fees.initialize_fee_program_global_event",
|
|
fields: PUMP_FEES_EVENT_INITIALIZE_FEE_PROGRAM_GLOBAL_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_RESET_FEE_SHARING_CONFIG_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "ResetFeeSharingConfigEvent",
|
|
event_kind: "pump_fees.reset_fee_sharing_config_event",
|
|
fields: PUMP_FEES_EVENT_RESET_FEE_SHARING_CONFIG_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_SET_AUTHORITY_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "SetAuthorityEvent",
|
|
event_kind: "pump_fees.set_authority_event",
|
|
fields: PUMP_FEES_EVENT_SET_AUTHORITY_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_SET_CLAIM_RATE_LIMIT_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "SetClaimRateLimitEvent",
|
|
event_kind: "pump_fees.set_claim_rate_limit_event",
|
|
fields: PUMP_FEES_EVENT_SET_CLAIM_RATE_LIMIT_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_SET_DISABLE_FLAGS_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "SetDisableFlagsEvent",
|
|
event_kind: "pump_fees.set_disable_flags_event",
|
|
fields: PUMP_FEES_EVENT_SET_DISABLE_FLAGS_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_SET_SOCIAL_CLAIM_AUTHORITY_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "SetSocialClaimAuthorityEvent",
|
|
event_kind: "pump_fees.set_social_claim_authority_event",
|
|
fields: PUMP_FEES_EVENT_SET_SOCIAL_CLAIM_AUTHORITY_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_SOCIAL_FEE_PDA_CLAIMED_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "SocialFeePdaClaimed",
|
|
event_kind: "pump_fees.social_fee_pda_claimed",
|
|
fields: PUMP_FEES_EVENT_SOCIAL_FEE_PDA_CLAIMED_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_SOCIAL_FEE_PDA_CREATED_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "SocialFeePdaCreated",
|
|
event_kind: "pump_fees.social_fee_pda_created",
|
|
fields: PUMP_FEES_EVENT_SOCIAL_FEE_PDA_CREATED_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_SWEEP_BUYBACK_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "SweepBuybackEvent",
|
|
event_kind: "pump_fees.sweep_buyback_event",
|
|
fields: PUMP_FEES_EVENT_SWEEP_BUYBACK_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPDATE_ADMIN_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "UpdateAdminEvent",
|
|
event_kind: "pump_fees.update_admin_event",
|
|
fields: PUMP_FEES_EVENT_UPDATE_ADMIN_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPDATE_FEE_CONFIG_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "UpdateFeeConfigEvent",
|
|
event_kind: "pump_fees.update_fee_config_event",
|
|
fields: PUMP_FEES_EVENT_UPDATE_FEE_CONFIG_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPDATE_FEE_SHARES_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "UpdateFeeSharesEvent",
|
|
event_kind: "pump_fees.update_fee_shares_event",
|
|
fields: PUMP_FEES_EVENT_UPDATE_FEE_SHARES_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPDATE_STABLE_FEE_CONFIG_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "UpdateStableFeeConfigEvent",
|
|
event_kind: "pump_fees.update_stable_fee_config_event",
|
|
fields: PUMP_FEES_EVENT_UPDATE_STABLE_FEE_CONFIG_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPSERT_FEE_TIERS_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "UpsertFeeTiersEvent",
|
|
event_kind: "pump_fees.upsert_fee_tiers_event",
|
|
fields: PUMP_FEES_EVENT_UPSERT_FEE_TIERS_EVENT_FIELDS,
|
|
});
|
|
},
|
|
PUMP_FEES_UPSERT_STABLE_FEE_TIERS_EVENT_DISCRIMINATOR => {
|
|
return Some(PumpFeesAnchorEventSpec {
|
|
name: "UpsertStableFeeTiersEvent",
|
|
event_kind: "pump_fees.upsert_stable_fee_tiers_event",
|
|
fields: PUMP_FEES_EVENT_UPSERT_STABLE_FEE_TIERS_EVENT_FIELDS,
|
|
});
|
|
},
|
|
_ => return None,
|
|
}
|
|
}
|
|
|
|
fn parse_pump_fees_instruction_data(
|
|
data_json: std::option::Option<&std::string::String>,
|
|
) -> Result<std::option::Option<PumpFeesInstructionData>, crate::Error> {
|
|
let data_text = match decode_instruction_data_text(data_json) {
|
|
Some(data_text) => data_text,
|
|
None => return Ok(None),
|
|
};
|
|
let decoded_result = bs58::decode(data_text.trim()).into_vec();
|
|
let decoded = match decoded_result {
|
|
Ok(decoded) => decoded,
|
|
Err(error) => {
|
|
return Err(crate::Error::InvalidState(format!(
|
|
"cannot base58-decode pump_fees instruction data: {}",
|
|
error
|
|
)));
|
|
},
|
|
};
|
|
if decoded.len() < 8 {
|
|
return Ok(None);
|
|
}
|
|
let discriminator = match read_8_byte_array(decoded.as_slice(), 0) {
|
|
Some(discriminator) => discriminator,
|
|
None => return Ok(None),
|
|
};
|
|
let spec = match pump_fees_instruction_spec(discriminator) {
|
|
Some(spec) => spec,
|
|
None => return Ok(None),
|
|
};
|
|
let decoded_fields_result = decode_borsh_fields(&decoded[8..], spec.fields);
|
|
let decoded_fields = match decoded_fields_result {
|
|
Ok(decoded_fields) => decoded_fields,
|
|
Err(error) => {
|
|
tracing::debug!(
|
|
decoder = "pump_fees",
|
|
discriminator_hex = bytes_to_hex(discriminator.as_slice()),
|
|
payload_size = decoded.len().saturating_sub(8),
|
|
error = %error,
|
|
"ignoring malformed pump_fees instruction payload"
|
|
);
|
|
return Ok(None);
|
|
},
|
|
};
|
|
return Ok(Some(PumpFeesInstructionData {
|
|
spec,
|
|
discriminator_hex: bytes_to_hex(discriminator.as_slice()),
|
|
args_json: decoded_fields,
|
|
}));
|
|
}
|
|
|
|
fn decode_pump_fees_anchor_events(
|
|
log_messages: &[std::string::String],
|
|
) -> Result<std::vec::Vec<PumpFeesAnchorEventData>, crate::Error> {
|
|
let mut events = std::vec::Vec::new();
|
|
for message in log_messages {
|
|
let encoded = match extract_program_data_base64(message.as_str()) {
|
|
Some(encoded) => encoded,
|
|
None => continue,
|
|
};
|
|
let event_result = decode_pump_fees_anchor_event_from_base64(encoded);
|
|
let event_option = match event_result {
|
|
Ok(event_option) => event_option,
|
|
Err(error) => return Err(error),
|
|
};
|
|
if let Some(event) = event_option {
|
|
events.push(event);
|
|
}
|
|
}
|
|
return Ok(events);
|
|
}
|
|
|
|
fn decode_pump_fees_anchor_event_from_base64(
|
|
encoded: &str,
|
|
) -> Result<std::option::Option<PumpFeesAnchorEventData>, crate::Error> {
|
|
let decoded = decode_base64_standard(encoded);
|
|
let decoded = match decoded {
|
|
Some(decoded) => decoded,
|
|
None => return Ok(None),
|
|
};
|
|
let payload = if decoded.len() >= 16
|
|
&& read_8_byte_array(decoded.as_slice(), 0)
|
|
== Some(PUMP_FEES_ANCHOR_SELF_CPI_LOG_DISCRIMINATOR)
|
|
{
|
|
&decoded[8..]
|
|
} else {
|
|
decoded.as_slice()
|
|
};
|
|
if payload.len() < 8 {
|
|
return Ok(None);
|
|
}
|
|
let discriminator = match read_8_byte_array(payload, 0) {
|
|
Some(discriminator) => discriminator,
|
|
None => return Ok(None),
|
|
};
|
|
let spec = match pump_fees_anchor_event_spec(discriminator) {
|
|
Some(spec) => spec,
|
|
None => return Ok(None),
|
|
};
|
|
let fields_result = decode_borsh_fields(&payload[8..], spec.fields);
|
|
let fields_json = match fields_result {
|
|
Ok(fields_json) => fields_json,
|
|
Err(error) => {
|
|
tracing::debug!(
|
|
decoder = "pump_fees",
|
|
discriminator_hex = bytes_to_hex(discriminator.as_slice()),
|
|
payload_size = payload.len().saturating_sub(8),
|
|
error = %error,
|
|
"ignoring malformed pump_fees anchor event payload"
|
|
);
|
|
return Ok(None);
|
|
},
|
|
};
|
|
return Ok(Some(PumpFeesAnchorEventData {
|
|
spec,
|
|
discriminator_hex: bytes_to_hex(discriminator.as_slice()),
|
|
payload_size: payload.len().saturating_sub(8),
|
|
fields_json,
|
|
}));
|
|
}
|
|
|
|
fn decode_borsh_fields(
|
|
data: &[u8],
|
|
fields: &[PumpFeesBorshFieldDescriptor],
|
|
) -> Result<serde_json::Value, crate::Error> {
|
|
let mut offset = 0_usize;
|
|
let mut object = serde_json::Map::new();
|
|
for field in fields {
|
|
let decoded_result = read_borsh_field_value(data, offset, field.kind);
|
|
let (value, next_offset) = match decoded_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
offset = next_offset;
|
|
object.insert(field.name.to_string(), value);
|
|
}
|
|
object.insert("payloadBytesConsumed".to_string(), serde_json::json!(offset));
|
|
object.insert("payloadBytesTotal".to_string(), serde_json::json!(data.len()));
|
|
return Ok(serde_json::Value::Object(object));
|
|
}
|
|
|
|
fn read_borsh_field_value(
|
|
data: &[u8],
|
|
offset: usize,
|
|
kind: PumpFeesBorshFieldKind,
|
|
) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
match kind {
|
|
PumpFeesBorshFieldKind::U8 => return read_u8_value(data, offset),
|
|
PumpFeesBorshFieldKind::U64 => return read_u64_value(data, offset),
|
|
PumpFeesBorshFieldKind::I64 => return read_i64_value(data, offset),
|
|
PumpFeesBorshFieldKind::U128 => return read_u128_value(data, offset),
|
|
PumpFeesBorshFieldKind::Bool => return read_bool_value(data, offset),
|
|
PumpFeesBorshFieldKind::Pubkey => return read_pubkey_value(data, offset),
|
|
PumpFeesBorshFieldKind::String => return read_string_value(data, offset),
|
|
PumpFeesBorshFieldKind::OptionBool => return read_option_bool_value(data, offset),
|
|
PumpFeesBorshFieldKind::OptionPubkey => return read_option_pubkey_value(data, offset),
|
|
PumpFeesBorshFieldKind::Fees => return read_fees_value(data, offset),
|
|
PumpFeesBorshFieldKind::ConfigStatus => return read_config_status_value(data, offset),
|
|
PumpFeesBorshFieldKind::ShareholderVec => return read_shareholder_vec_value(data, offset),
|
|
PumpFeesBorshFieldKind::FeeTierVec => return read_fee_tier_vec_value(data, offset),
|
|
}
|
|
}
|
|
|
|
fn read_u8_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
if offset.saturating_add(1) > data.len() {
|
|
return Err(crate::Error::InvalidState("pump_fees u8 field exceeds payload".to_string()));
|
|
}
|
|
return Ok((serde_json::json!(data[offset]), offset.saturating_add(1)));
|
|
}
|
|
|
|
fn read_u16_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
if offset.saturating_add(2) > data.len() {
|
|
return Err(crate::Error::InvalidState("pump_fees u16 field exceeds payload".to_string()));
|
|
}
|
|
let mut bytes = [0_u8; 2];
|
|
bytes.copy_from_slice(&data[offset..offset + 2]);
|
|
return Ok((serde_json::json!(u16::from_le_bytes(bytes)), offset.saturating_add(2)));
|
|
}
|
|
|
|
fn read_u32_raw(data: &[u8], offset: usize) -> Result<(u32, usize), crate::Error> {
|
|
if offset.saturating_add(4) > data.len() {
|
|
return Err(crate::Error::InvalidState("pump_fees u32 field exceeds payload".to_string()));
|
|
}
|
|
let mut bytes = [0_u8; 4];
|
|
bytes.copy_from_slice(&data[offset..offset + 4]);
|
|
return Ok((u32::from_le_bytes(bytes), offset.saturating_add(4)));
|
|
}
|
|
|
|
fn read_u64_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
if offset.saturating_add(8) > data.len() {
|
|
return Err(crate::Error::InvalidState("pump_fees u64 field exceeds payload".to_string()));
|
|
}
|
|
let mut bytes = [0_u8; 8];
|
|
bytes.copy_from_slice(&data[offset..offset + 8]);
|
|
let value = u64::from_le_bytes(bytes);
|
|
return Ok((serde_json::Value::String(value.to_string()), offset.saturating_add(8)));
|
|
}
|
|
|
|
fn read_i64_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
if offset.saturating_add(8) > data.len() {
|
|
return Err(crate::Error::InvalidState("pump_fees i64 field exceeds payload".to_string()));
|
|
}
|
|
let mut bytes = [0_u8; 8];
|
|
bytes.copy_from_slice(&data[offset..offset + 8]);
|
|
let value = i64::from_le_bytes(bytes);
|
|
return Ok((serde_json::Value::String(value.to_string()), offset.saturating_add(8)));
|
|
}
|
|
|
|
fn read_u128_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
if offset.saturating_add(16) > data.len() {
|
|
return Err(crate::Error::InvalidState("pump_fees u128 field exceeds payload".to_string()));
|
|
}
|
|
let mut bytes = [0_u8; 16];
|
|
bytes.copy_from_slice(&data[offset..offset + 16]);
|
|
let value = u128::from_le_bytes(bytes);
|
|
return Ok((serde_json::Value::String(value.to_string()), offset.saturating_add(16)));
|
|
}
|
|
|
|
fn read_bool_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
let value_result = read_u8_value(data, offset);
|
|
let (value, next_offset) = match value_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let numeric = match value.as_u64() {
|
|
Some(numeric) => numeric,
|
|
None => 0,
|
|
};
|
|
return Ok((serde_json::Value::Bool(numeric != 0), next_offset));
|
|
}
|
|
|
|
fn read_pubkey_value(
|
|
data: &[u8],
|
|
offset: usize,
|
|
) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
if offset.saturating_add(32) > data.len() {
|
|
return Err(crate::Error::InvalidState(
|
|
"pump_fees pubkey field exceeds payload".to_string(),
|
|
));
|
|
}
|
|
let encoded = bs58::encode(&data[offset..offset + 32]).into_string();
|
|
return Ok((serde_json::Value::String(encoded), offset.saturating_add(32)));
|
|
}
|
|
|
|
fn read_string_value(
|
|
data: &[u8],
|
|
offset: usize,
|
|
) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
let length_result = read_u32_raw(data, offset);
|
|
let (length, mut current_offset) = match length_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let length_usize = length as usize;
|
|
if current_offset.saturating_add(length_usize) > data.len() {
|
|
return Err(crate::Error::InvalidState(
|
|
"pump_fees string field exceeds payload".to_string(),
|
|
));
|
|
}
|
|
let text_result = std::str::from_utf8(&data[current_offset..current_offset + length_usize]);
|
|
let text = match text_result {
|
|
Ok(text) => text,
|
|
Err(error) => {
|
|
return Err(crate::Error::InvalidState(format!(
|
|
"pump_fees string field is not utf8: {}",
|
|
error
|
|
)));
|
|
},
|
|
};
|
|
current_offset = current_offset.saturating_add(length_usize);
|
|
return Ok((serde_json::Value::String(text.to_string()), current_offset));
|
|
}
|
|
|
|
fn read_option_bool_value(
|
|
data: &[u8],
|
|
offset: usize,
|
|
) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
return read_bool_value(data, offset);
|
|
}
|
|
|
|
fn read_option_pubkey_value(
|
|
data: &[u8],
|
|
offset: usize,
|
|
) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
let tag_result = read_u8_value(data, offset);
|
|
let (tag_value, mut current_offset) = match tag_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let tag = match tag_value.as_u64() {
|
|
Some(tag) => tag,
|
|
None => 0,
|
|
};
|
|
if tag == 0 {
|
|
return Ok((serde_json::Value::Null, current_offset));
|
|
}
|
|
let pubkey_result = read_pubkey_value(data, current_offset);
|
|
let (pubkey, next_offset) = match pubkey_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
current_offset = next_offset;
|
|
return Ok((pubkey, current_offset));
|
|
}
|
|
|
|
fn read_fees_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
let lp_result = read_u64_value(data, offset);
|
|
let (lp_fee_bps, current_offset) = match lp_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let protocol_result = read_u64_value(data, current_offset);
|
|
let (protocol_fee_bps, current_offset) = match protocol_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let creator_result = read_u64_value(data, current_offset);
|
|
let (creator_fee_bps, current_offset) = match creator_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let mut object = serde_json::Map::new();
|
|
object.insert("lp_fee_bps".to_string(), lp_fee_bps);
|
|
object.insert("protocol_fee_bps".to_string(), protocol_fee_bps);
|
|
object.insert("creator_fee_bps".to_string(), creator_fee_bps);
|
|
return Ok((serde_json::Value::Object(object), current_offset));
|
|
}
|
|
|
|
fn read_config_status_value(
|
|
data: &[u8],
|
|
offset: usize,
|
|
) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
let status_result = read_u8_value(data, offset);
|
|
let (status_value, next_offset) = match status_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let status_code = match status_value.as_u64() {
|
|
Some(status_code) => status_code,
|
|
None => 0,
|
|
};
|
|
let status_name = match status_code {
|
|
0 => "paused",
|
|
1 => "active",
|
|
_ => "unknown",
|
|
};
|
|
let mut object = serde_json::Map::new();
|
|
object.insert("code".to_string(), serde_json::json!(status_code));
|
|
object.insert("name".to_string(), serde_json::Value::String(status_name.to_string()));
|
|
return Ok((serde_json::Value::Object(object), next_offset));
|
|
}
|
|
|
|
fn read_shareholder_vec_value(
|
|
data: &[u8],
|
|
offset: usize,
|
|
) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
let length_result = read_u32_raw(data, offset);
|
|
let (length, mut current_offset) = match length_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let mut values = std::vec::Vec::new();
|
|
for _ in 0..length {
|
|
let address_result = read_pubkey_value(data, current_offset);
|
|
let (address, next_offset) = match address_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
current_offset = next_offset;
|
|
let share_result = read_u16_value(data, current_offset);
|
|
let (share_bps, next_offset) = match share_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
current_offset = next_offset;
|
|
let mut object = serde_json::Map::new();
|
|
object.insert("address".to_string(), address);
|
|
object.insert("share_bps".to_string(), share_bps);
|
|
values.push(serde_json::Value::Object(object));
|
|
}
|
|
return Ok((serde_json::Value::Array(values), current_offset));
|
|
}
|
|
|
|
fn read_fee_tier_vec_value(
|
|
data: &[u8],
|
|
offset: usize,
|
|
) -> Result<(serde_json::Value, usize), crate::Error> {
|
|
let length_result = read_u32_raw(data, offset);
|
|
let (length, mut current_offset) = match length_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let mut values = std::vec::Vec::new();
|
|
for _ in 0..length {
|
|
let threshold_result = read_u128_value(data, current_offset);
|
|
let (threshold, next_offset) = match threshold_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
current_offset = next_offset;
|
|
let fees_result = read_fees_value(data, current_offset);
|
|
let (fees, next_offset) = match fees_result {
|
|
Ok(value) => value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
current_offset = next_offset;
|
|
let mut object = serde_json::Map::new();
|
|
object.insert("market_cap_lamports_threshold".to_string(), threshold);
|
|
object.insert("fees".to_string(), fees);
|
|
values.push(serde_json::Value::Object(object));
|
|
}
|
|
return Ok((serde_json::Value::Array(values), current_offset));
|
|
}
|
|
|
|
fn add_materialization_aliases(
|
|
payload: &mut serde_json::Map<std::string::String, serde_json::Value>,
|
|
name: &str,
|
|
decoded: &serde_json::Value,
|
|
accounts_by_name: &serde_json::Value,
|
|
) {
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "mint", "mint");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "base_mint", "baseMint");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "quote_mint", "quoteMint");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "fee_config", "feeConfig");
|
|
copy_payload_alias(
|
|
payload,
|
|
decoded,
|
|
accounts_by_name,
|
|
"fee_program_global",
|
|
"feeProgramGlobal",
|
|
);
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "sharing_config", "sharingConfig");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "social_fee_pda", "socialFeePda");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "donation_fee_pda", "donationFeePda");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "buyback_vault", "buybackVault");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "admin", "admin");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "authority", "authority");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "recipient", "recipient");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "destination", "destination");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "signer", "signer");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "creator", "creator");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "new_authority", "newAuthority");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "new_admin", "newAdmin");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "amount", "amount");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "amount_claimed", "amountClaimed");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "sol_amount", "solAmount");
|
|
copy_payload_alias(payload, decoded, accounts_by_name, "token_amount", "tokenAmount");
|
|
let actor = first_string_value(
|
|
payload,
|
|
&[
|
|
"recipient",
|
|
"destination",
|
|
"signer",
|
|
"authority",
|
|
"admin",
|
|
"creator",
|
|
"newAuthority",
|
|
"newAdmin",
|
|
],
|
|
);
|
|
if let Some(actor) = actor {
|
|
payload.insert("actorWallet".to_string(), serde_json::Value::String(actor));
|
|
}
|
|
if name.contains("SocialFeePdaClaimed")
|
|
|| name == "claim_social_fee_pda"
|
|
|| name == "claim_social_fee_pda_v2"
|
|
{
|
|
if let Some(amount) = first_json_value(payload, &["amountClaimed", "amount"]) {
|
|
payload.insert("rewardAmountRaw".to_string(), amount);
|
|
}
|
|
if let Some(mint) = first_string_value(payload, &["quoteMint", "mint"]) {
|
|
payload.insert("rewardTokenMint".to_string(), serde_json::Value::String(mint));
|
|
}
|
|
}
|
|
if name.contains("DonationFee") || name.contains("Buyback") || name == "sweep_buyback" {
|
|
if let Some(amount) = first_json_value(payload, &["amount", "solAmount", "tokenAmount"]) {
|
|
payload.insert("feeAmountRaw".to_string(), amount);
|
|
}
|
|
if let Some(mint) = first_string_value(payload, &["quoteMint", "mint"]) {
|
|
payload.insert("feeTokenMint".to_string(), serde_json::Value::String(mint));
|
|
}
|
|
}
|
|
payload.insert(
|
|
"pumpFeesMaterializationPolicy".to_string(),
|
|
serde_json::Value::String(materialization_policy_for_name(name).to_string()),
|
|
);
|
|
}
|
|
|
|
fn materialization_policy_for_name(name: &str) -> &'static str {
|
|
if name == "get_fees" {
|
|
return "decoded_only_fee_calculation";
|
|
}
|
|
if name.contains("Claimed") || name.contains("Cranked") || name.contains("Sweep") {
|
|
return "materialize_if_successful_fee_or_reward";
|
|
}
|
|
if name.contains("Config")
|
|
|| name.contains("Authority")
|
|
|| name.contains("Admin")
|
|
|| name.contains("Tier")
|
|
|| name.contains("Disable")
|
|
|| name.contains("Buyback")
|
|
|| name.contains("Sharing")
|
|
|| name.contains("Created")
|
|
|| name.starts_with("create_")
|
|
|| name.starts_with("initialize_")
|
|
|| name.starts_with("update_")
|
|
|| name.starts_with("set_")
|
|
|| name.starts_with("reset_")
|
|
|| name.starts_with("extend_")
|
|
|| name.starts_with("upsert_")
|
|
{
|
|
return "materialize_if_successful_admin_or_lifecycle";
|
|
}
|
|
return "decoded_only_audit";
|
|
}
|
|
|
|
fn event_kind_is_buyback_or_fee(name: &str) -> bool {
|
|
return name.contains("fee") || name.contains("buyback") || name.contains("donation");
|
|
}
|
|
|
|
fn copy_payload_alias(
|
|
payload: &mut serde_json::Map<std::string::String, serde_json::Value>,
|
|
decoded: &serde_json::Value,
|
|
accounts_by_name: &serde_json::Value,
|
|
source_key: &str,
|
|
target_key: &str,
|
|
) {
|
|
if payload.get(target_key).is_some() {
|
|
return;
|
|
}
|
|
if let Some(value) = json_value_by_key(decoded, source_key) {
|
|
payload.insert(target_key.to_string(), value.clone());
|
|
return;
|
|
}
|
|
if let Some(value) = json_value_by_key(accounts_by_name, source_key) {
|
|
payload.insert(target_key.to_string(), value.clone());
|
|
}
|
|
}
|
|
|
|
fn json_value_by_key<'a>(
|
|
value: &'a serde_json::Value,
|
|
key: &str,
|
|
) -> std::option::Option<&'a serde_json::Value> {
|
|
if let Some(object) = value.as_object() {
|
|
if let Some(found) = object.get(key) {
|
|
return Some(found);
|
|
}
|
|
let camel = snake_to_camel(key);
|
|
if let Some(found) = object.get(camel.as_str()) {
|
|
return Some(found);
|
|
}
|
|
}
|
|
return None;
|
|
}
|
|
|
|
fn first_json_value(
|
|
payload: &serde_json::Map<std::string::String, serde_json::Value>,
|
|
keys: &[&str],
|
|
) -> std::option::Option<serde_json::Value> {
|
|
for key in keys {
|
|
if let Some(value) = payload.get(*key) {
|
|
return Some(value.clone());
|
|
}
|
|
}
|
|
return None;
|
|
}
|
|
|
|
fn first_string_value(
|
|
payload: &serde_json::Map<std::string::String, serde_json::Value>,
|
|
keys: &[&str],
|
|
) -> std::option::Option<std::string::String> {
|
|
for key in keys {
|
|
if let Some(value) = payload.get(*key) {
|
|
if let Some(text) = value.as_str() {
|
|
if !text.trim().is_empty() {
|
|
return Some(text.to_string());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return None;
|
|
}
|
|
|
|
fn payload_string(
|
|
payload: &serde_json::Value,
|
|
keys: &[&str],
|
|
) -> std::option::Option<std::string::String> {
|
|
if let Some(object) = payload.as_object() {
|
|
for key in keys {
|
|
if let Some(value) = object.get(*key) {
|
|
if let Some(text) = value.as_str() {
|
|
if !text.trim().is_empty() {
|
|
return Some(text.to_string());
|
|
}
|
|
}
|
|
}
|
|
let camel = snake_to_camel(key);
|
|
if let Some(value) = object.get(camel.as_str()) {
|
|
if let Some(text) = value.as_str() {
|
|
if !text.trim().is_empty() {
|
|
return Some(text.to_string());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for nested_key in &["decodedArguments", "decodedEvent", "accountsByName"] {
|
|
if let Some(nested) = object.get(*nested_key) {
|
|
let nested_value = payload_string(nested, keys);
|
|
if nested_value.is_some() {
|
|
return nested_value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return None;
|
|
}
|
|
|
|
fn snake_to_camel(key: &str) -> std::string::String {
|
|
let mut result = std::string::String::new();
|
|
let mut uppercase_next = false;
|
|
for character in key.chars() {
|
|
if character == '_' {
|
|
uppercase_next = true;
|
|
continue;
|
|
}
|
|
if uppercase_next {
|
|
for upper in character.to_uppercase() {
|
|
result.push(upper);
|
|
}
|
|
uppercase_next = false;
|
|
} else {
|
|
result.push(character);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
fn instruction_id_for_anchor_event(
|
|
event_index: usize,
|
|
links: &[(u32, i64, serde_json::Value)],
|
|
) -> std::option::Option<i64> {
|
|
if links.is_empty() {
|
|
return None;
|
|
}
|
|
if event_index < links.len() {
|
|
return Some(links[event_index].1);
|
|
}
|
|
return Some(links[links.len().saturating_sub(1)].1);
|
|
}
|
|
|
|
fn accounts_by_name_for_anchor_event(
|
|
event_index: usize,
|
|
links: &[(u32, i64, serde_json::Value)],
|
|
) -> serde_json::Value {
|
|
if links.is_empty() {
|
|
return serde_json::json!({});
|
|
}
|
|
if event_index < links.len() {
|
|
return links[event_index].2.clone();
|
|
}
|
|
return links[links.len().saturating_sub(1)].2.clone();
|
|
}
|
|
|
|
fn accounts_by_name_json(
|
|
parsed_json: std::option::Option<&serde_json::Value>,
|
|
accounts: &[std::string::String],
|
|
) -> serde_json::Value {
|
|
let mut object = serde_json::Map::new();
|
|
if let Some(parsed_json) = parsed_json {
|
|
if let Some(accounts_value) = parsed_json.get("accounts") {
|
|
if let Some(accounts_array) = accounts_value.as_array() {
|
|
for account in accounts_array {
|
|
let name = account.get("name").and_then(serde_json::Value::as_str);
|
|
let pubkey = account.get("pubkey").and_then(serde_json::Value::as_str);
|
|
if let (Some(name), Some(pubkey)) = (name, pubkey) {
|
|
object.insert(
|
|
name.to_string(),
|
|
serde_json::Value::String(pubkey.to_string()),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
let fallback_names = [
|
|
"account0",
|
|
"account1",
|
|
"account2",
|
|
"account3",
|
|
"account4",
|
|
"account5",
|
|
"account6",
|
|
"account7",
|
|
"account8",
|
|
"account9",
|
|
"account10",
|
|
"account11",
|
|
"account12",
|
|
"account13",
|
|
"account14",
|
|
"account15",
|
|
"account16",
|
|
"account17",
|
|
"account18",
|
|
"account19",
|
|
"account20",
|
|
];
|
|
for (index, account) in accounts.iter().enumerate() {
|
|
if index >= fallback_names.len() {
|
|
continue;
|
|
}
|
|
object
|
|
.insert(fallback_names[index].to_string(), serde_json::Value::String(account.clone()));
|
|
}
|
|
return serde_json::Value::Object(object);
|
|
}
|
|
|
|
fn extract_program_data_base64(message: &str) -> std::option::Option<&str> {
|
|
let trimmed = message.trim();
|
|
if let Some(rest) = trimmed.strip_prefix("Program data: ") {
|
|
return Some(rest.trim());
|
|
}
|
|
return None;
|
|
}
|
|
|
|
fn decode_base64_standard(encoded: &str) -> std::option::Option<std::vec::Vec<u8>> {
|
|
use base64::Engine as _;
|
|
let decoded = base64::engine::general_purpose::STANDARD.decode(encoded.as_bytes());
|
|
match decoded {
|
|
Ok(decoded) => return Some(decoded),
|
|
Err(_) => return None,
|
|
}
|
|
}
|
|
|
|
fn extract_log_messages(
|
|
transaction_json: &serde_json::Value,
|
|
) -> std::vec::Vec<std::string::String> {
|
|
let mut messages = std::vec::Vec::new();
|
|
let meta = match transaction_json.get("meta") {
|
|
Some(meta) => meta,
|
|
None => return messages,
|
|
};
|
|
let logs = match meta.get("logMessages") {
|
|
Some(logs) => logs,
|
|
None => return messages,
|
|
};
|
|
let logs_array = match logs.as_array() {
|
|
Some(logs_array) => logs_array,
|
|
None => return messages,
|
|
};
|
|
for value in logs_array {
|
|
if let Some(text) = value.as_str() {
|
|
messages.push(text.to_string());
|
|
}
|
|
}
|
|
return messages;
|
|
}
|
|
|
|
fn parse_accounts_json(
|
|
accounts_json: &str,
|
|
) -> Result<std::vec::Vec<std::string::String>, crate::Error> {
|
|
let values_result = serde_json::from_str::<std::vec::Vec<serde_json::Value>>(accounts_json);
|
|
let values = match values_result {
|
|
Ok(values) => values,
|
|
Err(error) => {
|
|
return Err(crate::Error::Json(format!(
|
|
"cannot parse pump_fees accounts_json: {}",
|
|
error
|
|
)));
|
|
},
|
|
};
|
|
let mut accounts = std::vec::Vec::new();
|
|
for value in values {
|
|
if let Some(text) = value.as_str() {
|
|
accounts.push(text.to_string());
|
|
} else if let Some(object) = value.as_object() {
|
|
if let Some(pubkey) = object.get("pubkey").and_then(serde_json::Value::as_str) {
|
|
accounts.push(pubkey.to_string());
|
|
}
|
|
}
|
|
}
|
|
return Ok(accounts);
|
|
}
|
|
|
|
fn parse_optional_parsed_json(
|
|
parsed_json: std::option::Option<&std::string::String>,
|
|
) -> Result<std::option::Option<serde_json::Value>, crate::Error> {
|
|
let text = match parsed_json {
|
|
Some(text) => text,
|
|
None => return Ok(None),
|
|
};
|
|
let parsed = serde_json::from_str::<serde_json::Value>(text.as_str());
|
|
match parsed {
|
|
Ok(parsed) => return Ok(Some(parsed)),
|
|
Err(error) => {
|
|
return Err(crate::Error::Json(format!(
|
|
"cannot parse pump_fees parsed_json: {}",
|
|
error
|
|
)));
|
|
},
|
|
}
|
|
}
|
|
|
|
fn decode_instruction_data_text(
|
|
data_json: std::option::Option<&std::string::String>,
|
|
) -> std::option::Option<std::string::String> {
|
|
let data_json = match data_json {
|
|
Some(data_json) => data_json,
|
|
None => return None,
|
|
};
|
|
let value_result = serde_json::from_str::<serde_json::Value>(data_json.as_str());
|
|
if let Ok(value) = value_result {
|
|
if let Some(text) = value.as_str() {
|
|
return Some(text.to_string());
|
|
}
|
|
if let Some(object) = value.as_object() {
|
|
if let Some(text) = object.get("data").and_then(serde_json::Value::as_str) {
|
|
return Some(text.to_string());
|
|
}
|
|
}
|
|
}
|
|
let trimmed = data_json.trim();
|
|
if trimmed.is_empty() {
|
|
return None;
|
|
}
|
|
return Some(trimmed.to_string());
|
|
}
|
|
|
|
fn read_8_byte_array(data: &[u8], offset: usize) -> std::option::Option<[u8; 8]> {
|
|
if offset.saturating_add(8) > data.len() {
|
|
return None;
|
|
}
|
|
let mut bytes = [0_u8; 8];
|
|
bytes.copy_from_slice(&data[offset..offset + 8]);
|
|
return Some(bytes);
|
|
}
|
|
|
|
fn bytes_to_hex(data: &[u8]) -> std::string::String {
|
|
let mut text = std::string::String::new();
|
|
for byte in data {
|
|
text.push_str(format!("{:02x}", byte).as_str());
|
|
}
|
|
return text;
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
#[test]
|
|
fn decodes_get_fees_instruction_args() {
|
|
let mut data = std::vec::Vec::new();
|
|
data.extend_from_slice(&super::PUMP_FEES_GET_FEES_DISCRIMINATOR);
|
|
data.push(1_u8);
|
|
data.extend_from_slice(&123456789_u128.to_le_bytes());
|
|
data.extend_from_slice(&42_u64.to_le_bytes());
|
|
data.push(0_u8);
|
|
let encoded = bs58::encode(data).into_string();
|
|
let data_json = serde_json::json!(encoded).to_string();
|
|
let decoded_result = super::parse_pump_fees_instruction_data(Some(&data_json));
|
|
let decoded = match decoded_result {
|
|
Ok(Some(decoded)) => decoded,
|
|
Ok(None) => panic!("expected decoded get_fees"),
|
|
Err(error) => panic!("unexpected decode error: {}", error),
|
|
};
|
|
assert_eq!(decoded.spec.name, "get_fees");
|
|
assert_eq!(decoded.discriminator_hex, "e7257e55cf5b3f34");
|
|
assert_eq!(decoded.args_json["is_pump_pool"], serde_json::json!(true));
|
|
assert_eq!(decoded.args_json["market_cap_lamports"], serde_json::json!("123456789"));
|
|
assert_eq!(decoded.args_json["trade_size_lamports"], serde_json::json!("42"));
|
|
assert_eq!(decoded.args_json["is_new_quote_mint"], serde_json::json!(false));
|
|
}
|
|
|
|
#[test]
|
|
fn decodes_update_fee_shares_shareholders() {
|
|
let mut data = std::vec::Vec::new();
|
|
data.extend_from_slice(&super::PUMP_FEES_UPDATE_FEE_SHARES_DISCRIMINATOR);
|
|
data.extend_from_slice(&1_u32.to_le_bytes());
|
|
data.extend_from_slice(&[7_u8; 32]);
|
|
data.extend_from_slice(&2500_u16.to_le_bytes());
|
|
let encoded = bs58::encode(data).into_string();
|
|
let data_json = serde_json::json!(encoded).to_string();
|
|
let decoded_result = super::parse_pump_fees_instruction_data(Some(&data_json));
|
|
let decoded = match decoded_result {
|
|
Ok(Some(decoded)) => decoded,
|
|
Ok(None) => panic!("expected decoded update_fee_shares"),
|
|
Err(error) => panic!("unexpected decode error: {}", error),
|
|
};
|
|
assert_eq!(decoded.spec.name, "update_fee_shares");
|
|
assert_eq!(decoded.discriminator_hex, "bd0d8863bba4ed23");
|
|
assert_eq!(decoded.args_json["shareholders"][0]["share_bps"], serde_json::json!(2500));
|
|
}
|
|
|
|
#[test]
|
|
fn decodes_social_fee_claimed_anchor_event() {
|
|
let mut data = pump_fees_anchor_event_prefix(
|
|
&super::PUMP_FEES_SOCIAL_FEE_PDA_CLAIMED_DISCRIMINATOR,
|
|
);
|
|
data.extend_from_slice(&123_i64.to_le_bytes());
|
|
let user = "user-1".as_bytes();
|
|
data.extend_from_slice(&(user.len() as u32).to_le_bytes());
|
|
data.extend_from_slice(user);
|
|
data.push(2_u8);
|
|
data.extend_from_slice(&[1_u8; 32]);
|
|
data.extend_from_slice(&[2_u8; 32]);
|
|
data.extend_from_slice(&[3_u8; 32]);
|
|
data.extend_from_slice(&99_u64.to_le_bytes());
|
|
data.extend_from_slice(&100_u64.to_le_bytes());
|
|
data.extend_from_slice(&101_u64.to_le_bytes());
|
|
data.extend_from_slice(&102_u64.to_le_bytes());
|
|
data.extend_from_slice(&201_u64.to_le_bytes());
|
|
data.extend_from_slice(&[4_u8; 32]);
|
|
let decoded = decode_synthetic_anchor_event(data, "social fee event");
|
|
assert_eq!(decoded.spec.name, "SocialFeePdaClaimed");
|
|
assert_eq!(decoded.discriminator_hex, "3212c141edd2eaec");
|
|
assert_eq!(decoded.fields_json["user_id"], serde_json::json!("user-1"));
|
|
assert_eq!(decoded.fields_json["amount_claimed"], serde_json::json!("99"));
|
|
}
|
|
|
|
#[test]
|
|
fn decodes_unobserved_idl_admin_anchor_events() {
|
|
let mut authority_data = pump_fees_anchor_event_prefix(
|
|
&super::PUMP_FEES_SET_AUTHORITY_EVENT_DISCRIMINATOR,
|
|
);
|
|
authority_data.extend_from_slice(&11_i64.to_le_bytes());
|
|
authority_data.extend_from_slice(&[1_u8; 32]);
|
|
authority_data.extend_from_slice(&[2_u8; 32]);
|
|
let authority_event = decode_synthetic_anchor_event(authority_data, "set authority event");
|
|
assert_eq!(authority_event.spec.name, "SetAuthorityEvent");
|
|
assert_eq!(authority_event.discriminator_hex, "12af8442d0c957f2");
|
|
assert_eq!(authority_event.fields_json["timestamp"], serde_json::json!("11"));
|
|
assert_eq!(
|
|
authority_event.fields_json["old_authority"],
|
|
serde_json::json!(bs58::encode([1_u8; 32]).into_string())
|
|
);
|
|
assert_eq!(
|
|
authority_event.fields_json["new_authority"],
|
|
serde_json::json!(bs58::encode([2_u8; 32]).into_string())
|
|
);
|
|
|
|
let mut claim_rate_limit_data = pump_fees_anchor_event_prefix(
|
|
&super::PUMP_FEES_SET_CLAIM_RATE_LIMIT_EVENT_DISCRIMINATOR,
|
|
);
|
|
claim_rate_limit_data.extend_from_slice(&12_i64.to_le_bytes());
|
|
claim_rate_limit_data.extend_from_slice(&999_u64.to_le_bytes());
|
|
let claim_rate_limit_event =
|
|
decode_synthetic_anchor_event(claim_rate_limit_data, "set claim rate limit event");
|
|
assert_eq!(claim_rate_limit_event.spec.name, "SetClaimRateLimitEvent");
|
|
assert_eq!(claim_rate_limit_event.discriminator_hex, "0d8f8febb5133328");
|
|
assert_eq!(
|
|
claim_rate_limit_event.fields_json["claim_rate_limit"],
|
|
serde_json::json!("999")
|
|
);
|
|
|
|
let mut disable_flags_data = pump_fees_anchor_event_prefix(
|
|
&super::PUMP_FEES_SET_DISABLE_FLAGS_EVENT_DISCRIMINATOR,
|
|
);
|
|
disable_flags_data.extend_from_slice(&13_i64.to_le_bytes());
|
|
disable_flags_data.push(7_u8);
|
|
let disable_flags_event =
|
|
decode_synthetic_anchor_event(disable_flags_data, "set disable flags event");
|
|
assert_eq!(disable_flags_event.spec.name, "SetDisableFlagsEvent");
|
|
assert_eq!(disable_flags_event.discriminator_hex, "0508b3413137917e");
|
|
assert_eq!(
|
|
disable_flags_event.fields_json["disable_flags"],
|
|
serde_json::json!(7)
|
|
);
|
|
|
|
let mut social_claim_authority_data = pump_fees_anchor_event_prefix(
|
|
&super::PUMP_FEES_SET_SOCIAL_CLAIM_AUTHORITY_EVENT_DISCRIMINATOR,
|
|
);
|
|
social_claim_authority_data.extend_from_slice(&14_i64.to_le_bytes());
|
|
social_claim_authority_data.extend_from_slice(&[3_u8; 32]);
|
|
let social_claim_authority_event = decode_synthetic_anchor_event(
|
|
social_claim_authority_data,
|
|
"set social claim authority event",
|
|
);
|
|
assert_eq!(
|
|
social_claim_authority_event.spec.name,
|
|
"SetSocialClaimAuthorityEvent"
|
|
);
|
|
assert_eq!(
|
|
social_claim_authority_event.discriminator_hex,
|
|
"3c767f84ef34fe0e"
|
|
);
|
|
assert_eq!(
|
|
social_claim_authority_event.fields_json["social_claim_authority"],
|
|
serde_json::json!(bs58::encode([3_u8; 32]).into_string())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ignores_truncated_known_instruction_payload() {
|
|
let mut data = std::vec::Vec::new();
|
|
data.extend_from_slice(&super::PUMP_FEES_GET_FEES_DISCRIMINATOR);
|
|
let encoded = bs58::encode(data).into_string();
|
|
let data_json = serde_json::json!(encoded).to_string();
|
|
let decoded_result = super::parse_pump_fees_instruction_data(Some(&data_json));
|
|
match decoded_result {
|
|
Ok(None) => {},
|
|
Ok(Some(decoded)) => panic!("unexpected decoded truncated instruction: {}", decoded.spec.name),
|
|
Err(error) => panic!("truncated instruction should be ignored: {}", error),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn ignores_truncated_known_anchor_event_payload() {
|
|
let data = pump_fees_anchor_event_prefix(
|
|
&super::PUMP_FEES_SOCIAL_FEE_PDA_CLAIMED_DISCRIMINATOR,
|
|
);
|
|
let encoded = {
|
|
use base64::Engine as _;
|
|
base64::engine::general_purpose::STANDARD.encode(data)
|
|
};
|
|
let decoded_result = super::decode_pump_fees_anchor_event_from_base64(encoded.as_str());
|
|
match decoded_result {
|
|
Ok(None) => {},
|
|
Ok(Some(decoded)) => panic!("unexpected decoded truncated anchor event: {}", decoded.spec.name),
|
|
Err(error) => panic!("truncated anchor event should be ignored: {}", error),
|
|
}
|
|
}
|
|
|
|
fn pump_fees_anchor_event_prefix(event_discriminator: &[u8; 8]) -> std::vec::Vec<u8> {
|
|
let mut data = std::vec::Vec::new();
|
|
data.extend_from_slice(&super::PUMP_FEES_ANCHOR_SELF_CPI_LOG_DISCRIMINATOR);
|
|
data.extend_from_slice(event_discriminator);
|
|
return data;
|
|
}
|
|
|
|
fn decode_synthetic_anchor_event(
|
|
data: std::vec::Vec<u8>,
|
|
context: &str,
|
|
) -> super::PumpFeesAnchorEventData {
|
|
let encoded = {
|
|
use base64::Engine as _;
|
|
base64::engine::general_purpose::STANDARD.encode(data)
|
|
};
|
|
let decoded_result = super::decode_pump_fees_anchor_event_from_base64(encoded.as_str());
|
|
match decoded_result {
|
|
Ok(Some(decoded)) => return decoded,
|
|
Ok(None) => panic!("expected decoded {}", context),
|
|
Err(error) => panic!("unexpected decode error for {}: {}", context, error),
|
|
}
|
|
}
|
|
}
|