carbon_pumpfun_decoder/accounts/
mod.rs

1use 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        // Arrange
61        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        // Act
71        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        // Assert
77        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        // Arrange
108        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        // Act
133        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        // Assert
139        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}