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;
9pub mod last_withdraw;
10
11#[allow(clippy::large_enum_variant)]
12pub enum PumpAccount {
13 BondingCurve(bonding_curve::BondingCurve),
14 Global(global::Global),
15 LastWithdraw(last_withdraw::LastWithdraw),
16}
17
18impl AccountDecoder<'_> for PumpfunDecoder {
19 type AccountType = PumpAccount;
20 fn decode_account(
21 &self,
22 account: &solana_account::Account,
23 ) -> Option<carbon_core::account::DecodedAccount<Self::AccountType>> {
24 if !account.owner.eq(&PROGRAM_ID) {
25 return None;
26 }
27
28 if let Some(decoded_account) =
29 bonding_curve::BondingCurve::deserialize(account.data.as_slice())
30 {
31 return Some(carbon_core::account::DecodedAccount {
32 lamports: account.lamports,
33 data: PumpAccount::BondingCurve(decoded_account),
34 owner: account.owner,
35 executable: account.executable,
36 rent_epoch: account.rent_epoch,
37 });
38 }
39
40 if let Some(decoded_account) = global::Global::deserialize(account.data.as_slice()) {
41 return Some(carbon_core::account::DecodedAccount {
42 lamports: account.lamports,
43 data: PumpAccount::Global(decoded_account),
44 owner: account.owner,
45 executable: account.executable,
46 rent_epoch: account.rent_epoch,
47 });
48 }
49
50 if let Some(decoded_account) =
51 last_withdraw::LastWithdraw::deserialize(account.data.as_slice())
52 {
53 return Some(carbon_core::account::DecodedAccount {
54 lamports: account.lamports,
55 data: PumpAccount::LastWithdraw(decoded_account),
56 owner: account.owner,
57 executable: account.executable,
58 rent_epoch: account.rent_epoch,
59 });
60 }
61
62 None
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use solana_pubkey::Pubkey;
69
70 use super::*;
71
72 #[test]
73 fn test_decode_bonding_curve_account() {
74 let expected_bonding_curve = bonding_curve::BondingCurve {
76 virtual_token_reserves: 1072906494066221,
77 virtual_sol_reserves: 30002615555,
78 real_token_reserves: 793006494066221,
79 real_sol_reserves: 2615555,
80 token_total_supply: 1000000000000000,
81 complete: false,
82 };
83
84 let decoder = PumpfunDecoder;
86 let account = carbon_test_utils::read_account("tests/fixtures/bonding_curve_account.json")
87 .expect("read fixture");
88 let decoded_account = decoder.decode_account(&account).expect("decode fixture");
89
90 match decoded_account.data {
92 PumpAccount::BondingCurve(bonding_curve) => {
93 assert_eq!(
94 expected_bonding_curve.virtual_token_reserves,
95 bonding_curve.virtual_token_reserves
96 );
97 assert_eq!(
98 expected_bonding_curve.virtual_sol_reserves,
99 bonding_curve.virtual_sol_reserves
100 );
101 assert_eq!(
102 expected_bonding_curve.real_token_reserves,
103 bonding_curve.real_token_reserves
104 );
105 assert_eq!(
106 expected_bonding_curve.real_sol_reserves,
107 bonding_curve.real_sol_reserves
108 );
109 assert_eq!(
110 expected_bonding_curve.token_total_supply,
111 bonding_curve.token_total_supply
112 );
113 assert_eq!(expected_bonding_curve.complete, bonding_curve.complete);
114 }
115 _ => panic!("Expected BondingCurve"),
116 }
117 }
118
119 #[test]
120 fn test_decode_global_account() {
121 let expected_global_account = global::Global {
123 initialized: true,
124 authority: Pubkey::from_str_const("FFWtrEQ4B4PKQoVuHYzZq8FabGkVatYzDpEVHsK5rrhF"),
125 withdraw_authority: Pubkey::from_str_const(
126 "39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg",
127 ),
128 fee_recipient: Pubkey::from_str_const("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV"),
129 initial_virtual_token_reserves: 1073000000000000,
130 initial_virtual_sol_reserves: 30000000000,
131 initial_real_token_reserves: 793100000000000,
132 token_total_supply: 1000000000000000,
133 fee_basis_points: 100,
134 pool_migration_fee: 15000001,
135 enable_migrate: true,
136 fee_recipients: [
137 Pubkey::from_str_const("7VtfL8fvgNfhz17qKRMjzQEXgbdpnHHHQRh54R9jP2RJ"),
138 Pubkey::from_str_const("7hTckgnGnLQR6sdH7YkqFTAA7VwTfYFaZ6EhEsU3saCX"),
139 Pubkey::from_str_const("9rPYyANsfQZw3DnDmKE3YCQF5E8oD89UXoHn9JFEhJUz"),
140 Pubkey::from_str_const("AVmoTthdrX6tKt4nDjco2D775W2YK3sDhxPcMmzUAmTY"),
141 Pubkey::from_str_const("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM"),
142 Pubkey::from_str_const("FWsW1xNtWscwNmKv6wVsU1iTzRN6wmmk3MjxRP5tT7hz"),
143 Pubkey::from_str_const("G5UZAVbAf46s7cKWoyKu8kYTip9DGTpbLZ2qa9Aq69dP"),
144 ],
145 ..Default::default()
146 };
147
148 let decoder = PumpfunDecoder;
150 let account = carbon_test_utils::read_account("tests/fixtures/global_account.json")
151 .expect("read fixture");
152 let decoded_account = decoder.decode_account(&account).expect("decode fixture");
153
154 match decoded_account.data {
156 PumpAccount::Global(global_account) => {
157 assert_eq!(
158 expected_global_account.initialized,
159 global_account.initialized
160 );
161 assert_eq!(expected_global_account.authority, global_account.authority);
162 assert_eq!(
163 expected_global_account.fee_recipient,
164 global_account.fee_recipient
165 );
166 assert_eq!(
167 expected_global_account.initial_virtual_token_reserves,
168 global_account.initial_virtual_token_reserves
169 );
170 assert_eq!(
171 expected_global_account.initial_virtual_sol_reserves,
172 global_account.initial_virtual_sol_reserves
173 );
174 assert_eq!(
175 expected_global_account.initial_real_token_reserves,
176 global_account.initial_real_token_reserves
177 );
178 assert_eq!(
179 expected_global_account.token_total_supply,
180 global_account.token_total_supply
181 );
182 assert_eq!(
183 expected_global_account.fee_basis_points,
184 global_account.fee_basis_points
185 );
186 }
187 _ => panic!("Expected Global"),
188 }
189 }
190
191 #[test]
192 fn test_decode_last_withdraw_account() {
193 let expected_last_withdraw_account = last_withdraw::LastWithdraw {
195 last_withdraw_timestamp: 1741550682,
196 };
197
198 let decoder = PumpfunDecoder;
200 let account = carbon_test_utils::read_account("tests/fixtures/last_withdraw_account.json")
201 .expect("read fixture");
202 let decoded_account = decoder.decode_account(&account).expect("decode fixture");
203
204 match decoded_account.data {
206 PumpAccount::LastWithdraw(last_withdraw) => {
207 assert_eq!(
208 expected_last_withdraw_account.last_withdraw_timestamp,
209 last_withdraw.last_withdraw_timestamp
210 );
211 }
212 _ => panic!("Expected LastWithdraw"),
213 }
214 }
215}