Skip to main content

hopper_native/
token.rs

1//! SPL Token program CPI instructions.
2//!
3//! Provides Transfer, MintTo, Burn, CloseAccount, Approve, and Revoke
4//! builders that invoke the SPL Token program via `sol_invoke_signed_c`.
5
6use crate::account_view::AccountView;
7use crate::address::Address;
8use crate::instruction::{CpiAccount, Signer};
9use crate::ProgramResult;
10
11/// SPL Token program address.
12pub const TOKEN_PROGRAM_ID: Address =
13    crate::address!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
14
15// ── Transfer ─────────────────────────────────────────────────────────
16
17/// Builder for SPL Token Transfer (instruction index 3).
18pub struct Transfer<'a> {
19    pub from: &'a AccountView,
20    pub to: &'a AccountView,
21    pub authority: &'a AccountView,
22    pub amount: u64,
23}
24
25impl Transfer<'_> {
26    #[inline]
27    pub fn invoke(&self) -> ProgramResult {
28        self.invoke_signed(&[])
29    }
30
31    #[inline]
32    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
33        let mut data = [0u8; 9];
34        data[0] = 3;
35        data[1..9].copy_from_slice(&self.amount.to_le_bytes());
36
37        let accounts = [
38            CpiAccount::from(self.from),
39            CpiAccount::from(self.to),
40            CpiAccount::from(self.authority),
41        ];
42
43        invoke_token(&data, &accounts, signers)
44    }
45}
46
47// ── MintTo ───────────────────────────────────────────────────────────
48
49/// Builder for SPL Token MintTo (instruction index 7).
50pub struct MintTo<'a> {
51    pub mint: &'a AccountView,
52    pub account: &'a AccountView,
53    pub mint_authority: &'a AccountView,
54    pub amount: u64,
55}
56
57impl MintTo<'_> {
58    #[inline]
59    pub fn invoke(&self) -> ProgramResult {
60        self.invoke_signed(&[])
61    }
62
63    #[inline]
64    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
65        let mut data = [0u8; 9];
66        data[0] = 7;
67        data[1..9].copy_from_slice(&self.amount.to_le_bytes());
68
69        let accounts = [
70            CpiAccount::from(self.mint),
71            CpiAccount::from(self.account),
72            CpiAccount::from(self.mint_authority),
73        ];
74
75        invoke_token(&data, &accounts, signers)
76    }
77}
78
79// ── Burn ─────────────────────────────────────────────────────────────
80
81/// Builder for SPL Token Burn (instruction index 8).
82pub struct Burn<'a> {
83    pub account: &'a AccountView,
84    pub mint: &'a AccountView,
85    pub authority: &'a AccountView,
86    pub amount: u64,
87}
88
89impl Burn<'_> {
90    #[inline]
91    pub fn invoke(&self) -> ProgramResult {
92        self.invoke_signed(&[])
93    }
94
95    #[inline]
96    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
97        let mut data = [0u8; 9];
98        data[0] = 8;
99        data[1..9].copy_from_slice(&self.amount.to_le_bytes());
100
101        let accounts = [
102            CpiAccount::from(self.account),
103            CpiAccount::from(self.mint),
104            CpiAccount::from(self.authority),
105        ];
106
107        invoke_token(&data, &accounts, signers)
108    }
109}
110
111// ── CloseAccount ─────────────────────────────────────────────────────
112
113/// Builder for SPL Token CloseAccount (instruction index 9).
114pub struct CloseAccount<'a> {
115    pub account: &'a AccountView,
116    pub destination: &'a AccountView,
117    pub authority: &'a AccountView,
118}
119
120impl CloseAccount<'_> {
121    #[inline]
122    pub fn invoke(&self) -> ProgramResult {
123        self.invoke_signed(&[])
124    }
125
126    #[inline]
127    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
128        let data = [9u8];
129
130        let accounts = [
131            CpiAccount::from(self.account),
132            CpiAccount::from(self.destination),
133            CpiAccount::from(self.authority),
134        ];
135
136        invoke_token(&data, &accounts, signers)
137    }
138}
139
140// ── Approve ──────────────────────────────────────────────────────────
141
142/// Builder for SPL Token Approve (instruction index 4).
143pub struct Approve<'a> {
144    pub source: &'a AccountView,
145    pub delegate: &'a AccountView,
146    pub authority: &'a AccountView,
147    pub amount: u64,
148}
149
150impl Approve<'_> {
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 mut data = [0u8; 9];
159        data[0] = 4;
160        data[1..9].copy_from_slice(&self.amount.to_le_bytes());
161
162        let accounts = [
163            CpiAccount::from(self.source),
164            CpiAccount::from(self.delegate),
165            CpiAccount::from(self.authority),
166        ];
167
168        invoke_token(&data, &accounts, signers)
169    }
170}
171
172// ── Revoke ───────────────────────────────────────────────────────────
173
174/// Builder for SPL Token Revoke (instruction index 5).
175pub struct Revoke<'a> {
176    pub source: &'a AccountView,
177    pub authority: &'a AccountView,
178}
179
180impl Revoke<'_> {
181    #[inline]
182    pub fn invoke(&self) -> ProgramResult {
183        self.invoke_signed(&[])
184    }
185
186    #[inline]
187    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
188        let data = [5u8];
189
190        let accounts = [
191            CpiAccount::from(self.source),
192            CpiAccount::from(self.authority),
193        ];
194
195        invoke_token(&data, &accounts, signers)
196    }
197}
198
199// ── Internal helper ──────────────────────────────────────────────────
200
201#[inline]
202fn invoke_token(data: &[u8], accounts: &[CpiAccount], signers: &[Signer]) -> ProgramResult {
203    #[cfg(target_os = "solana")]
204    {
205        let ix = crate::instruction::InstructionView {
206            program_id: &TOKEN_PROGRAM_ID,
207            data,
208            accounts: &[],
209        };
210        let result = unsafe {
211            crate::syscalls::sol_invoke_signed_c(
212                &ix as *const _ as *const u8,
213                accounts.as_ptr() as *const u8,
214                accounts.len() as u64,
215                signers.as_ptr() as *const u8,
216                signers.len() as u64,
217            )
218        };
219        if result == 0 {
220            Ok(())
221        } else {
222            Err(crate::ProgramError::from(result))
223        }
224    }
225    #[cfg(not(target_os = "solana"))]
226    {
227        let _ = (data, accounts, signers);
228        Ok(())
229    }
230}
231
232/// Compatibility re-exports matching `pinocchio_token::instructions::*`.
233pub mod instructions {
234    pub use super::{Approve, Burn, CloseAccount, MintTo, Revoke, Transfer};
235}