1#![no_std]
2
3use {
4 core::{mem::transmute, ops::Deref},
5 pinocchio::error::ProgramError,
6 pinocchio_associated_token_account::ID as ATA_PROGRAM_ID,
7 pinocchio_token::{
8 state::{Mint as SplMint, TokenAccount as SplTokenAccount},
9 ID as TOKEN_PROGRAM_ID,
10 },
11 solana_address::{address_eq, Address},
12 typhoon_traits::{Accessor, CheckOwner, CheckProgramId, DataStrategy, Discriminator},
13};
14
15mod traits;
16
17pub use {
18 pinocchio_associated_token_account::instructions as ata_instructions,
19 pinocchio_token::instructions as spl_instructions, traits::*,
20};
21
22#[cfg(feature = "token2022")]
23const TOKEN_2022_PROGRAM_ID: Address =
24 Address::from_str_const("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
25
26pub struct AtaTokenProgram;
27
28impl CheckProgramId for AtaTokenProgram {
29 #[inline(always)]
30 fn address_eq(program_id: &Address) -> bool {
31 address_eq(program_id, &ATA_PROGRAM_ID)
32 }
33}
34
35pub struct TokenProgram;
36
37impl CheckProgramId for TokenProgram {
38 #[inline(always)]
39 fn address_eq(program_id: &Address) -> bool {
40 #[cfg(feature = "token2022")]
41 {
42 address_eq(program_id, &TOKEN_PROGRAM_ID)
43 || address_eq(program_id, &TOKEN_2022_PROGRAM_ID)
44 }
45 #[cfg(not(feature = "token2022"))]
46 {
47 address_eq(program_id, &TOKEN_PROGRAM_ID)
48 }
49 }
50}
51
52pub struct SplStrategy;
53
54impl<'a> Accessor<'a, Mint> for SplStrategy {
55 type Data = &'a Mint;
56
57 #[inline(always)]
58 fn access(data: &'a [u8]) -> Result<Self::Data, ProgramError> {
59 Ok(unsafe { transmute::<&SplMint, &Mint>(SplMint::from_bytes_unchecked(data)) })
63 }
64
65 #[inline(always)]
66 fn read(data: &mut &'a [u8]) -> Result<Self::Data, ProgramError> {
67 let Some((to_read, rem)) = data.split_at_checked(Mint::LEN) else {
68 return Err(ProgramError::InvalidInstructionData);
69 };
70 *data = rem;
71 Ok(<Self as Accessor<Mint>>::access(to_read)?)
72 }
73}
74
75impl<'a> Accessor<'a, TokenAccount> for SplStrategy {
76 type Data = &'a TokenAccount;
77
78 #[inline(always)]
79 fn access(data: &'a [u8]) -> Result<Self::Data, ProgramError> {
80 Ok(unsafe {
84 transmute::<&SplTokenAccount, &TokenAccount>(SplTokenAccount::from_bytes_unchecked(
85 data,
86 ))
87 })
88 }
89
90 #[inline(always)]
91 fn read(data: &mut &'a [u8]) -> Result<Self::Data, ProgramError> {
92 let Some((to_read, rem)) = data.split_at_checked(TokenAccount::LEN) else {
93 return Err(ProgramError::InvalidInstructionData);
94 };
95 *data = rem;
96 Ok(<Self as Accessor<TokenAccount>>::access(to_read)?)
97 }
98}
99
100#[repr(transparent)]
101pub struct Mint(SplMint);
102
103impl Mint {
104 pub const LEN: usize = SplMint::LEN;
105}
106
107impl DataStrategy for Mint {
108 type Strategy = SplStrategy;
109}
110
111impl Discriminator for Mint {
112 const DISCRIMINATOR: &'static [u8] = &[];
113}
114
115impl CheckOwner for Mint {
116 #[inline(always)]
117 fn owned_by(program_id: &Address) -> bool {
118 #[cfg(feature = "token2022")]
119 {
120 address_eq(program_id, &TOKEN_PROGRAM_ID)
121 || address_eq(program_id, &TOKEN_2022_PROGRAM_ID)
122 }
123 #[cfg(not(feature = "token2022"))]
124 {
125 address_eq(program_id, &TOKEN_PROGRAM_ID)
126 }
127 }
128}
129
130impl Deref for Mint {
131 type Target = SplMint;
132
133 fn deref(&self) -> &Self::Target {
134 &self.0
135 }
136}
137
138#[repr(transparent)]
139pub struct TokenAccount(SplTokenAccount);
140
141impl TokenAccount {
142 pub const LEN: usize = SplTokenAccount::LEN;
143}
144
145impl DataStrategy for TokenAccount {
146 type Strategy = SplStrategy;
147}
148
149impl Discriminator for TokenAccount {
150 const DISCRIMINATOR: &'static [u8] = &[];
151}
152
153impl CheckOwner for TokenAccount {
154 #[inline(always)]
155 fn owned_by(program_id: &Address) -> bool {
156 #[cfg(feature = "token2022")]
157 {
158 address_eq(program_id, &TOKEN_PROGRAM_ID)
159 || address_eq(program_id, &TOKEN_2022_PROGRAM_ID)
160 }
161 #[cfg(not(feature = "token2022"))]
162 {
163 address_eq(program_id, &TOKEN_PROGRAM_ID)
164 }
165 }
166}
167
168impl Deref for TokenAccount {
169 type Target = SplTokenAccount;
170
171 fn deref(&self) -> &Self::Target {
172 &self.0
173 }
174}
175
176pub fn find_associated_token_address(mint: &Address, owner: &Address) -> Address {
177 Address::find_program_address(
178 &[owner.as_ref(), TOKEN_PROGRAM_ID.as_ref(), mint.as_ref()],
179 &ATA_PROGRAM_ID,
180 )
181 .0
182}