// 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, /// Best-effort base/subject mint. pub token_a_mint: std::option::Option, /// Best-effort quote/fee mint. pub token_b_mint: std::option::Option, /// Best-effort secondary account used as lp/config surrogate. pub lp_mint: std::option::Option, /// 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, 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::(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 { 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 { 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, 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, 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, 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 { 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, 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, 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, keys: &[&str], ) -> std::option::Option { 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, keys: &[&str], ) -> std::option::Option { 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 { 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 { 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> { 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 { 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, crate::Error> { let values_result = serde_json::from_str::>(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, crate::Error> { let text = match parsed_json { Some(text) => text, None => return Ok(None), }; let parsed = serde_json::from_str::(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 { let data_json = match data_json { Some(data_json) => data_json, None => return None, }; let value_result = serde_json::from_str::(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 { 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, 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), } } }