carbon_pumpfun_decoder/accounts/
mod.rs1use carbon_core::account::AccountDecoder;
2use carbon_core::deserialize::CarbonDeserialize;
3
4use crate::PROGRAM_ID;
5
6use super::PumpfunDecoder;
7pub mod bonding_curve;
8pub mod global;
9
10#[allow(clippy::large_enum_variant)]
11pub enum PumpfunAccount {
12 BondingCurve(bonding_curve::BondingCurve),
13 Global(global::Global),
14}
15
16impl AccountDecoder<'_> for PumpfunDecoder {
17 type AccountType = PumpfunAccount;
18 fn decode_account(
19 &self,
20 account: &solana_account::Account,
21 ) -> Option<carbon_core::account::DecodedAccount<Self::AccountType>> {
22 if !account.owner.eq(&PROGRAM_ID) {
23 return None;
24 }
25
26 if let Some(decoded_account) =
27 bonding_curve::BondingCurve::deserialize(account.data.as_slice())
28 {
29 return Some(carbon_core::account::DecodedAccount {
30 lamports: account.lamports,
31 data: PumpfunAccount::BondingCurve(decoded_account),
32 owner: account.owner,
33 executable: account.executable,
34 rent_epoch: account.rent_epoch,
35 });
36 }
37
38 if let Some(decoded_account) = global::Global::deserialize(account.data.as_slice()) {
39 return Some(carbon_core::account::DecodedAccount {
40 lamports: account.lamports,
41 data: PumpfunAccount::Global(decoded_account),
42 owner: account.owner,
43 executable: account.executable,
44 rent_epoch: account.rent_epoch,
45 });
46 }
47
48 None
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use solana_pubkey::pubkey;
55
56 use super::*;
57
58 #[test]
59 fn test_decode_bonding_curve_account() {
60 let expected_bonding_curve = bonding_curve::BondingCurve {
62 virtual_token_reserves: 1072911112000000,
63 virtual_sol_reserves: 30002485430,
64 real_token_reserves: 793011112000000,
65 real_sol_reserves: 2485430,
66 token_total_supply: 1000000000000000,
67 complete: false,
68 };
69
70 let decoder = PumpfunDecoder;
72 let account = carbon_test_utils::read_account("tests/fixtures/bonding_curve_account.json")
73 .expect("read fixture");
74 let decoded_account = decoder.decode_account(&account).expect("decode fixture");
75
76 match decoded_account.data {
78 PumpfunAccount::BondingCurve(bonding_curve) => {
79 assert_eq!(
80 expected_bonding_curve.virtual_token_reserves,
81 bonding_curve.virtual_token_reserves
82 );
83 assert_eq!(
84 expected_bonding_curve.virtual_sol_reserves,
85 bonding_curve.virtual_sol_reserves
86 );
87 assert_eq!(
88 expected_bonding_curve.real_token_reserves,
89 bonding_curve.real_token_reserves
90 );
91 assert_eq!(
92 expected_bonding_curve.real_sol_reserves,
93 bonding_curve.real_sol_reserves
94 );
95 assert_eq!(
96 expected_bonding_curve.token_total_supply,
97 bonding_curve.token_total_supply
98 );
99 assert_eq!(expected_bonding_curve.complete, bonding_curve.complete);
100 }
101 _ => panic!("Expected BondingCurve"),
102 }
103 }
104
105 #[test]
106 fn test_decode_global_account() {
107 let expected_global_account = global::Global {
109 initialized: true,
110 authority: pubkey!("FFWtrEQ4B4PKQoVuHYzZq8FabGkVatYzDpEVHsK5rrhF"),
111 withdraw_authority: pubkey!("39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg"),
112 fee_recipient: pubkey!("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV"),
113 initial_virtual_token_reserves: 1073000000000000,
114 initial_virtual_sol_reserves: 30000000000,
115 initial_real_token_reserves: 793100000000000,
116 token_total_supply: 1000000000000000,
117 fee_basis_points: 95,
118 pool_migration_fee: 15000001,
119 enable_migrate: true,
120 fee_recipients: [
121 pubkey!("7VtfL8fvgNfhz17qKRMjzQEXgbdpnHHHQRh54R9jP2RJ"),
122 pubkey!("7hTckgnGnLQR6sdH7YkqFTAA7VwTfYFaZ6EhEsU3saCX"),
123 pubkey!("9rPYyANsfQZw3DnDmKE3YCQF5E8oD89UXoHn9JFEhJUz"),
124 pubkey!("AVmoTthdrX6tKt4nDjco2D775W2YK3sDhxPcMmzUAmTY"),
125 pubkey!("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM"),
126 pubkey!("FWsW1xNtWscwNmKv6wVsU1iTzRN6wmmk3MjxRP5tT7hz"),
127 pubkey!("G5UZAVbAf46s7cKWoyKu8kYTip9DGTpbLZ2qa9Aq69dP"),
128 ],
129 ..Default::default()
130 };
131
132 let decoder = PumpfunDecoder;
134 let account = carbon_test_utils::read_account("tests/fixtures/global_account.json")
135 .expect("read fixture");
136 let decoded_account = decoder.decode_account(&account).expect("decode fixture");
137
138 match decoded_account.data {
140 PumpfunAccount::Global(global_account) => {
141 assert_eq!(
142 expected_global_account.initialized,
143 global_account.initialized
144 );
145 assert_eq!(expected_global_account.authority, global_account.authority);
146 assert_eq!(
147 expected_global_account.fee_recipient,
148 global_account.fee_recipient
149 );
150 assert_eq!(
151 expected_global_account.initial_virtual_token_reserves,
152 global_account.initial_virtual_token_reserves
153 );
154 assert_eq!(
155 expected_global_account.initial_virtual_sol_reserves,
156 global_account.initial_virtual_sol_reserves
157 );
158 assert_eq!(
159 expected_global_account.initial_real_token_reserves,
160 global_account.initial_real_token_reserves
161 );
162 assert_eq!(
163 expected_global_account.token_total_supply,
164 global_account.token_total_supply
165 );
166 assert_eq!(
167 expected_global_account.fee_basis_points,
168 global_account.fee_basis_points
169 );
170 }
171 _ => panic!("Expected Global"),
172 }
173 }
174}