@@ -3,13 +3,20 @@ use std::{sync::Arc, time::Instant};
33use anchor_lang:: { AccountDeserialize , Discriminator } ;
44use anyhow:: Result ;
55use jito_priority_fee_distribution_sdk:: {
6- instruction:: close_claim_status_ix as close_pf_claim_status_ix,
6+ instruction:: {
7+ close_claim_status_ix as close_pf_claim_status_ix,
8+ close_priority_fee_distribution_account_ix,
9+ } ,
710 jito_priority_fee_distribution:: accounts:: ClaimStatus as PriorityFeeDistributionClaimStatus ,
11+ jito_priority_fee_distribution:: accounts:: Config as PriorityFeeDistributionConfig ,
812 PriorityFeeDistributionAccount ,
913} ;
1014use jito_tip_distribution_sdk:: {
11- instruction:: close_claim_status_ix as close_tip_claim_status_ix,
15+ instruction:: {
16+ close_claim_status_ix as close_tip_claim_status_ix, close_tip_distribution_account_ix,
17+ } ,
1218 jito_tip_distribution:: accounts:: ClaimStatus as TipDistributionClaimStatus ,
19+ jito_tip_distribution:: accounts:: Config as TipDistributionConfig ,
1320 TipDistributionAccount ,
1421} ;
1522use log:: { error, info} ;
@@ -34,6 +41,32 @@ pub async fn close_expired_accounts(
3441 priority_fee_distribution_program_id : Pubkey ,
3542 signer : Arc < Keypair > ,
3643 num_monitored_epochs : u64 ,
44+ ) -> Result < ( ) > {
45+ close_expired_distribution_accounts (
46+ rpc_url,
47+ tip_distribution_program_id,
48+ priority_fee_distribution_program_id,
49+ signer. clone ( ) ,
50+ num_monitored_epochs,
51+ )
52+ . await ?;
53+ close_expired_claims (
54+ rpc_url,
55+ tip_distribution_program_id,
56+ priority_fee_distribution_program_id,
57+ signer. clone ( ) ,
58+ num_monitored_epochs,
59+ )
60+ . await ?;
61+ Ok ( ( ) )
62+ }
63+
64+ pub async fn close_expired_claims (
65+ rpc_url : & str ,
66+ tip_distribution_program_id : Pubkey ,
67+ priority_fee_distribution_program_id : Pubkey ,
68+ signer : Arc < Keypair > ,
69+ num_monitored_epochs : u64 ,
3770) -> Result < ( ) > {
3871 let epochs_to_process = {
3972 // Use default timeout and commitment config for fetching the current epoch
@@ -56,7 +89,6 @@ pub async fn close_expired_accounts(
5689 let duration = start. elapsed ( ) ;
5790 datapoint_info ! (
5891 "tip_router_cli.expired_claim_statuses" ,
59- ( "epoch" , epoch, i64 ) ,
6092 (
6193 "expired_tip_claim_statuses" ,
6294 tip_distribution_claim_accounts. len( ) ,
@@ -68,6 +100,7 @@ pub async fn close_expired_accounts(
68100 i64
69101 ) ,
70102 ( "duration" , duration. as_secs( ) , i64 ) ,
103+ "epoch" => epoch. to_string( ) ,
71104 ) ;
72105
73106 let close_tip_claim_transactions = close_tip_claim_transactions (
@@ -112,6 +145,100 @@ pub async fn close_expired_accounts(
112145 Ok ( ( ) )
113146}
114147
148+ pub async fn close_expired_distribution_accounts (
149+ rpc_url : & str ,
150+ tip_distribution_program_id : Pubkey ,
151+ priority_fee_distribution_program_id : Pubkey ,
152+ signer : Arc < Keypair > ,
153+ num_monitored_epochs : u64 ,
154+ ) -> Result < ( ) > {
155+ let epochs_to_process = {
156+ // Use default timeout and commitment config for fetching the current epoch
157+ let rpc_client = rpc_utils:: new_rpc_client ( rpc_url) ;
158+ let current_epoch = rpc_client. get_epoch_info ( ) . await ?. epoch ;
159+ ( current_epoch - num_monitored_epochs) ..current_epoch
160+ } ;
161+ for epoch in epochs_to_process {
162+ let rpc_client = rpc_utils:: new_high_timeout_rpc_client ( rpc_url) ;
163+ info ! ( "Fetching distribution accounts expiring in epoch {}" , epoch) ;
164+ let start = Instant :: now ( ) ;
165+ let ( tip_distribution_accounts, priority_fee_distribution_accounts) =
166+ fetch_expired_distribution_accounts (
167+ & rpc_client,
168+ tip_distribution_program_id,
169+ priority_fee_distribution_program_id,
170+ epoch,
171+ )
172+ . await ?;
173+ let duration = start. elapsed ( ) ;
174+ datapoint_info ! (
175+ "tip_router_cli.expired_distribution_accounts" ,
176+ (
177+ "expired_tip_distribution_accounts" ,
178+ tip_distribution_accounts. len( ) ,
179+ i64
180+ ) ,
181+ (
182+ "expired_priority_fee_distribution_accounts" ,
183+ priority_fee_distribution_accounts. len( ) ,
184+ i64
185+ ) ,
186+ ( "duration" , duration. as_secs( ) , i64 ) ,
187+ "epoch" => epoch. to_string( ) ,
188+ ) ;
189+
190+ let close_tip_claim_transactions = close_tip_distribution_account_transactions (
191+ & rpc_client,
192+ & tip_distribution_accounts,
193+ tip_distribution_program_id,
194+ signer. pubkey ( ) ,
195+ epoch,
196+ )
197+ . await ?;
198+ let close_priority_fee_claim_transactions =
199+ close_priority_fee_distribution_account_transactions (
200+ & rpc_client,
201+ & priority_fee_distribution_accounts,
202+ priority_fee_distribution_program_id,
203+ signer. pubkey ( ) ,
204+ epoch,
205+ )
206+ . await ?;
207+ let mut transactions = [
208+ close_tip_claim_transactions,
209+ close_priority_fee_claim_transactions,
210+ ]
211+ . concat ( ) ;
212+
213+ info ! (
214+ "Processing {} close distribution account transactions" ,
215+ transactions. len( )
216+ ) ;
217+ let rpc_client = rpc_utils:: new_rpc_client ( rpc_url) ;
218+ transactions. shuffle ( & mut rand:: thread_rng ( ) ) ;
219+ for batch in transactions. chunks_mut ( 100_000 ) {
220+ let start = Instant :: now ( ) ;
221+ let mut blockhash = rpc_client. get_latest_blockhash ( ) . await ?;
222+ for transaction in batch. iter_mut ( ) {
223+ transaction. sign ( & [ & signer] , blockhash) ;
224+ let maybe_signature = rpc_client. send_transaction ( transaction) . await ;
225+ if let Err ( e) = maybe_signature {
226+ // Fetch a new blockhash if the transaction failed
227+ blockhash = rpc_client. get_latest_blockhash ( ) . await ?;
228+ error ! ( "Error sending transaction: {:?}" , e) ;
229+ }
230+ }
231+ let duration = start. elapsed ( ) ;
232+ info ! (
233+ "Processed batch of {} close distribution account transactions in {:?} seconds" ,
234+ batch. len( ) ,
235+ duration. as_secs( )
236+ ) ;
237+ }
238+ }
239+ Ok ( ( ) )
240+ }
241+
115242fn close_tip_claim_transactions (
116243 accounts : & [ ( Pubkey , TipDistributionClaimStatus ) ] ,
117244 tip_distribution_program_id : Pubkey ,
@@ -150,6 +277,91 @@ fn close_priority_fee_claim_transactions(
150277 pack_transactions ( instructions, payer, MAX_TRANSACTION_SIZE )
151278}
152279
280+ async fn close_tip_distribution_account_transactions (
281+ rpc_client : & RpcClient ,
282+ accounts : & [ ( Pubkey , TipDistributionAccount ) ] ,
283+ tip_distribution_program_id : Pubkey ,
284+ payer : Pubkey ,
285+ target_epoch : u64 ,
286+ ) -> Result < Vec < Transaction > > {
287+ let config_pubkey =
288+ jito_tip_distribution_sdk:: derive_config_account_address ( & tip_distribution_program_id) . 0 ;
289+
290+ let config_account = rpc_client
291+ . get_account_with_config (
292+ & config_pubkey,
293+ RpcAccountInfoConfig {
294+ encoding : Some ( UiAccountEncoding :: Base64 ) ,
295+ ..RpcAccountInfoConfig :: default ( )
296+ } ,
297+ )
298+ . await ?
299+ . value
300+ . ok_or ( anyhow:: anyhow!( "Config account not found" ) ) ?;
301+
302+ let tip_distribution_config =
303+ TipDistributionConfig :: try_deserialize ( & mut config_account. data . as_slice ( ) ) ?;
304+
305+ let instructions: Vec < _ > = accounts
306+ . iter ( )
307+ . map ( |( pubkey, account) | {
308+ close_tip_distribution_account_ix (
309+ config_pubkey,
310+ * pubkey,
311+ tip_distribution_config. expired_funds_account ,
312+ account. validator_vote_account ,
313+ payer,
314+ account. epoch_created_at ,
315+ )
316+ } )
317+ . collect ( ) ;
318+
319+ Ok ( pack_transactions ( instructions, payer, MAX_TRANSACTION_SIZE ) )
320+ }
321+
322+ async fn close_priority_fee_distribution_account_transactions (
323+ rpc_client : & RpcClient ,
324+ accounts : & [ ( Pubkey , PriorityFeeDistributionAccount ) ] ,
325+ priority_fee_distribution_program_id : Pubkey ,
326+ payer : Pubkey ,
327+ target_epoch : u64 ,
328+ ) -> Result < Vec < Transaction > > {
329+ let config_pubkey = jito_priority_fee_distribution_sdk:: derive_config_account_address (
330+ & priority_fee_distribution_program_id,
331+ )
332+ . 0 ;
333+ let config_account = rpc_client
334+ . get_account_with_config (
335+ & config_pubkey,
336+ RpcAccountInfoConfig {
337+ encoding : Some ( UiAccountEncoding :: Base64 ) ,
338+ ..RpcAccountInfoConfig :: default ( )
339+ } ,
340+ )
341+ . await ?
342+ . value
343+ . ok_or ( anyhow:: anyhow!( "Config account not found" ) ) ?;
344+
345+ let priority_fee_distribution_config =
346+ PriorityFeeDistributionConfig :: try_deserialize ( & mut config_account. data . as_slice ( ) ) ?;
347+
348+ let instructions: Vec < _ > = accounts
349+ . iter ( )
350+ . map ( |( pubkey, account) | {
351+ close_priority_fee_distribution_account_ix (
352+ config_pubkey,
353+ * pubkey,
354+ priority_fee_distribution_config. expired_funds_account ,
355+ account. validator_vote_account ,
356+ payer,
357+ account. epoch_created_at ,
358+ )
359+ } )
360+ . collect ( ) ;
361+
362+ Ok ( pack_transactions ( instructions, payer, MAX_TRANSACTION_SIZE ) )
363+ }
364+
153365pub async fn fetch_expired_distribution_accounts (
154366 rpc_client : & RpcClient ,
155367 tip_distribution_program_id : Pubkey ,
@@ -173,7 +385,9 @@ pub async fn fetch_expired_distribution_accounts(
173385 + 8 // merkle_root.max_total_claim
174386 + 8 // merkle_root.max_num_nodes
175387 + 8 // merkle_root.total_funds_claimed
176- + 8 , // merkle_root.num_nodes_claimed
388+ + 8 // merkle_root.num_nodes_claimed
389+ + 8 // epoch_created_at
390+ + 2 , // commission_bps
177391 target_epoch. to_le_bytes( ) . to_vec( ) ,
178392 ) ) ,
179393 ] ;
@@ -203,7 +417,9 @@ pub async fn fetch_expired_distribution_accounts(
203417 + 8 // merkle_root.max_total_claim
204418 + 8 // merkle_root.max_num_nodes
205419 + 8 // merkle_root.total_funds_claimed
206- + 8 , // merkle_root.num_nodes_claimed
420+ + 8 // merkle_root.num_nodes_claimed
421+ + 8 // epoch_created_at
422+ + 2 , // commission_bps
207423 target_epoch. to_le_bytes( ) . to_vec( ) ,
208424 ) ) ,
209425 ] ;
0 commit comments