Skip to main content

hopper_token_2022/
lib.rs

1//! Hopper-owned Token-2022 builder and screening surface.
2//!
3//! Thin first-class Hopper wrappers over the canonical runtime builders,
4//! plus Token-2022 extension screening re-exports from `hopper-solana`.
5
6#![no_std]
7#![deny(unsafe_op_in_unsafe_fn)]
8
9use hopper_runtime::instruction::{InstructionAccount, InstructionView, Signer};
10use hopper_runtime::{AccountView, ProgramResult};
11
12pub use hopper_solana::constants::TOKEN_2022_PROGRAM_ID;
13pub use hopper_solana::mint::{
14    check_mint_authority, check_mint_initialized, mint_authority, mint_decimals,
15    mint_freeze_authority, mint_supply, MINT_LEN,
16};
17pub use hopper_solana::token::{
18    check_not_frozen, check_token_balance_gte, check_token_initialized, check_token_mint,
19    check_token_owner, token_account_amount, token_account_mint, token_account_owner,
20    token_account_state, TOKEN_ACCOUNT_LEN,
21};
22pub use hopper_solana::token2022_ext::{
23    check_no_confidential_transfer, check_no_permanent_delegate, check_no_transfer_fee,
24    check_no_transfer_hook, check_safe_token_2022_mint, check_transfer_hook_program,
25    check_transferable, find_extension_data, mint_has_extension, read_transfer_fee_config,
26    read_transfer_hook, token_has_extension, TransferFeeConfig, TransferHook, ACCOUNT_TYPE_MINT,
27    ACCOUNT_TYPE_OFFSET, ACCOUNT_TYPE_TOKEN, EXT_CONFIDENTIAL_TRANSFER_ACCOUNT,
28    EXT_CONFIDENTIAL_TRANSFER_MINT, EXT_CPI_GUARD, EXT_DEFAULT_ACCOUNT_STATE,
29    EXT_GROUP_MEMBER_POINTER, EXT_GROUP_POINTER, EXT_IMMUTABLE_OWNER, EXT_INTEREST_BEARING,
30    EXT_MEMO_TRANSFER, EXT_METADATA_POINTER, EXT_MINT_CLOSE_AUTHORITY, EXT_NON_TRANSFERABLE,
31    EXT_PERMANENT_DELEGATE, EXT_TOKEN_METADATA, EXT_TRANSFER_FEE_AMOUNT, EXT_TRANSFER_FEE_CONFIG,
32    EXT_TRANSFER_HOOK, MINT_BASE_SIZE, TLV_OFFSET, TOKEN_ACCOUNT_BASE_SIZE,
33};
34
35/// Builder for Token-2022 Transfer (instruction index 3).
36pub struct Transfer<'a> {
37    pub from: &'a AccountView,
38    pub to: &'a AccountView,
39    pub authority: &'a AccountView,
40    pub amount: u64,
41}
42
43impl Transfer<'_> {
44    #[inline]
45    pub fn invoke(&self) -> ProgramResult {
46        self.invoke_signed(&[])
47    }
48
49    #[inline]
50    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
51        let mut data = [0u8; 9];
52        data[0] = 3;
53        data[1..9].copy_from_slice(&self.amount.to_le_bytes());
54
55        let accounts = [
56            InstructionAccount::writable(self.from.address()),
57            InstructionAccount::writable(self.to.address()),
58            InstructionAccount::readonly_signer(self.authority.address()),
59        ];
60        let views = [self.from, self.to, self.authority];
61        let instruction = InstructionView {
62            program_id: &TOKEN_2022_PROGRAM_ID,
63            data: &data,
64            accounts: &accounts,
65        };
66
67        hopper_runtime::cpi::invoke_signed(&instruction, &views, signers)
68    }
69}
70
71/// Builder for Token-2022 MintTo (instruction index 7).
72pub struct MintTo<'a> {
73    pub mint: &'a AccountView,
74    pub account: &'a AccountView,
75    pub mint_authority: &'a AccountView,
76    pub amount: u64,
77}
78
79impl MintTo<'_> {
80    #[inline]
81    pub fn invoke(&self) -> ProgramResult {
82        self.invoke_signed(&[])
83    }
84
85    #[inline]
86    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
87        let mut data = [0u8; 9];
88        data[0] = 7;
89        data[1..9].copy_from_slice(&self.amount.to_le_bytes());
90
91        let accounts = [
92            InstructionAccount::writable(self.mint.address()),
93            InstructionAccount::writable(self.account.address()),
94            InstructionAccount::readonly_signer(self.mint_authority.address()),
95        ];
96        let views = [self.mint, self.account, self.mint_authority];
97        let instruction = InstructionView {
98            program_id: &TOKEN_2022_PROGRAM_ID,
99            data: &data,
100            accounts: &accounts,
101        };
102
103        hopper_runtime::cpi::invoke_signed(&instruction, &views, signers)
104    }
105}
106
107/// Builder for Token-2022 Burn (instruction index 8).
108pub struct Burn<'a> {
109    pub account: &'a AccountView,
110    pub mint: &'a AccountView,
111    pub authority: &'a AccountView,
112    pub amount: u64,
113}
114
115impl Burn<'_> {
116    #[inline]
117    pub fn invoke(&self) -> ProgramResult {
118        self.invoke_signed(&[])
119    }
120
121    #[inline]
122    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
123        let mut data = [0u8; 9];
124        data[0] = 8;
125        data[1..9].copy_from_slice(&self.amount.to_le_bytes());
126
127        let accounts = [
128            InstructionAccount::writable(self.account.address()),
129            InstructionAccount::writable(self.mint.address()),
130            InstructionAccount::readonly_signer(self.authority.address()),
131        ];
132        let views = [self.account, self.mint, self.authority];
133        let instruction = InstructionView {
134            program_id: &TOKEN_2022_PROGRAM_ID,
135            data: &data,
136            accounts: &accounts,
137        };
138
139        hopper_runtime::cpi::invoke_signed(&instruction, &views, signers)
140    }
141}
142
143/// Builder for Token-2022 CloseAccount (instruction index 9).
144pub struct CloseAccount<'a> {
145    pub account: &'a AccountView,
146    pub destination: &'a AccountView,
147    pub authority: &'a AccountView,
148}
149
150impl CloseAccount<'_> {
151    #[inline]
152    pub fn invoke(&self) -> ProgramResult {
153        self.invoke_signed(&[])
154    }
155
156    #[inline]
157    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
158        let data = [9u8];
159        let accounts = [
160            InstructionAccount::writable(self.account.address()),
161            InstructionAccount::writable(self.destination.address()),
162            InstructionAccount::readonly_signer(self.authority.address()),
163        ];
164        let views = [self.account, self.destination, self.authority];
165        let instruction = InstructionView {
166            program_id: &TOKEN_2022_PROGRAM_ID,
167            data: &data,
168            accounts: &accounts,
169        };
170
171        hopper_runtime::cpi::invoke_signed(&instruction, &views, signers)
172    }
173}
174
175/// Builder for Token-2022 Approve (instruction index 4).
176pub struct Approve<'a> {
177    pub source: &'a AccountView,
178    pub delegate: &'a AccountView,
179    pub authority: &'a AccountView,
180    pub amount: u64,
181}
182
183impl Approve<'_> {
184    #[inline]
185    pub fn invoke(&self) -> ProgramResult {
186        self.invoke_signed(&[])
187    }
188
189    #[inline]
190    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
191        let mut data = [0u8; 9];
192        data[0] = 4;
193        data[1..9].copy_from_slice(&self.amount.to_le_bytes());
194
195        let accounts = [
196            InstructionAccount::writable(self.source.address()),
197            InstructionAccount::readonly(self.delegate.address()),
198            InstructionAccount::readonly_signer(self.authority.address()),
199        ];
200        let views = [self.source, self.delegate, self.authority];
201        let instruction = InstructionView {
202            program_id: &TOKEN_2022_PROGRAM_ID,
203            data: &data,
204            accounts: &accounts,
205        };
206
207        hopper_runtime::cpi::invoke_signed(&instruction, &views, signers)
208    }
209}
210
211/// Builder for Token-2022 Revoke (instruction index 5).
212pub struct Revoke<'a> {
213    pub source: &'a AccountView,
214    pub authority: &'a AccountView,
215}
216
217impl Revoke<'_> {
218    #[inline]
219    pub fn invoke(&self) -> ProgramResult {
220        self.invoke_signed(&[])
221    }
222
223    #[inline]
224    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
225        let data = [5u8];
226        let accounts = [
227            InstructionAccount::writable(self.source.address()),
228            InstructionAccount::readonly_signer(self.authority.address()),
229        ];
230        let views = [self.source, self.authority];
231        let instruction = InstructionView {
232            program_id: &TOKEN_2022_PROGRAM_ID,
233            data: &data,
234            accounts: &accounts,
235        };
236
237        hopper_runtime::cpi::invoke_signed(&instruction, &views, signers)
238    }
239}
240
241/// Builder for Token-2022 InitializeAccount (instruction index 1).
242pub struct InitializeAccount<'a> {
243    pub account: &'a AccountView,
244    pub mint: &'a AccountView,
245    pub owner: &'a AccountView,
246    pub rent_sysvar: &'a AccountView,
247}
248
249impl InitializeAccount<'_> {
250    #[inline]
251    pub fn invoke(&self) -> ProgramResult {
252        let data = [1u8];
253        let accounts = [
254            InstructionAccount::writable(self.account.address()),
255            InstructionAccount::readonly(self.mint.address()),
256            InstructionAccount::readonly(self.owner.address()),
257            InstructionAccount::readonly(self.rent_sysvar.address()),
258        ];
259        let views = [self.account, self.mint, self.owner, self.rent_sysvar];
260        let instruction = InstructionView {
261            program_id: &TOKEN_2022_PROGRAM_ID,
262            data: &data,
263            accounts: &accounts,
264        };
265
266        hopper_runtime::cpi::invoke(&instruction, &views)
267    }
268}
269
270pub mod instructions {
271    pub use super::{Approve, Burn, CloseAccount, InitializeAccount, MintTo, Revoke, Transfer};
272}