solana_tx_decoding/instruction/pumpfun/
process_pumpfun_event_instruction.rs

1use borsh::BorshDeserialize;
2use solana_central::Instruction;
3use solana_central::Pools;
4use solana_central::SwapDirection;
5use solana_central::SwapTx;
6use solana_central::constants::LAMPORTS_PER_SOL;
7use solana_central::constants::{PUMP_CONSTANTS, TOKENS};
8use solana_central::derive_bonding_curve;
9use solana_central::protocol_idls::pumpfun::PfTradeEventIdlCurrent;
10use solana_central::protocol_idls::pumpfun::PfTradeEventIdlOld;
11use solana_sdk::pubkey::Pubkey;
12use solana_sdk::signature::Signature;
13use std::collections::HashSet;
14
15/// Process a Pumpfun bonding curve event instruction and create a SwapTx. Pumpfun bonding curve
16/// events contain all information needed to build the SwapTx type. Assumes the instruction has
17/// already been validated as a valid Pumpfun event instruction.
18pub fn process_pumpfun_event_instruction(
19  instruction: &Instruction,
20  block_time: u64,
21  slot: u64,
22  index: u64,
23  atomic_instruction_index: u8,
24  signers: &HashSet<Pubkey>,
25  signature: &Signature,
26) -> SwapTx {
27  let token_address;
28  let is_buy;
29  let fee_fraction_lp;
30  let sol_amount;
31  let total_fee;
32  let token_amount;
33  let pool_token_a_vault_amount;
34  let pool_token_b_vault_amount;
35  let virtual_sol_reserves;
36  let virtual_token_reserves;
37
38  /*
39  273: ix name (buy): 2YSPuG5KmLsZmQAsnzgUo1185nvqR8fHUZtaUxZW5ug53717ajgHATggpUEN5UN9HpM4DCQYQKwAXEigUpAhDiqh
40  274: ix name (sell): 4kDQCjvGqzMF53MnLaTwCyTN3Rc2E4vDSMYrvwj36XZq7nqETkPiEbiZzwwKbBYxJtyiVECwh3wQoqvPuPtUXhxp
41  286: ix name (buy_exact_sol_in): 55P61eQ7N8BGDNLP92H1evgk14uM5zw8FBb44StXwnYthspKzgRdCKXGXL2Mgsisu4SErXLQrS3PeLfppTF9sUAc
42
43  266 is the previous events used that didn't have the strings with ix name on it
44  */
45  if instruction.data.len() == 266
46    || instruction.data.len() == 273
47    || instruction.data.len() == 274
48    || instruction.data.len() == 286
49  {
50    let decoded_layout = PfTradeEventIdlCurrent::try_from_slice(&instruction.data[..266]).unwrap();
51    token_address = decoded_layout.mint;
52    is_buy = decoded_layout.is_buy;
53    sol_amount = decoded_layout.sol_amount;
54    total_fee = decoded_layout.creator_fee + decoded_layout.fee;
55    token_amount = decoded_layout.token_amount;
56    pool_token_a_vault_amount = decoded_layout.real_token_reserves;
57    pool_token_b_vault_amount = decoded_layout.real_sol_reserves;
58    virtual_sol_reserves = decoded_layout.virtual_sol_reserves;
59    virtual_token_reserves = decoded_layout.virtual_token_reserves;
60    fee_fraction_lp =
61      (decoded_layout.fee_basis_points + decoded_layout.creator_fee_basis_points) * 100000;
62  }
63  // The old creator fee event pre creator fee update
64  else if instruction.data.len() == 137 {
65    let decoded_layout = PfTradeEventIdlOld::try_from_slice(instruction.data).unwrap();
66    token_address = decoded_layout.mint;
67    is_buy = decoded_layout.is_buy;
68    sol_amount = decoded_layout.sol_amount;
69    token_amount = decoded_layout.token_amount;
70    pool_token_a_vault_amount =
71      decoded_layout.virtual_token_reserves - PUMP_CONSTANTS.bc_init_virtual_token_reserve_diff;
72    pool_token_b_vault_amount =
73      decoded_layout.virtual_sol_reserves - PUMP_CONSTANTS.bc_init_virtual_sol_reserves;
74    virtual_sol_reserves = decoded_layout.virtual_sol_reserves;
75    virtual_token_reserves = decoded_layout.virtual_token_reserves;
76    /*
77    For old events, the fee fraction was always 1%, and an offset was used to go from virtual to
78    real reserves for token and sol
79    */
80    fee_fraction_lp = 10000000;
81    // Fee was 1% of sol amount involved both on the way in added on and on the way out subtracted off
82    total_fee = sol_amount / 100;
83  } else {
84    panic!(
85      "Pumpfun: Found a swap event, but data length is not recognized, tx signature: {}",
86      signature
87    );
88  }
89
90  let market_address = derive_bonding_curve(&token_address);
91  let direction;
92  let swapped_amount_in;
93  let swapped_amount_received;
94
95  if is_buy {
96    direction = SwapDirection::BToA;
97    swapped_amount_in = sol_amount + total_fee;
98    swapped_amount_received = token_amount;
99  } else {
100    direction = SwapDirection::AToB;
101    swapped_amount_in = token_amount;
102    swapped_amount_received = sol_amount - total_fee;
103  }
104
105  SwapTx {
106    pool: Pools::PfBondingCurve,
107    direction,
108    block_time,
109    slot,
110    index,
111    atomic_instruction_index,
112    fee_fraction_lp,
113
114    // If swap is from A to B, then this is in terms of A tokens spent in
115    swapped_amount_in,
116    // If swap is from A to b, then this is in terms fo B tokens recieved
117    swapped_amount_received,
118    // Quantity of token in vault AFTER swap is completed
119    pool_token_a_vault_amount,
120    pool_token_b_vault_amount,
121    price_a_b_lp: (virtual_token_reserves as u128 * LAMPORTS_PER_SOL
122      / virtual_sol_reserves as u128),
123    price_b_a_lp: (virtual_sol_reserves as u128 * LAMPORTS_PER_SOL
124      / virtual_token_reserves as u128),
125
126    token_a_address: token_address,
127    token_b_address: TOKENS.wsol,
128    market_address,
129    signature: signature.clone(),
130    signers: signers.clone(),
131  }
132}