1use light_compressed_account::compressed_account::CompressedAccountWithMerkleContext;
3use light_sdk::{
4 instruction::PackedAccounts,
5 light_hasher::{sha256::Sha256BE, HasherError},
6};
7pub use light_token_interface::state::TokenData;
8use light_token_interface::state::TokenDataVersion;
9use solana_account_info::AccountInfo;
10use solana_program_error::ProgramError;
11
12use crate::{AnchorDeserialize, AnchorSerialize};
13
14pub trait Pack {
18 type Packed;
19 fn pack(&self, remaining_accounts: &mut PackedAccounts) -> Result<Self::Packed, ProgramError>;
20}
21pub trait Unpack {
22 type Unpacked;
23 fn unpack(
24 &self,
25 remaining_accounts: &[AccountInfo],
26 ) -> std::result::Result<Self::Unpacked, ProgramError>;
27}
28
29pub mod compat {
31 use solana_pubkey::Pubkey;
32
33 use super::*;
34
35 #[derive(Clone, Copy, Debug, PartialEq, Eq, AnchorDeserialize, AnchorSerialize, Default)]
36 #[repr(u8)]
37 pub enum AccountState {
38 #[default]
39 Initialized = 0,
40 Frozen = 1,
41 }
42
43 impl From<AccountState> for light_token_interface::state::CompressedTokenAccountState {
44 fn from(state: AccountState) -> Self {
45 match state {
46 AccountState::Initialized => {
47 light_token_interface::state::CompressedTokenAccountState::Initialized
48 }
49 AccountState::Frozen => {
50 light_token_interface::state::CompressedTokenAccountState::Frozen
51 }
52 }
53 }
54 }
55
56 impl TryFrom<u8> for AccountState {
57 type Error = ProgramError;
58
59 fn try_from(value: u8) -> Result<Self, Self::Error> {
60 match value {
61 0 => Ok(AccountState::Initialized),
62 1 => Ok(AccountState::Frozen),
63 _ => Err(ProgramError::InvalidAccountData),
64 }
65 }
66 }
67
68 #[derive(Debug, PartialEq, Eq, AnchorDeserialize, AnchorSerialize, Clone, Default)]
72 pub struct TokenData {
73 pub mint: Pubkey,
75 pub owner: Pubkey,
77 pub amount: u64,
79 pub delegate: Option<Pubkey>,
81 pub state: AccountState,
83 pub tlv: Option<Vec<light_token_interface::state::ExtensionStruct>>,
85 }
86
87 impl TokenData {
88 #[inline(always)]
91 pub fn hash_sha_flat(&self) -> Result<[u8; 32], HasherError> {
92 use light_sdk::light_hasher::Hasher;
93 let bytes = self.try_to_vec().map_err(|_| HasherError::BorshError)?;
94 Sha256BE::hash(bytes.as_slice())
95 }
96 }
97
98 #[derive(Debug, Clone, PartialEq)]
100 pub struct TokenDataWithMerkleContext {
101 pub token_data: TokenData,
102 pub compressed_account: CompressedAccountWithMerkleContext,
103 }
104
105 impl TokenDataWithMerkleContext {
106 pub fn hash(&self) -> Result<[u8; 32], HasherError> {
108 if let Some(data) = self.compressed_account.compressed_account.data.as_ref() {
109 match data.discriminator {
110 [0, 0, 0, 0, 0, 0, 0, 4] => self.token_data.hash_sha_flat(),
111 _ => Err(HasherError::EmptyInput),
112 }
113 } else {
114 Err(HasherError::EmptyInput)
115 }
116 }
117 }
118
119 impl From<TokenData> for light_token_interface::state::TokenData {
120 fn from(data: TokenData) -> Self {
121 use light_token_interface::state::CompressedTokenAccountState;
122
123 Self {
124 mint: data.mint.to_bytes().into(),
125 owner: data.owner.to_bytes().into(),
126 amount: data.amount,
127 delegate: data.delegate.map(|d| d.to_bytes().into()),
128 state: match data.state {
129 AccountState::Initialized => CompressedTokenAccountState::Initialized as u8,
130 AccountState::Frozen => CompressedTokenAccountState::Frozen as u8,
131 },
132 tlv: data.tlv,
133 }
134 }
135 }
136
137 impl From<light_token_interface::state::TokenData> for TokenData {
138 fn from(data: light_token_interface::state::TokenData) -> Self {
139 Self {
140 mint: Pubkey::new_from_array(data.mint.to_bytes()),
141 owner: Pubkey::new_from_array(data.owner.to_bytes()),
142 amount: data.amount,
143 delegate: data.delegate.map(|d| Pubkey::new_from_array(d.to_bytes())),
144 state: AccountState::try_from(data.state).unwrap_or(AccountState::Initialized),
145 tlv: data.tlv,
146 }
147 }
148 }
149
150 impl Pack for TokenData {
151 type Packed = InputTokenDataCompressible;
152
153 fn pack(
154 &self,
155 remaining_accounts: &mut PackedAccounts,
156 ) -> Result<Self::Packed, ProgramError> {
157 Ok(InputTokenDataCompressible {
158 owner: remaining_accounts.insert_or_get(self.owner),
159 mint: remaining_accounts.insert_or_get_read_only(self.mint),
160 amount: self.amount,
161 has_delegate: self.delegate.is_some(),
162 delegate: if let Some(delegate) = self.delegate {
163 remaining_accounts.insert_or_get(delegate)
164 } else {
165 0
166 },
167 version: TokenDataVersion::ShaFlat as u8,
168 })
169 }
170 }
171
172 impl Unpack for TokenData {
173 type Unpacked = Self;
174
175 fn unpack(
176 &self,
177 _remaining_accounts: &[AccountInfo],
178 ) -> std::result::Result<Self::Unpacked, ProgramError> {
179 Ok(self.clone())
180 }
181 }
182
183 impl Unpack for InputTokenDataCompressible {
184 type Unpacked = TokenData;
185
186 fn unpack(
187 &self,
188 remaining_accounts: &[AccountInfo],
189 ) -> std::result::Result<Self::Unpacked, ProgramError> {
190 Ok(TokenData {
191 owner: *remaining_accounts
192 .get(self.owner as usize)
193 .ok_or(ProgramError::InvalidAccountData)?
194 .key,
195 amount: self.amount,
196 delegate: if self.has_delegate {
197 Some(
198 *remaining_accounts
199 .get(self.delegate as usize)
200 .ok_or(ProgramError::InvalidAccountData)?
201 .key,
202 )
203 } else {
204 None
205 },
206 mint: *remaining_accounts
207 .get(self.mint as usize)
208 .ok_or(ProgramError::InvalidAccountData)?
209 .key,
210 state: AccountState::Initialized,
211 tlv: None,
212 })
213 }
214 }
215
216 #[derive(AnchorSerialize, AnchorDeserialize, Debug, Clone)]
218 pub struct TokenDataWithVariant<V> {
219 pub variant: V,
220 pub token_data: TokenData,
221 }
222
223 #[derive(AnchorSerialize, AnchorDeserialize, Debug, Clone)]
224 pub struct PackedTokenDataWithVariant<V> {
225 pub variant: V,
226 pub token_data: InputTokenDataCompressible,
227 }
228
229 #[derive(AnchorSerialize, AnchorDeserialize, Debug, Clone)]
230 pub struct CTokenDataWithVariant<V> {
231 pub variant: V,
232 pub token_data: TokenData,
233 }
234
235 impl<V> Pack for CTokenDataWithVariant<V>
236 where
237 V: Pack,
238 V::Packed: AnchorSerialize + Clone + std::fmt::Debug,
239 {
240 type Packed = PackedTokenDataWithVariant<V::Packed>;
241
242 fn pack(
243 &self,
244 remaining_accounts: &mut PackedAccounts,
245 ) -> Result<Self::Packed, ProgramError> {
246 Ok(PackedTokenDataWithVariant {
247 variant: self.variant.pack(remaining_accounts)?,
248 token_data: self.token_data.pack(remaining_accounts)?,
249 })
250 }
251 }
252
253 impl<V> Unpack for CTokenDataWithVariant<V>
254 where
255 V: Clone,
256 {
257 type Unpacked = TokenDataWithVariant<V>;
258
259 fn unpack(
260 &self,
261 remaining_accounts: &[AccountInfo],
262 ) -> std::result::Result<Self::Unpacked, ProgramError> {
263 Ok(TokenDataWithVariant {
266 variant: self.variant.clone(),
267 token_data: self.token_data.unpack(remaining_accounts)?,
268 })
269 }
270 }
271
272 impl<V> Pack for TokenDataWithVariant<V>
273 where
274 V: Pack,
275 V::Packed: AnchorSerialize + Clone + std::fmt::Debug,
276 {
277 type Packed = PackedTokenDataWithVariant<V::Packed>;
278
279 fn pack(
280 &self,
281 remaining_accounts: &mut PackedAccounts,
282 ) -> Result<Self::Packed, ProgramError> {
283 Ok(PackedTokenDataWithVariant {
284 variant: self.variant.pack(remaining_accounts)?,
285 token_data: self.token_data.pack(remaining_accounts)?,
286 })
287 }
288 }
289
290 impl<V> Unpack for PackedTokenDataWithVariant<V>
291 where
292 V: Unpack,
293 {
294 type Unpacked = TokenDataWithVariant<V::Unpacked>;
295
296 fn unpack(
297 &self,
298 remaining_accounts: &[AccountInfo],
299 ) -> std::result::Result<Self::Unpacked, ProgramError> {
300 Ok(TokenDataWithVariant {
301 variant: self.variant.unpack(remaining_accounts)?,
302 token_data: self.token_data.unpack(remaining_accounts)?,
303 })
304 }
305 }
306
307 pub type InputTokenDataCompressible =
309 light_token_interface::instructions::transfer2::MultiTokenTransferOutputData;
310 pub type CompressibleTokenDataWithVariant<V> = CTokenDataWithVariant<V>;
311 pub type PackedCompressibleTokenDataWithVariant<V> = PackedTokenDataWithVariant<V>;
312 pub type CTokenData<V> = CTokenDataWithVariant<V>;
313 pub type PackedCTokenData<V> = PackedTokenDataWithVariant<V>;
314}