solders_account_decoder/
lib.rs

1use solders_traits_core::{
2    handle_py_value_err, py_from_bytes_general_via_bincode, pybytes_general_via_bincode,
3    RichcmpEqualityOnly,
4};
5use std::fmt::Display;
6
7use derive_more::{From, Into};
8use pyo3::prelude::*;
9use pythonize::{depythonize, pythonize};
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12use solana_account_decoder::{
13    parse_account_data::ParsedAccount as ParsedAccountOriginal,
14    parse_token::UiTokenAmount as UiTokenAmountOriginal,
15    UiAccountEncoding as UiAccountEncodingOriginal, UiDataSliceConfig as UiDataSliceConfigOriginal,
16};
17use solders_macros::{common_methods, enum_original_mapping, richcmp_eq_only};
18
19/// Configuration object for limiting returned account data.
20///
21/// Args:
22///     offset (int): Skip this many bytes at the beginning of the data.
23///     length (int): Return only this many bytes.
24///
25#[pyclass(module = "solders.account_decoder")]
26#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Hash, From, Into)]
27pub struct UiDataSliceConfig(UiDataSliceConfigOriginal);
28
29#[richcmp_eq_only]
30#[pymethods]
31impl UiDataSliceConfig {
32    #[new]
33    fn new(offset: usize, length: usize) -> Self {
34        Self(UiDataSliceConfigOriginal { offset, length })
35    }
36
37    #[getter]
38    pub fn offset(&self) -> usize {
39        self.0.offset
40    }
41
42    #[getter]
43    pub fn length(&self) -> usize {
44        self.0.length
45    }
46
47    pub fn __repr__(&self) -> String {
48        format!("{self:#?}")
49    }
50}
51
52impl RichcmpEqualityOnly for UiDataSliceConfig {}
53
54/// Encoding options for account data.
55#[pyclass(module = "solders.account_decoder")]
56#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Hash)]
57#[serde(rename_all = "camelCase")]
58#[enum_original_mapping(UiAccountEncodingOriginal)]
59pub enum UiAccountEncoding {
60    Binary, // Legacy. Retained for RPC backwards compatibility
61    Base58,
62    Base64,
63    JsonParsed,
64    #[serde(rename = "base64+zstd")]
65    Base64Zstd,
66}
67
68#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, From, Into)]
69#[pyclass(module = "solders.account_decoder")]
70pub struct ParsedAccount(pub ParsedAccountOriginal);
71
72impl RichcmpEqualityOnly for ParsedAccount {}
73impl Display for ParsedAccount {
74    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
75        write!(f, "{self:?}")
76    }
77}
78pybytes_general_via_bincode!(ParsedAccount);
79py_from_bytes_general_via_bincode!(ParsedAccount);
80solders_traits_core::common_methods_default!(ParsedAccount);
81
82#[richcmp_eq_only]
83#[common_methods]
84#[pymethods]
85impl ParsedAccount {
86    #[new]
87    pub fn new(program: &str, parsed: &PyAny, space: u64) -> PyResult<Self> {
88        let value = handle_py_value_err(depythonize::<Value>(parsed))?;
89        Ok(ParsedAccountOriginal {
90            program: program.to_owned(),
91            parsed: value,
92            space,
93        }
94        .into())
95    }
96
97    #[getter]
98    pub fn program(&self) -> String {
99        self.0.program.clone()
100    }
101
102    #[getter]
103    pub fn parsed(&self, py: Python<'_>) -> PyResult<PyObject> {
104        handle_py_value_err(pythonize(py, &self.0.parsed))
105    }
106
107    #[getter]
108    pub fn space(&self) -> u64 {
109        self.0.space
110    }
111}
112
113#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, From, Into)]
114#[pyclass(module = "solders.account_decoder")]
115pub struct UiTokenAmount(UiTokenAmountOriginal);
116
117impl RichcmpEqualityOnly for UiTokenAmount {}
118impl Display for UiTokenAmount {
119    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
120        write!(f, "{self:?}")
121    }
122}
123pybytes_general_via_bincode!(UiTokenAmount);
124py_from_bytes_general_via_bincode!(UiTokenAmount);
125solders_traits_core::common_methods_default!(UiTokenAmount);
126
127#[richcmp_eq_only]
128#[common_methods]
129#[pymethods]
130impl UiTokenAmount {
131    #[pyo3(
132        signature = (ui_amount, decimals, amount, ui_amount_string)
133    )]
134    #[new]
135    pub fn new(
136        ui_amount: Option<f64>,
137        decimals: u8,
138        amount: String,
139        ui_amount_string: String,
140    ) -> PyResult<Self> {
141        Ok(UiTokenAmountOriginal {
142            ui_amount,
143            decimals,
144            amount,
145            ui_amount_string,
146        }
147        .into())
148    }
149
150    #[getter]
151    pub fn ui_amount(&self) -> Option<f64> {
152        self.0.ui_amount
153    }
154
155    #[getter]
156    pub fn decimals(&self) -> u8 {
157        self.0.decimals
158    }
159
160    #[getter]
161    pub fn amount(&self) -> String {
162        self.0.amount.clone()
163    }
164
165    #[getter]
166    pub fn ui_amount_string(&self) -> String {
167        self.0.ui_amount_string.clone()
168    }
169}
170
171pub fn create_account_decoder_mod(py: Python<'_>) -> PyResult<&PyModule> {
172    let m = PyModule::new(py, "account_decoder")?;
173    m.add_class::<UiDataSliceConfig>()?;
174    m.add_class::<UiAccountEncoding>()?;
175    m.add_class::<ParsedAccount>()?;
176    m.add_class::<UiTokenAmount>()?;
177    Ok(m)
178}