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<'a> AccountDecoder<'a> for PumpfunDecoder {
19 type AccountType = PumpAccount;
20 fn decode_account(
21 &self,
22 account: &solana_sdk::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_sdk::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!("DCpJReAfonSrgohiQbTmKKbjbqVofspFRHz9yQikzooP"),
125 fee_recipient: pubkey!("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV"),
126 initial_virtual_token_reserves: 1073000000000000,
127 initial_virtual_sol_reserves: 30000000000,
128 initial_real_token_reserves: 793100000000000,
129 token_total_supply: 1000000000000000,
130 fee_basis_points: 100,
131 ..Default::default()
132 };
133
134 let decoder = PumpfunDecoder;
136 let account = carbon_test_utils::read_account("tests/fixtures/global_account.json")
137 .expect("read fixture");
138 let decoded_account = decoder.decode_account(&account).expect("decode fixture");
139
140 match decoded_account.data {
142 PumpAccount::Global(global_account) => {
143 assert_eq!(
144 expected_global_account.initialized,
145 global_account.initialized
146 );
147 assert_eq!(expected_global_account.authority, global_account.authority);
148 assert_eq!(
149 expected_global_account.fee_recipient,
150 global_account.fee_recipient
151 );
152 assert_eq!(
153 expected_global_account.initial_virtual_token_reserves,
154 global_account.initial_virtual_token_reserves
155 );
156 assert_eq!(
157 expected_global_account.initial_virtual_sol_reserves,
158 global_account.initial_virtual_sol_reserves
159 );
160 assert_eq!(
161 expected_global_account.initial_real_token_reserves,
162 global_account.initial_real_token_reserves
163 );
164 assert_eq!(
165 expected_global_account.token_total_supply,
166 global_account.token_total_supply
167 );
168 assert_eq!(
169 expected_global_account.fee_basis_points,
170 global_account.fee_basis_points
171 );
172 }
173 _ => panic!("Expected Global"),
174 }
175 }
176
177 #[test]
178 fn test_decode_last_withdraw_account() {
179 let expected_last_withdraw_account = last_withdraw::LastWithdraw {
181 last_withdraw_timestamp: 1741550682,
182 };
183
184 let decoder = PumpfunDecoder;
186 let account = carbon_test_utils::read_account("tests/fixtures/last_withdraw_account.json")
187 .expect("read fixture");
188 let decoded_account = decoder.decode_account(&account).expect("decode fixture");
189
190 match decoded_account.data {
192 PumpAccount::LastWithdraw(last_withdraw) => {
193 assert_eq!(
194 expected_last_withdraw_account.last_withdraw_timestamp,
195 last_withdraw.last_withdraw_timestamp
196 );
197 }
198 _ => panic!("Expected LastWithdraw"),
199 }
200 }
201}