gmsol_sdk/client/ops/
token_account.rs

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
9/// Token account operations.
10pub trait TokenAccountOps<C> {
11    /// Prepare a claimable account.
12    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    /// Close a claimable account if it is empty.
23    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    /// Prepare associated token account.
33    fn prepare_associated_token_account(
34        &self,
35        mint: &Pubkey,
36        token_program_id: &Pubkey,
37        owner: Option<&Pubkey>,
38    ) -> TransactionBuilder<C>;
39
40    /// Create token metadata for a token whose mint authority is `store`.
41    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    /// Update a token metadata whose update authority is `store`.
51    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    /// Update a token metadata whose update authority is `store` for the give mint.
61    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}