1use std::{future::Future, ops::Deref};
2
3use gmsol_programs::gmsol_store::{
4 accounts::GtExchange,
5 client::{accounts, args},
6};
7use gmsol_solana_utils::{transaction_builder::TransactionBuilder, IntoAtomicGroup};
8use gmsol_utils::gt::get_time_window_index;
9use solana_sdk::{pubkey::Pubkey, signer::Signer, system_program};
10
11use crate::{builders::gt::MintGtReward, utils::zero_copy::ZeroCopy};
12
13pub trait GtOps<C> {
15 fn initialize_gt(
17 &self,
18 store: &Pubkey,
19 decimals: u8,
20 initial_minting_cost: u128,
21 grow_factor: u128,
22 grow_step: u64,
23 ranks: Vec<u64>,
24 ) -> TransactionBuilder<C>;
25
26 fn gt_set_order_fee_discount_factors(
28 &self,
29 store: &Pubkey,
30 factors: Vec<u128>,
31 ) -> TransactionBuilder<C>;
32
33 fn gt_set_referral_reward_factors(
35 &self,
36 store: &Pubkey,
37 factors: Vec<u128>,
38 ) -> TransactionBuilder<C>;
39
40 fn gt_set_exchange_time_window(&self, store: &Pubkey, window: u32) -> TransactionBuilder<C>;
42
43 fn prepare_gt_exchange_vault_with_time_window_index(
45 &self,
46 store: &Pubkey,
47 time_window_index: i64,
48 time_window: u32,
49 ) -> TransactionBuilder<C, Pubkey>;
50
51 fn prepare_gt_exchange_vault_with_time_window(
53 &self,
54 store: &Pubkey,
55 time_window: u32,
56 ) -> crate::Result<TransactionBuilder<C, Pubkey>> {
57 Ok(self.prepare_gt_exchange_vault_with_time_window_index(
58 store,
59 current_time_window_index(time_window)?,
60 time_window,
61 ))
62 }
63
64 fn confirm_gt_exchange_vault(
66 &self,
67 store: &Pubkey,
68 vault: &Pubkey,
69 buyback_value: u128,
70 buyback_price: Option<u128>,
71 ) -> TransactionBuilder<C>;
72
73 fn request_gt_exchange_with_time_window_index(
75 &self,
76 store: &Pubkey,
77 time_window_index: i64,
78 time_window: u32,
79 amount: u64,
80 ) -> TransactionBuilder<C>;
81
82 fn request_gt_exchange_with_time_window(
84 &self,
85 store: &Pubkey,
86 time_window: u32,
87 amount: u64,
88 ) -> crate::Result<TransactionBuilder<C>> {
89 Ok(self.request_gt_exchange_with_time_window_index(
90 store,
91 current_time_window_index(time_window)?,
92 time_window,
93 amount,
94 ))
95 }
96
97 fn close_gt_exchange(
99 &self,
100 store: &Pubkey,
101 exchange: &Pubkey,
102 hint_owner: Option<&Pubkey>,
103 hint_vault: Option<&Pubkey>,
104 ) -> impl Future<Output = crate::Result<TransactionBuilder<C>>>;
105
106 fn mint_gt_reward(
108 &self,
109 store: &Pubkey,
110 owner: &Pubkey,
111 amount: u64,
112 ) -> crate::Result<TransactionBuilder<C>>;
113}
114
115impl<C: Deref<Target = impl Signer> + Clone> GtOps<C> for crate::Client<C> {
116 fn initialize_gt(
117 &self,
118 store: &Pubkey,
119 decimals: u8,
120 initial_minting_cost: u128,
121 grow_factor: u128,
122 grow_step: u64,
123 ranks: Vec<u64>,
124 ) -> TransactionBuilder<C> {
125 self.store_transaction()
126 .anchor_accounts(accounts::InitializeGt {
127 authority: self.payer(),
128 store: *store,
129 system_program: system_program::ID,
130 })
131 .anchor_args(args::InitializeGt {
132 decimals,
133 initial_minting_cost,
134 grow_factor,
135 grow_step,
136 ranks,
137 })
138 }
139
140 fn gt_set_order_fee_discount_factors(
141 &self,
142 store: &Pubkey,
143 factors: Vec<u128>,
144 ) -> TransactionBuilder<C> {
145 self.store_transaction()
146 .anchor_accounts(accounts::GtSetOrderFeeDiscountFactors {
147 authority: self.payer(),
148 store: *store,
149 })
150 .anchor_args(args::GtSetOrderFeeDiscountFactors { factors })
151 }
152
153 fn gt_set_referral_reward_factors(
154 &self,
155 store: &Pubkey,
156 factors: Vec<u128>,
157 ) -> TransactionBuilder<C> {
158 self.store_transaction()
159 .anchor_accounts(accounts::GtSetReferralRewardFactors {
160 authority: self.payer(),
161 store: *store,
162 })
163 .anchor_args(args::GtSetReferralRewardFactors { factors })
164 }
165
166 fn gt_set_exchange_time_window(&self, store: &Pubkey, window: u32) -> TransactionBuilder<C> {
167 self.store_transaction()
168 .anchor_accounts(accounts::GtSetExchangeTimeWindow {
169 authority: self.payer(),
170 store: *store,
171 })
172 .anchor_args(args::GtSetExchangeTimeWindow { window })
173 }
174
175 fn prepare_gt_exchange_vault_with_time_window_index(
176 &self,
177 store: &Pubkey,
178 time_window_index: i64,
179 time_window: u32,
180 ) -> TransactionBuilder<C, Pubkey> {
181 let vault = self.find_gt_exchange_vault_address(store, time_window_index, time_window);
182 self.store_transaction()
183 .anchor_accounts(accounts::PrepareGtExchangeVault {
184 payer: self.payer(),
185 store: *store,
186 vault,
187 system_program: system_program::ID,
188 })
189 .anchor_args(args::PrepareGtExchangeVault { time_window_index })
190 .output(vault)
191 }
192
193 fn confirm_gt_exchange_vault(
194 &self,
195 store: &Pubkey,
196 vault: &Pubkey,
197 buyback_value: u128,
198 buyback_price: Option<u128>,
199 ) -> TransactionBuilder<C> {
200 self.store_transaction()
201 .anchor_accounts(accounts::ConfirmGtExchangeVaultV2 {
202 authority: self.payer(),
203 store: *store,
204 vault: *vault,
205 event_authority: self.store_event_authority(),
206 program: *self.store_program_id(),
207 })
208 .anchor_args(args::ConfirmGtExchangeVaultV2 {
209 buyback_value,
210 buyback_price,
211 })
212 }
213
214 fn request_gt_exchange_with_time_window_index(
215 &self,
216 store: &Pubkey,
217 time_window_index: i64,
218 time_window: u32,
219 amount: u64,
220 ) -> TransactionBuilder<C> {
221 let owner = self.payer();
222 let vault = self.find_gt_exchange_vault_address(store, time_window_index, time_window);
223 self.store_transaction()
224 .anchor_accounts(accounts::RequestGtExchange {
225 owner,
226 store: *store,
227 user: self.find_user_address(store, &owner),
228 vault,
229 exchange: self.find_gt_exchange_address(&vault, &owner),
230 system_program: system_program::ID,
231 event_authority: self.store_event_authority(),
232 program: *self.store_program_id(),
233 })
234 .anchor_args(args::RequestGtExchange { amount })
235 }
236
237 async fn close_gt_exchange(
238 &self,
239 store: &Pubkey,
240 exchange: &Pubkey,
241 hint_owner: Option<&Pubkey>,
242 hint_vault: Option<&Pubkey>,
243 ) -> crate::Result<TransactionBuilder<C>> {
244 let (owner, vault) = match (hint_owner, hint_vault) {
245 (Some(owner), Some(vault)) => (*owner, *vault),
246 _ => {
247 let exchange = self
248 .account::<ZeroCopy<GtExchange>>(exchange)
249 .await?
250 .ok_or(crate::Error::NotFound)?
251 .0;
252 (exchange.owner, exchange.vault)
253 }
254 };
255
256 Ok(self
257 .store_transaction()
258 .anchor_accounts(accounts::CloseGtExchange {
259 authority: self.payer(),
260 store: *store,
261 owner,
262 vault,
263 exchange: *exchange,
264 })
265 .anchor_args(args::CloseGtExchange {}))
266 }
267
268 fn mint_gt_reward(
269 &self,
270 store: &Pubkey,
271 owner: &Pubkey,
272 amount: u64,
273 ) -> crate::Result<TransactionBuilder<C>> {
274 Ok(self.store_transaction().pre_atomic_group(
275 MintGtReward::builder()
276 .amount(amount)
277 .payer(self.payer())
278 .owner((*owner).into())
279 .store_program(self.store_program_for_builders(store))
280 .build()
281 .into_atomic_group(&())?,
282 true,
283 ))
284 }
285}
286
287pub fn current_time_window_index(time_window: u32) -> crate::Result<i64> {
289 use std::time::SystemTime;
290 let now = SystemTime::now()
291 .duration_since(SystemTime::UNIX_EPOCH)
292 .map_err(crate::Error::custom)?;
293
294 let ts = now.as_secs() as i64;
295 Ok(get_time_window_index(ts, time_window as i64))
296}