solana_tx_decoding/instruction/raydium/
process_raydium_ammv4_swap_instruction.rs

1use solana_central::Instruction;
2use solana_central::Pools;
3use solana_central::SwapDirection;
4use solana_central::SwapTx;
5use solana_central::constants::LAMPORTS_PER_SOL;
6use solana_sdk::pubkey::Pubkey;
7use solana_sdk::signature::Signature;
8use std::collections::HashMap;
9use std::collections::HashSet;
10
11/// Process a Raydium AMMv4 swap instruction and create a SwapTx. Assumes the instruction has been
12/// validated as a valid Raydium AMMv4 swap. Uses token transfer instructions that follow the swap
13/// to determine swap amounts.
14pub fn process_raydium_ammv4_swap_instruction(
15  // The swap instruction itself
16  instruction: &Instruction,
17  // The 2 token transfers (into one vault, out of the other vault) that come after the swap
18  // TODO this might have to be changed depending on whether rpc data returns parsed for this
19  transfers: &[Instruction],
20  ta_mint: &HashMap<u8, Pubkey>,
21  running_token_balances: &mut HashMap<u8, u64>,
22  block_time: u64,
23  slot: u64,
24  index: u64,
25  atomic_instruction_index: u8,
26  signers: &HashSet<Pubkey>,
27  signature: &Signature,
28) -> SwapTx {
29  let market_address = instruction.tx_account_keys[instruction.accounts[1] as usize];
30
31  // account indices change based on length of accounts array
32  let token_a_vault_address;
33  let token_b_vault_address;
34  if instruction.accounts.len() == 17 {
35    token_a_vault_address = instruction.accounts[4];
36    token_b_vault_address = instruction.accounts[5];
37  } else if instruction.accounts.len() == 18 {
38    token_a_vault_address = instruction.accounts[5];
39    token_b_vault_address = instruction.accounts[6];
40  } else {
41    panic!(
42      "process_raydium_ammv4_swap_instruction: Invalid number of accounts in swap instruction, signature: {}",
43      signature
44    );
45  }
46  // Identify token addresses involved in tx, not included in swap instruction
47  let token_a_address = *ta_mint.get(&token_a_vault_address).expect(format!("process_raydium_ammv4_swap_instruction: Token a vault address not found in ta_mint, signature: {}", signature).as_str());
48  let token_b_address = *ta_mint.get(&token_b_vault_address).expect(format!("process_raydium_ammv4_swap_instruction: Token b vault address not found in ta_mint, signature: {}", signature).as_str());
49
50  /*
51  Amount in is how much you sent to the pool in the first transfer instruction. Amount out is how
52  much you got out of the pool in the second transfer instruction. The amount value is stored in
53  the instruction data following the instruction 1 byte discriminator and is in a u64 value.
54  Direction doesn't influence where amount in and amount out are found and the order of the
55  transfers is always the same. First in then out
56  */
57  let swapped_amount_in = u64::from_le_bytes(transfers[0].data[1..9].try_into().unwrap());
58  let swapped_amount_received = u64::from_le_bytes(transfers[1].data[1..9].try_into().unwrap());
59
60  /*
61  Direction here is determined by if you are sending to the token a vault, then its A to B,
62  otherwise its B to A the same way that historical ingestion does it
63  */
64  let direction: SwapDirection;
65  if transfers[0].accounts[1] == token_a_vault_address {
66    direction = SwapDirection::AToB;
67    if let Some(running_token_balance) = running_token_balances.get_mut(&token_a_vault_address) {
68      *running_token_balance += swapped_amount_in;
69    }
70    if let Some(running_token_balance) = running_token_balances.get_mut(&token_b_vault_address) {
71      *running_token_balance -= swapped_amount_received;
72    }
73  } else {
74    direction = SwapDirection::BToA;
75    if let Some(running_token_balance) = running_token_balances.get_mut(&token_b_vault_address) {
76      *running_token_balance += swapped_amount_in;
77    }
78    if let Some(running_token_balance) = running_token_balances.get_mut(&token_a_vault_address) {
79      *running_token_balance -= swapped_amount_received;
80    }
81  };
82
83  let pool_token_a_vault_amount: u64 = running_token_balances[&token_a_vault_address];
84  let pool_token_b_vault_amount: u64 = running_token_balances[&token_b_vault_address];
85  let price_b_a_lp =
86    LAMPORTS_PER_SOL * pool_token_b_vault_amount as u128 / pool_token_a_vault_amount as u128;
87  let price_a_b_lp =
88    LAMPORTS_PER_SOL * pool_token_a_vault_amount as u128 / pool_token_b_vault_amount as u128;
89
90  SwapTx {
91    pool: Pools::RaydiumAmmV4,
92    direction,
93    block_time,
94    slot,
95    index,
96    atomic_instruction_index,
97    // Raydium Ammv4 fees are 0.25% fixed rate
98    fee_fraction_lp: 2500000,
99    swapped_amount_in,
100    swapped_amount_received,
101    pool_token_a_vault_amount,
102    pool_token_b_vault_amount,
103    price_a_b_lp,
104    price_b_a_lp,
105    token_a_address,
106    token_b_address,
107    market_address,
108    signature: signature.clone(),
109    signers: signers.clone(),
110  }
111}