Skip to main content

hopper_associated_token/
lib.rs

1//! Hopper-owned associated token account helpers and CPI builders.
2//!
3//! Thin first-class Hopper wrappers over ATA derivation/verification helpers
4//! and ATA program instruction builders.
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::ATA_PROGRAM_ID;
13
14#[cfg(target_os = "solana")]
15pub use hopper_solana::ata::{
16    derive_ata, derive_ata_2022, derive_ata_for_program, verify_ata, verify_ata_2022,
17    verify_ata_any,
18};
19
20/// Builder for Associated Token Account `Create` (instruction 0).
21pub struct Create<'a> {
22    pub payer: &'a AccountView,
23    pub associated_account: &'a AccountView,
24    pub wallet: &'a AccountView,
25    pub mint: &'a AccountView,
26    pub system_program: &'a AccountView,
27    pub token_program: &'a AccountView,
28}
29
30impl Create<'_> {
31    #[inline]
32    pub fn invoke(&self) -> ProgramResult {
33        self.invoke_signed(&[])
34    }
35
36    #[inline]
37    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
38        let data = [0u8];
39        let accounts = [
40            InstructionAccount::writable_signer(self.payer.address()),
41            InstructionAccount::writable(self.associated_account.address()),
42            InstructionAccount::readonly(self.wallet.address()),
43            InstructionAccount::readonly(self.mint.address()),
44            InstructionAccount::readonly(self.system_program.address()),
45            InstructionAccount::readonly(self.token_program.address()),
46        ];
47        let views = [
48            self.payer,
49            self.associated_account,
50            self.wallet,
51            self.mint,
52            self.system_program,
53            self.token_program,
54        ];
55        let instruction = InstructionView {
56            program_id: &ATA_PROGRAM_ID,
57            data: &data,
58            accounts: &accounts,
59        };
60
61        hopper_runtime::cpi::invoke_signed(&instruction, &views, signers)
62    }
63}
64
65/// Builder for Associated Token Account `CreateIdempotent` (instruction 1).
66pub struct CreateIdempotent<'a> {
67    pub payer: &'a AccountView,
68    pub associated_account: &'a AccountView,
69    pub wallet: &'a AccountView,
70    pub mint: &'a AccountView,
71    pub system_program: &'a AccountView,
72    pub token_program: &'a AccountView,
73}
74
75impl CreateIdempotent<'_> {
76    #[inline]
77    pub fn invoke(&self) -> ProgramResult {
78        self.invoke_signed(&[])
79    }
80
81    #[inline]
82    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
83        let data = [1u8];
84        let accounts = [
85            InstructionAccount::writable_signer(self.payer.address()),
86            InstructionAccount::writable(self.associated_account.address()),
87            InstructionAccount::readonly(self.wallet.address()),
88            InstructionAccount::readonly(self.mint.address()),
89            InstructionAccount::readonly(self.system_program.address()),
90            InstructionAccount::readonly(self.token_program.address()),
91        ];
92        let views = [
93            self.payer,
94            self.associated_account,
95            self.wallet,
96            self.mint,
97            self.system_program,
98            self.token_program,
99        ];
100        let instruction = InstructionView {
101            program_id: &ATA_PROGRAM_ID,
102            data: &data,
103            accounts: &accounts,
104        };
105
106        hopper_runtime::cpi::invoke_signed(&instruction, &views, signers)
107    }
108}
109
110/// Builder for ATA `RecoverNested` (instruction 2).
111pub struct RecoverNested<'a> {
112    pub nested_associated_account: &'a AccountView,
113    pub nested_token_mint: &'a AccountView,
114    pub destination_associated_account: &'a AccountView,
115    pub owner_associated_account: &'a AccountView,
116    pub owner_token_mint: &'a AccountView,
117    pub wallet: &'a AccountView,
118    pub token_program: &'a AccountView,
119}
120
121impl RecoverNested<'_> {
122    #[inline]
123    pub fn invoke(&self) -> ProgramResult {
124        self.invoke_signed(&[])
125    }
126
127    #[inline]
128    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
129        let data = [2u8];
130        let accounts = [
131            InstructionAccount::writable(self.nested_associated_account.address()),
132            InstructionAccount::readonly(self.nested_token_mint.address()),
133            InstructionAccount::writable(self.destination_associated_account.address()),
134            InstructionAccount::readonly(self.owner_associated_account.address()),
135            InstructionAccount::readonly(self.owner_token_mint.address()),
136            InstructionAccount::writable_signer(self.wallet.address()),
137            InstructionAccount::readonly(self.token_program.address()),
138        ];
139        let views = [
140            self.nested_associated_account,
141            self.nested_token_mint,
142            self.destination_associated_account,
143            self.owner_associated_account,
144            self.owner_token_mint,
145            self.wallet,
146            self.token_program,
147        ];
148        let instruction = InstructionView {
149            program_id: &ATA_PROGRAM_ID,
150            data: &data,
151            accounts: &accounts,
152        };
153
154        hopper_runtime::cpi::invoke_signed(&instruction, &views, signers)
155    }
156}
157
158pub mod instructions {
159    pub use super::{Create, CreateIdempotent, RecoverNested};
160}