1use std::ops::Deref;
2
3use gmsol_programs::gmsol_store::client::{accounts, args};
4use gmsol_solana_utils::transaction_builder::TransactionBuilder;
5use solana_sdk::{pubkey::Pubkey, signer::Signer, system_program};
6
7use crate::client::Client;
8
9pub trait TokenAccountOps<C> {
11 fn use_claimable_account(
13 &self,
14 store: &Pubkey,
15 mint: &Pubkey,
16 user: &Pubkey,
17 timestamp: i64,
18 account: &Pubkey,
19 amount: u64,
20 ) -> TransactionBuilder<C>;
21
22 fn close_empty_claimable_account(
24 &self,
25 store: &Pubkey,
26 mint: &Pubkey,
27 user: &Pubkey,
28 timestamp: i64,
29 account: &Pubkey,
30 ) -> TransactionBuilder<C>;
31
32 fn prepare_associated_token_account(
34 &self,
35 mint: &Pubkey,
36 token_program_id: &Pubkey,
37 owner: Option<&Pubkey>,
38 ) -> TransactionBuilder<C>;
39
40 fn create_token_metadata(
42 &self,
43 store: &Pubkey,
44 mint: &Pubkey,
45 name: String,
46 symbol: String,
47 uri: String,
48 ) -> TransactionBuilder<C, Pubkey>;
49
50 fn update_token_metadata(
52 &self,
53 store: &Pubkey,
54 metadata: &Pubkey,
55 name: String,
56 symbol: String,
57 uri: String,
58 ) -> TransactionBuilder<C>;
59
60 fn update_token_metadata_by_mint(
62 &self,
63 store: &Pubkey,
64 mint: &Pubkey,
65 name: String,
66 symbol: String,
67 uri: String,
68 ) -> TransactionBuilder<C> {
69 let metadata = find_token_metadata_address(mint);
70 self.update_token_metadata(store, &metadata, name, symbol, uri)
71 }
72}
73
74impl<C: Deref<Target = impl Signer> + Clone> TokenAccountOps<C> for Client<C> {
75 fn use_claimable_account(
76 &self,
77 store: &Pubkey,
78 mint: &Pubkey,
79 owner: &Pubkey,
80 timestamp: i64,
81 account: &Pubkey,
82 amount: u64,
83 ) -> TransactionBuilder<C> {
84 let authority = self.payer();
85 self.store_transaction()
86 .anchor_args(args::UseClaimableAccount { timestamp, amount })
87 .anchor_accounts(accounts::UseClaimableAccount {
88 authority,
89 store: *store,
90 mint: *mint,
91 owner: *owner,
92 account: *account,
93 system_program: system_program::ID,
94 token_program: anchor_spl::token::ID,
95 })
96 }
97
98 fn close_empty_claimable_account(
99 &self,
100 store: &Pubkey,
101 mint: &Pubkey,
102 owner: &Pubkey,
103 timestamp: i64,
104 account: &Pubkey,
105 ) -> TransactionBuilder<C> {
106 let authority = self.payer();
107 self.store_transaction()
108 .anchor_args(args::CloseEmptyClaimableAccount { timestamp })
109 .anchor_accounts(accounts::CloseEmptyClaimableAccount {
110 authority,
111 store: *store,
112 mint: *mint,
113 owner: *owner,
114 account: *account,
115 system_program: system_program::ID,
116 token_program: anchor_spl::token::ID,
117 })
118 }
119
120 fn prepare_associated_token_account(
121 &self,
122 mint: &Pubkey,
123 token_program_id: &Pubkey,
124 owner: Option<&Pubkey>,
125 ) -> TransactionBuilder<C> {
126 use anchor_spl::associated_token::spl_associated_token_account;
127
128 let payer = self.payer();
129 let owner = owner.copied().unwrap_or(payer);
130 let ix =
131 spl_associated_token_account::instruction::create_associated_token_account_idempotent(
132 &payer,
133 &owner,
134 mint,
135 token_program_id,
136 );
137 self.store_transaction()
138 .program(spl_associated_token_account::ID)
139 .pre_instruction(ix, true)
140 }
141
142 fn create_token_metadata(
143 &self,
144 store: &Pubkey,
145 mint: &Pubkey,
146 name: String,
147 symbol: String,
148 uri: String,
149 ) -> TransactionBuilder<C, Pubkey> {
150 let authority = self.payer();
151 let metadata = find_token_metadata_address(mint);
152 self.store_transaction()
153 .anchor_accounts(accounts::CreateTokenMetadata {
154 authority,
155 store: *store,
156 mint: *mint,
157 metadata,
158 system_program: system_program::ID,
159 sysvar_instructions: solana_sdk::sysvar::instructions::ID,
160 metadata_program: anchor_spl::metadata::ID,
161 })
162 .anchor_args(args::CreateTokenMetadata { name, symbol, uri })
163 .output(metadata)
164 }
165
166 fn update_token_metadata(
167 &self,
168 store: &Pubkey,
169 metadata: &Pubkey,
170 name: String,
171 symbol: String,
172 uri: String,
173 ) -> TransactionBuilder<C> {
174 let authority = self.payer();
175 self.store_transaction()
176 .anchor_accounts(accounts::UpdateTokenMetadata {
177 authority,
178 store: *store,
179 metadata: *metadata,
180 metadata_program: anchor_spl::metadata::ID,
181 })
182 .anchor_args(args::UpdateTokenMetadata { name, symbol, uri })
183 }
184}
185
186const TOKEN_METADATA_SEED: &[u8] = b"metadata";
187
188fn find_token_metadata_address(mint: &Pubkey) -> Pubkey {
189 let program_id = &anchor_spl::metadata::ID;
190 Pubkey::find_program_address(
191 &[TOKEN_METADATA_SEED, program_id.as_ref(), mint.as_ref()],
192 program_id,
193 )
194 .0
195}