1use crate::{
3 idl::arcium::{
4 accounts::{ArxNode, MXEAccount},
5 types::{
6 ArgumentList,
7 BN254G2BLSPublicKey,
8 CallbackInstruction,
9 CircuitSource,
10 MempoolSize,
11 NodeMetadata,
12 Output,
13 Parameter,
14 },
15 ID as ARCIUM_PROG_ID,
16 },
17 instruction::{
18 activate_cluster_ix,
19 bind_recovery_peer_migration_ix,
20 claim_computation_rent_ix,
21 claim_failure_append_ix,
22 claim_failure_finalize_ix,
23 claim_failure_init_ix,
24 claim_node_fees_ix,
25 close_aborted_key_recovery_ix,
26 close_successful_key_recovery_ix,
27 deactivate_arx_ix,
28 finalize_mxe_keys_ix,
29 increase_mempool_size_ix,
30 init_arx_node_acc_ix,
31 init_cluster_ix,
32 init_computation_definition_ix,
33 init_key_recovery_execution_part1_ix,
34 init_key_recovery_execution_part2_ix,
35 init_mxe_part1_ix,
36 init_mxe_part2_ix,
37 init_network_program_ix,
38 init_node_operator_acc_ix,
39 init_recovery_peer_acc_ix,
40 join_cluster_ix,
41 migrate_arx_node_ix,
42 migrate_cluster_ix,
43 migrate_mxe_account_ix,
44 migrate_recovery_peer_ix,
45 propose_fee_ix,
46 propose_join_cluster_ix,
47 queue_computation_ix,
48 queue_key_recovery_finalize_ix,
49 queue_key_recovery_init_ix,
50 reclaim_expired_computation_fee_ix,
51 reclaim_failure_rent_idempotent_ix,
52 register_account_in_lut_ix,
53 requeue_mxe_keygen_ix,
54 set_active_ix,
55 set_arx_node_metadata_ix,
56 set_cluster_td_info_ix,
57 set_migration_ix,
58 staking::{
59 add_primary_stake_to_legacy_acc_ix,
60 init_cluster_stake_state_ix,
61 init_mxe_recovery_stake_state_ix,
62 },
63 submit_aggregated_bls_pubkey_ix,
64 submit_key_recovery_share_ix,
65 vote_fee_ix,
66 },
67 pda::{arx_acc, mxe_acc},
68 utils::MAX_RECOVERY_PEERS,
69};
70use anchor_client::{Client, Cluster as SolanaCluster, Program, RequestBuilder, ThreadSafeSigner};
71use anchor_lang::{prelude::*, solana_program::instruction::Instruction as SolanaInstruction};
72use rand::RngCore;
73use solana_keypair::Keypair;
74use solana_rpc_client::nonblocking::rpc_client::RpcClient as AsyncRpcClient;
75use solana_rpc_client_api::config::RpcSendTransactionConfig;
76use solana_signature::Signature;
77use solana_signer::Signer;
78use solana_transaction::Transaction;
79use std::{ops::Deref, result::Result, sync::Arc, vec};
81
82pub(crate) const MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE: usize = 587;
86
87#[derive(::thiserror::Error, Debug, Clone, PartialEq, Eq)]
88pub enum ClaimFailureError {
89 #[error(
90 "Multi-transaction failure claims not yet supported: callback data size {size} exceeds single-transaction limit of {limit} bytes"
91 )]
92 CallbackDataTooLarge { size: usize, limit: usize },
93}
94
95type ClaimResult<T> = Result<T, ClaimFailureError>;
97
98pub const DEFAULT_PRIM_STAKE_AMOUNT: u64 = 1000;
99pub const DEFAULT_FEE_BASIS_POINTS: u16 = 0;
100pub const DEFAULT_LOCKUP_EPOCHS: u64 = 10;
101pub const DEFAULT_URL: &str = "https://arcium.com";
102pub const DEFAULT_LOCATION: u8 = 0;
103pub const DEFAULT_CU_CLAIM: u64 = 1000;
104pub const DEFAULT_MAX_CLUSTERS: u32 = 1;
105pub const DEFAULT_MAX_SIZE: u32 = 1;
106pub const DEFAULT_CLUSTER_ENCRYPTION_PUBKEY: [u8; 32] = [0; 32];
107pub const DEFAULT_CU_PRICE: u64 = 1;
108pub const DEFAULT_COMPILED_CIRCUIT: [u8; 24] = [0; 24];
109pub const DEFAULT_AUTHORITY_PUBKEYS: Option<Vec<Pubkey>> = None;
110pub const DEFAULT_PARAMS: Vec<Parameter> = vec![];
111pub const DEFAULT_CALLBACK_DATA_OBJS: Vec<Pubkey> = vec![];
112pub const DEFAULT_OUTPUT_SCALAR_LEN: u8 = 0;
113pub const DEFAULT_DUMMY_CALLBACK_DISC: [u8; 8] = [0; 8];
114pub const DEFAULT_DUMMY_ENCRYPTION_PUBKEY: [u8; 32] = [0; 32];
115pub const DEFAULT_DUMMY_PROGRAM_ID: Pubkey =
116 pubkey!("Bzabqe5qowkb54kQ96frT6WY7KNLQiMhj4pGGmrZMHRa");
117
118pub async fn init_network_program(
119 arcium_program: &Program<Arc<Keypair>>,
120 signer: Arc<Keypair>,
121 config: RpcSendTransactionConfig,
122) -> Signature {
123 let current_time = current_unix_timestamp(arcium_program.internal_rpc()).await;
124 let ix = init_network_program_ix(current_time, &signer.pubkey());
125 let tx = arcium_program.request().instruction(ix);
126 send_tx(vec![signer], tx, config).await
127}
128
129#[allow(clippy::too_many_arguments)]
130pub async fn submit_key_recovery_share(
131 arcium_program: &Program<Arc<Keypair>>,
132 signer: Arc<Keypair>,
133 original_mxe_program: &Pubkey,
134 backup_mxe_program: &Pubkey,
135 peer_offset: u32,
136 peer_index: u32,
137 share: [[u8; 32]; 5],
138 config: RpcSendTransactionConfig,
139) -> Signature {
140 let ix = submit_key_recovery_share_ix(
141 &signer.pubkey(),
142 original_mxe_program,
143 backup_mxe_program,
144 peer_offset,
145 peer_index,
146 share,
147 );
148 let tx = arcium_program.request().instruction(ix);
149 send_tx(vec![signer], tx, config).await
150}
151
152pub async fn init_node_operator_acc(
155 arcium_program: &Program<Arc<Keypair>>,
156 signer: Arc<Keypair>,
157 url: String,
158 location: u8,
159
160 config: RpcSendTransactionConfig,
161) -> Signature {
162 let ix = init_node_operator_acc_ix(&signer.pubkey(), url, location);
163 let tx = arcium_program.request().instruction(ix);
164 send_tx(vec![signer], tx, config).await
165}
166
167#[allow(clippy::too_many_arguments)]
168pub async fn init_arx_node_acc(
169 arcium_program: &Program<Arc<Keypair>>,
170 operator_signer: Arc<Keypair>,
171 node_signer: Arc<Keypair>,
172 node_offset: u32,
173 callback_authority: Pubkey,
174 cu_claim: u64,
175 metadata: NodeMetadata,
176 bls_pubkey: BN254G2BLSPublicKey,
177 bls_pop_sig: [u8; 64],
178 x25519_pubkey: [u8; 32],
179 primary_stake_account: Pubkey,
180 staker: Arc<Keypair>,
181 config: RpcSendTransactionConfig,
182) -> Signature {
183 let ix = init_arx_node_acc_ix(
184 &operator_signer.pubkey(),
185 &node_signer.pubkey(),
186 node_offset,
187 &callback_authority,
188 cu_claim,
189 bls_pubkey,
190 bls_pop_sig,
191 metadata,
192 x25519_pubkey,
193 &primary_stake_account,
194 &staker.pubkey(),
195 );
196 let tx = arcium_program.request().instruction(ix);
197 send_tx(vec![operator_signer, node_signer, staker], tx, config).await
198}
199
200pub async fn set_arx_node_metadata(
201 arcium_program: &Program<Arc<Keypair>>,
202 signer: Arc<Keypair>,
203 node_offset: u32,
204 metadata: NodeMetadata,
205 config: RpcSendTransactionConfig,
206) -> Signature {
207 let ix = set_arx_node_metadata_ix(&signer.pubkey(), node_offset, metadata);
208 let tx = arcium_program.request().instruction(ix);
209 send_tx(vec![signer], tx, config).await
210}
211
212pub async fn deactivate_arx(
213 arcium_program: &Program<Arc<Keypair>>,
214 signer: Arc<Keypair>,
215 node_offset: u32,
216 cluster_offset: Option<u32>,
217 config: RpcSendTransactionConfig,
218) -> Signature {
219 let ix = deactivate_arx_ix(&signer.pubkey(), node_offset, cluster_offset);
220 let tx = arcium_program.request().instruction(ix);
221 send_tx(vec![signer], tx, config).await
222}
223
224#[allow(clippy::too_many_arguments)]
228pub async fn init_cluster(
229 arcium_program: &Program<Arc<Keypair>>,
230 payer: Arc<Keypair>,
231 cluster_authority: Pubkey,
232 cluster_offset: u32,
233 cluster_size: u16,
234 cu_price: u64,
235 mempool_size: MempoolSize,
236 td_info: Option<NodeMetadata>,
237 config: RpcSendTransactionConfig,
238) -> Signature {
239 let tx = init_cluster_instructions(
240 &payer.pubkey(),
241 cluster_authority,
242 cluster_offset,
243 cluster_size,
244 cu_price,
245 mempool_size,
246 td_info,
247 )
248 .into_iter()
249 .fold(arcium_program.request(), |tx, ix| tx.instruction(ix));
250 send_tx(vec![payer], tx, config).await
251}
252
253fn init_cluster_instructions(
254 payer: &Pubkey,
255 cluster_authority: Pubkey,
256 cluster_offset: u32,
257 cluster_size: u16,
258 cu_price: u64,
259 mempool_size: MempoolSize,
260 td_info: Option<NodeMetadata>,
261) -> [SolanaInstruction; 2] {
262 [
263 init_cluster_ix(
264 payer,
265 cluster_authority,
266 cluster_offset,
267 cluster_size,
268 cu_price,
269 mempool_size,
270 td_info,
271 ),
272 init_cluster_stake_state_ix(payer, cluster_offset),
273 ]
274}
275
276pub async fn init_recovery_peer_acc(
277 arcium_program: &Program<Arc<Keypair>>,
278 signer: Arc<Keypair>,
279 peer_offset: u32,
280 x25519_pubkey: [u8; 32],
281 primary_stake_account: &Pubkey,
282 staker: Arc<Keypair>,
283 config: RpcSendTransactionConfig,
284) -> Signature {
285 let ix = init_recovery_peer_acc_ix(
286 &signer.pubkey(),
287 peer_offset,
288 x25519_pubkey,
289 primary_stake_account,
290 &staker.pubkey(),
291 );
292 let tx = arcium_program.request().instruction(ix);
293 send_tx(vec![signer, staker], tx, config).await
294}
295
296#[allow(clippy::too_many_arguments)]
297pub async fn init_mxe(
298 arcium_program: &Program<Arc<Keypair>>,
299 payer: Arc<Keypair>,
300 mxe_program: &Pubkey,
301 mxe_authority: Option<Pubkey>,
302 keygen_comp_offset: u64,
303 key_recovery_init_comp_offset: u64,
304 cluster_offset: u32,
305 recovery_peers: [u32; MAX_RECOVERY_PEERS],
306 recent_slot: u64,
307 skip_init_mxe_part1: bool,
308 config: RpcSendTransactionConfig,
309) -> Signature {
310 if !skip_init_mxe_part1 {
312 let ix_pt1 = init_mxe_part1_ix(&payer.pubkey(), mxe_program, recovery_peers);
313 let tx_1 = arcium_program.request().instruction(ix_pt1);
314 let tx_1 = send_tx(vec![payer.clone()], tx_1, config);
315 tx_1.await;
316 }
317 let ix_pt2 = init_mxe_part2_ix(
318 &payer.pubkey(),
319 mxe_program,
320 cluster_offset,
321 keygen_comp_offset,
322 key_recovery_init_comp_offset,
323 mxe_authority,
324 recent_slot,
325 );
326 let ix_pt3 = init_mxe_recovery_stake_state_ix(&payer.pubkey(), mxe_program);
327 let tx_2 = arcium_program
328 .request()
329 .instruction(ix_pt2)
330 .instruction(ix_pt3);
331 let tx_2 = send_tx(vec![payer], tx_2, config);
332 tx_2.await
333}
334
335pub async fn register_account_in_lut(
336 arcium_program: &Program<Arc<Keypair>>,
337 signer: Arc<Keypair>,
338 mxe_program: &Pubkey,
339 account: &Pubkey,
340 config: RpcSendTransactionConfig,
341) -> Signature {
342 let mxe_account: MXEAccount = arcium_program
343 .account::<MXEAccount>(mxe_acc(mxe_program))
344 .await
345 .expect("failed to fetch MXE account when adding account to MXE LUT");
346 let lut_slot = mxe_account.lut_offset_slot;
347 let ix = register_account_in_lut_ix(&signer.pubkey(), mxe_program, lut_slot, *account);
348 let tx = arcium_program.request().instruction(ix);
349 send_tx(vec![signer], tx, config).await
350}
351
352#[allow(clippy::too_many_arguments)]
353pub async fn init_ephemeral_mxe(
354 arcium_program: &Program<Arc<Keypair>>,
355 payer: Arc<Keypair>,
356 mxe_program: &Pubkey,
357 mxe_authority: Option<Pubkey>,
358 keygen_comp_offset: u64,
359 cluster_offset: u32,
360 recent_slot: u64,
361 config: RpcSendTransactionConfig,
362) -> Signature {
363 let recovery_peers = [0u32; MAX_RECOVERY_PEERS];
364 let key_recovery_init_comp_offset = rand::thread_rng().next_u64();
365 let ix_pt1 = init_mxe_part1_ix(&payer.pubkey(), mxe_program, recovery_peers);
366 let ix_pt2 = init_mxe_part2_ix(
367 &payer.pubkey(),
368 mxe_program,
369 cluster_offset,
370 keygen_comp_offset,
371 key_recovery_init_comp_offset,
372 mxe_authority,
373 recent_slot,
374 );
375 let ix_pt3 = init_mxe_recovery_stake_state_ix(&payer.pubkey(), mxe_program);
376 let tx_1 = arcium_program.request().instruction(ix_pt1);
377 let tx_1 = send_tx(vec![payer.clone()], tx_1, config);
378 tx_1.await;
379 let tx_2 = arcium_program
380 .request()
381 .instruction(ix_pt2)
382 .instruction(ix_pt3);
383 let tx_2 = send_tx(vec![payer], tx_2, config);
384 tx_2.await
385}
386
387pub async fn finalize_mxe_keys(
388 arcium_program: &Program<Arc<Keypair>>,
389 payer: Arc<Keypair>,
390 mxe_program: &Pubkey,
391 cluster_offset: u32,
392 config: RpcSendTransactionConfig,
393) -> Signature {
394 let ix = finalize_mxe_keys_ix(&payer.pubkey(), mxe_program, cluster_offset);
395 let tx = arcium_program.request().instruction(ix);
396 send_tx(vec![payer], tx, config).await
397}
398
399pub async fn migrate_cluster_account(
403 arcium_program: &Program<Arc<Keypair>>,
404 payer: Arc<Keypair>,
405 cluster_offset: u32,
406 config: RpcSendTransactionConfig,
407) -> Signature {
408 let tx = arcium_program
409 .request()
410 .instruction(migrate_cluster_ix(&payer.pubkey(), cluster_offset));
411 send_tx(vec![payer], tx, config).await
412}
413
414pub async fn migrate_cluster_full(
418 arcium_program: &Program<Arc<Keypair>>,
419 payer: Arc<Keypair>,
420 cluster_offset: u32,
421 config: RpcSendTransactionConfig,
422) -> Signature {
423 let tx = arcium_program
424 .request()
425 .instruction(migrate_cluster_ix(&payer.pubkey(), cluster_offset))
426 .instruction(init_cluster_stake_state_ix(&payer.pubkey(), cluster_offset));
427 send_tx(vec![payer], tx, config).await
428}
429
430pub async fn migrate_mxe_account(
434 arcium_program: &Program<Arc<Keypair>>,
435 payer: Arc<Keypair>,
436 mxe_program: &Pubkey,
437 config: RpcSendTransactionConfig,
438) -> Signature {
439 let tx = arcium_program
440 .request()
441 .instruction(migrate_mxe_account_ix(&payer.pubkey(), mxe_program));
442 send_tx(vec![payer], tx, config).await
443}
444
445pub async fn migrate_mxe_account_full(
449 arcium_program: &Program<Arc<Keypair>>,
450 payer: Arc<Keypair>,
451 mxe_program: &Pubkey,
452 config: RpcSendTransactionConfig,
453) -> Signature {
454 let tx = arcium_program
455 .request()
456 .instruction(migrate_mxe_account_ix(&payer.pubkey(), mxe_program))
457 .instruction(init_mxe_recovery_stake_state_ix(
458 &payer.pubkey(),
459 mxe_program,
460 ));
461 send_tx(vec![payer], tx, config).await
462}
463
464pub async fn migrate_recovery_peer(
466 arcium_program: &Program<Arc<Keypair>>,
467 payer: Arc<Keypair>,
468 peer_offset: u32,
469 config: RpcSendTransactionConfig,
470) -> Signature {
471 let ix = migrate_recovery_peer_ix(&payer.pubkey(), peer_offset);
472 let tx = arcium_program.request().instruction(ix);
473 send_tx(vec![payer], tx, config).await
474}
475
476pub async fn migrate_arx_node(
481 arcium_program: &Program<Arc<Keypair>>,
482 authority: Arc<Keypair>,
483 node_offset: u32,
484 amount: u64,
485 fee_basis_points: u16,
486 config: RpcSendTransactionConfig,
487) -> Signature {
488 let tx = arcium_program
489 .request()
490 .instruction(add_primary_stake_to_legacy_acc_ix(
491 &authority.pubkey(),
492 amount,
493 fee_basis_points,
494 ))
495 .instruction(migrate_arx_node_ix(&authority.pubkey(), node_offset));
496 send_tx(vec![authority], tx, config).await
497}
498
499pub async fn migrate_recovery_peer_bind(
503 arcium_program: &Program<Arc<Keypair>>,
504 authority: Arc<Keypair>,
505 peer_offset: u32,
506 amount: u64,
507 fee_basis_points: u16,
508 config: RpcSendTransactionConfig,
509) -> Signature {
510 let tx = arcium_program
511 .request()
512 .instruction(migrate_recovery_peer_ix(&authority.pubkey(), peer_offset))
513 .instruction(add_primary_stake_to_legacy_acc_ix(
514 &authority.pubkey(),
515 amount,
516 fee_basis_points,
517 ))
518 .instruction(bind_recovery_peer_migration_ix(
519 &authority.pubkey(),
520 peer_offset,
521 ));
522 send_tx(vec![authority], tx, config).await
523}
524
525pub async fn set_migration(
526 arcium_program: &Program<Arc<Keypair>>,
527 signer: Arc<Keypair>,
528 mxe_program: &Pubkey,
529 config: RpcSendTransactionConfig,
530) -> Signature {
531 let ix = set_migration_ix(&signer.pubkey(), mxe_program);
532 let tx = arcium_program.request().instruction(ix);
533 send_tx(vec![signer.clone()], tx, config).await
534}
535
536pub async fn set_active(
537 arcium_program: &Program<Arc<Keypair>>,
538 signer: Arc<Keypair>,
539 mxe_program: &Pubkey,
540 config: RpcSendTransactionConfig,
541) -> Signature {
542 let ix = set_active_ix(&signer.pubkey(), mxe_program);
543 let tx = arcium_program.request().instruction(ix);
544 send_tx(vec![signer.clone()], tx, config).await
545}
546
547#[allow(clippy::too_many_arguments)]
548pub async fn init_key_recovery_execution(
549 arcium_program: &Program<Arc<Keypair>>,
550 payer: Arc<Keypair>,
551 original_mxe_program: &Pubkey,
552 backup_mxe_program: &Pubkey,
553 cluster_offset: u32,
554 key_recovery_finalize_offset: u64,
555 config: RpcSendTransactionConfig,
556) -> Signature {
557 let ix_pt1 = init_key_recovery_execution_part1_ix(
558 &payer.pubkey(),
559 &payer.pubkey(),
560 original_mxe_program,
561 backup_mxe_program,
562 );
563 let ix_pt2 = init_key_recovery_execution_part2_ix(
564 &payer.pubkey(),
565 original_mxe_program,
566 backup_mxe_program,
567 cluster_offset,
568 key_recovery_finalize_offset,
569 );
570 let tx = arcium_program
571 .request()
572 .instruction(ix_pt1)
573 .instruction(ix_pt2);
574 send_tx(vec![payer], tx, config).await
575}
576
577#[allow(clippy::too_many_arguments)]
578pub async fn queue_key_recovery_finalize(
579 arcium_program: &Program<Arc<Keypair>>,
580 signer: Arc<Keypair>,
581 original_mxe_program: &Pubkey,
582 backup_mxe_program: &Pubkey,
583 backup_cluster_offset: u32,
584 key_recovery_finalize_offset: u64,
585 lut_slot: u64,
586 config: RpcSendTransactionConfig,
587) -> Signature {
588 let ix = queue_key_recovery_finalize_ix(
589 &signer.pubkey(),
590 &signer.pubkey(),
591 original_mxe_program,
592 backup_mxe_program,
593 backup_cluster_offset,
594 key_recovery_finalize_offset,
595 lut_slot,
596 );
597 let tx = arcium_program.request().instruction(ix);
598 send_tx(vec![signer], tx, config).await
599}
600
601pub async fn close_successful_key_recovery(
602 arcium_program: &Program<Arc<Keypair>>,
603 signer: Arc<Keypair>,
604 original_mxe_program: &Pubkey,
605 backup_mxe_program: &Pubkey,
606 backup_cluster_offset: u32,
607 key_recovery_finalize_offset: u64,
608 config: RpcSendTransactionConfig,
609) -> Signature {
610 try_close_successful_key_recovery(
611 arcium_program,
612 signer,
613 original_mxe_program,
614 backup_mxe_program,
615 backup_cluster_offset,
616 key_recovery_finalize_offset,
617 config,
618 )
619 .await
620 .unwrap()
621}
622
623pub async fn try_close_successful_key_recovery(
629 arcium_program: &Program<Arc<Keypair>>,
630 signer: Arc<Keypair>,
631 original_mxe_program: &Pubkey,
632 backup_mxe_program: &Pubkey,
633 backup_cluster_offset: u32,
634 key_recovery_finalize_offset: u64,
635 config: RpcSendTransactionConfig,
636) -> Result<Signature, anchor_client::ClientError> {
637 let ix = close_successful_key_recovery_ix(
638 &signer.pubkey(),
639 original_mxe_program,
640 backup_mxe_program,
641 backup_cluster_offset,
642 key_recovery_finalize_offset,
643 );
644 let tx = arcium_program.request().instruction(ix);
645 try_send_tx(vec![signer], tx, config).await
646}
647
648pub async fn close_aborted_key_recovery(
649 arcium_program: &Program<Arc<Keypair>>,
650 signer: Arc<Keypair>,
651 original_mxe_program: &Pubkey,
652 backup_mxe_program: &Pubkey,
653 backup_cluster_offset: u32,
654 key_recovery_finalize_offset: u64,
655 config: RpcSendTransactionConfig,
656) -> Signature {
657 let ix = close_aborted_key_recovery_ix(
658 &signer.pubkey(),
659 original_mxe_program,
660 backup_mxe_program,
661 backup_cluster_offset,
662 key_recovery_finalize_offset,
663 );
664 let tx = arcium_program.request().instruction(ix);
665 send_tx(vec![signer], tx, config).await
666}
667
668pub async fn requeue_mxe_keygen(
669 arcium_program: &Program<Arc<Keypair>>,
670 payer: Arc<Keypair>,
671 mxe_program: &Pubkey,
672 keygen_offset: u64,
673 cluster_offset: u32,
674 config: RpcSendTransactionConfig,
675) -> Signature {
676 let ix = requeue_mxe_keygen_ix(&payer.pubkey(), mxe_program, cluster_offset, keygen_offset);
677 let tx = arcium_program.request().instruction(ix);
678 send_tx(vec![payer], tx, config).await
679}
680
681pub async fn queue_key_recovery_init(
682 arcium_program: &Program<Arc<Keypair>>,
683 payer: Arc<Keypair>,
684 mxe_program: &Pubkey,
685 cluster_offset: u32,
686 key_recovery_init_offset: u64,
687 config: RpcSendTransactionConfig,
688) -> Signature {
689 let ix = queue_key_recovery_init_ix(
690 &payer.pubkey(),
691 mxe_program,
692 cluster_offset,
693 key_recovery_init_offset,
694 );
695 let tx = arcium_program.request().instruction(ix);
696 send_tx(vec![payer], tx, config).await
697}
698
699pub async fn claim_computation_rent(
700 arcium_program: &Program<Arc<Keypair>>,
701 signer: Arc<Keypair>,
702 computation_offset: u64,
703 cluster_offset: u32,
704 config: RpcSendTransactionConfig,
705) -> Signature {
706 let ix = claim_computation_rent_ix(&signer.pubkey(), computation_offset, cluster_offset);
707 let tx = arcium_program.request().instruction(ix);
708 send_tx(vec![signer], tx, config).await
709}
710
711pub async fn reclaim_expired_computation_fee(
712 arcium_program: &Program<Arc<Keypair>>,
713 signer: Arc<Keypair>,
714 mxe_program: &Pubkey,
715 computation_offset: u64,
716 cluster_offset: u32,
717 config: RpcSendTransactionConfig,
718) -> Signature {
719 let ix = reclaim_expired_computation_fee_ix(
720 &signer.pubkey(),
721 mxe_program,
722 cluster_offset,
723 computation_offset,
724 );
725 let tx = arcium_program.request().instruction(ix);
726 send_tx(vec![signer], tx, config).await
727}
728
729#[allow(clippy::too_many_arguments)]
730pub async fn init_computation_definition(
731 arcium_program: &Program<Arc<Keypair>>,
732 payer: Arc<Keypair>,
733 computation_def_offset: u32,
734 mxe_program: &Pubkey,
735 circuit_len: u32,
736 params: Vec<Parameter>,
737 outputs: Vec<Output>,
738 circuit_source_override: Option<CircuitSource>,
739 cu_amount: u64,
740 lut_slot: u64,
741 config: RpcSendTransactionConfig,
742) -> Signature {
743 let ix = init_computation_definition_ix(
744 &payer.pubkey(),
745 computation_def_offset,
746 mxe_program,
747 circuit_len,
748 params,
749 outputs,
750 cu_amount,
751 circuit_source_override,
752 lut_slot,
753 );
754 let tx = arcium_program.request().instruction(ix);
755 send_tx(vec![payer], tx, config).await
756}
757
758pub async fn increase_mempool_size(
759 arcium_program: &Program<Arc<Keypair>>,
760 signer: Arc<Keypair>,
761 cluster_offset: u32,
762 config: RpcSendTransactionConfig,
763) -> Signature {
764 let ix = increase_mempool_size_ix(&signer.pubkey(), cluster_offset);
765 let tx = arcium_program.request().instruction(ix);
766 send_tx(vec![signer], tx, config).await
767}
768
769pub async fn activate_cluster(
770 arcium_program: &Program<Arc<Keypair>>,
771 signer: Arc<Keypair>,
772 cluster_offset: u32,
773 config: RpcSendTransactionConfig,
774) -> Signature {
775 let ix = activate_cluster_ix(&signer.pubkey(), cluster_offset);
776 let tx = arcium_program.request().instruction(ix);
777 send_tx(vec![signer], tx, config).await
778}
779
780pub async fn set_cluster_td_info(
781 arcium_program: &Program<Arc<Keypair>>,
782 signer: Arc<Keypair>,
783 cluster_offset: u32,
784 td_info: Option<NodeMetadata>,
785 config: RpcSendTransactionConfig,
786) -> Signature {
787 let ix = set_cluster_td_info_ix(&signer.pubkey(), cluster_offset, td_info);
788 let tx = arcium_program.request().instruction(ix);
789 send_tx(vec![signer], tx, config).await
790}
791
792pub async fn propose_join_cluster(
793 arcium_program: &Program<Arc<Keypair>>,
794 cluster_auth_signer: Arc<Keypair>,
795 cluster_offset: u32,
796 node_offset: u32,
797
798 config: RpcSendTransactionConfig,
799) -> Signature {
800 let node = arcium_program
804 .account::<ArxNode>(arx_acc(node_offset))
805 .await
806 .expect("failed to fetch ArxNode for propose_join_cluster");
807 let ix = propose_join_cluster_ix(
808 &cluster_auth_signer.pubkey(),
809 cluster_offset,
810 node_offset,
811 &node.primary_staking_account,
812 );
813 let tx = arcium_program.request().instruction(ix);
814 send_tx(vec![cluster_auth_signer], tx, config).await
815}
816
817pub async fn join_cluster(
818 arcium_program: &Program<Arc<Keypair>>,
819 node_auth_signer: Arc<Keypair>,
820 cluster_offset: u32,
821 node_offset: u32,
822 join: bool,
823 config: RpcSendTransactionConfig,
824) -> Signature {
825 let ix = join_cluster_ix(
826 &node_auth_signer.pubkey(),
827 cluster_offset,
828 node_offset,
829 join,
830 );
831 let tx = arcium_program.request().instruction(ix);
832 send_tx(vec![node_auth_signer], tx, config).await
833}
834
835#[allow(clippy::too_many_arguments)]
836pub async fn queue_computation(
837 arcium_program: &Program<Arc<Keypair>>,
838 payer: Arc<Keypair>,
839 mxe_program: &Pubkey,
840 computation_offset: u64,
841 comp_def_offset: u32,
842 args: ArgumentList,
843 cu_price_micro: u64,
844 callback_cu_limit: u32,
845 callback_instructions: Vec<CallbackInstruction>,
846 callback_transactions_required: u8,
847 config: RpcSendTransactionConfig,
848) -> Result<Signature, anchor_lang::error::Error> {
849 let mxe_raw_data = arcium_program
850 .internal_rpc()
851 .get_account_data(&mxe_acc(mxe_program))
852 .await
853 .expect("Failed to fetch MXE");
854 let mxe_data = MXEAccount::try_deserialize(&mut mxe_raw_data.as_slice())
855 .expect("Failed to deserialize MXE");
856
857 let ix = queue_computation_ix(
858 &payer.pubkey(),
859 mxe_program,
860 computation_offset,
861 comp_def_offset,
862 args,
863 cu_price_micro,
864 callback_cu_limit,
865 mxe_data,
866 callback_instructions,
867 callback_transactions_required,
868 )?;
869 let tx = arcium_program.request().instruction(ix);
870 Ok(send_tx(vec![payer], tx, config).await)
871}
872
873pub async fn propose_fee(
874 arcium_program: &Program<Arc<Keypair>>,
875 node_auth_signer: Arc<Keypair>,
876 cluster_offset: u32,
877 node_offset: u32,
878 proposed_fee: u64,
879 config: RpcSendTransactionConfig,
880) -> Signature {
881 let ix = propose_fee_ix(
882 node_auth_signer.pubkey(),
883 cluster_offset,
884 node_offset,
885 proposed_fee,
886 );
887 let tx = arcium_program.request().instruction(ix);
888 send_tx(vec![node_auth_signer], tx, config).await
889}
890
891pub async fn vote_fee(
892 arcium_program: &Program<Arc<Keypair>>,
893 node_auth_signer: Arc<Keypair>,
894 cluster_offset: u32,
895 node_offset: u32,
896 proposed_fee: u64,
897 config: RpcSendTransactionConfig,
898) -> Signature {
899 let ix = vote_fee_ix(
900 node_auth_signer.pubkey(),
901 cluster_offset,
902 node_offset,
903 proposed_fee,
904 );
905 let tx = arcium_program.request().instruction(ix);
906 send_tx(vec![node_auth_signer], tx, config).await
907}
908
909pub async fn claim_node_fees(
914 arcium_program: &Program<Arc<Keypair>>,
915 staking_pool_signer: Arc<Keypair>,
916 primary_stake_account: Pubkey,
917 cluster_offset: u32,
918 node_offset: u32,
919 config: RpcSendTransactionConfig,
920) -> Signature {
921 let ix = claim_node_fees_ix(
922 &staking_pool_signer.pubkey(),
923 &primary_stake_account,
924 cluster_offset,
925 node_offset,
926 );
927 let tx = arcium_program.request().instruction(ix);
928 send_tx(vec![staking_pool_signer], tx, config).await
929}
930
931pub async fn submit_aggregated_bls_pubkey(
932 arcium_program: &Program<Arc<Keypair>>,
933 node_authority: Arc<Keypair>,
934 cluster_offset: u32,
935 node_offset: u32,
936 aggregated_bls_pubkey: BN254G2BLSPublicKey,
937 config: RpcSendTransactionConfig,
938) -> Signature {
939 let ix = submit_aggregated_bls_pubkey_ix(
940 &node_authority.pubkey(),
941 cluster_offset,
942 node_offset,
943 aggregated_bls_pubkey,
944 );
945 let tx = arcium_program.request().instruction(ix);
946 send_tx(vec![node_authority], tx, config).await
947}
948
949#[allow(clippy::too_many_arguments)]
950pub async fn claim_failure(
951 arcium_program: &Program<Arc<Keypair>>,
952 signer: Arc<Keypair>,
953 mxe_program: &Pubkey,
954 cluster_offset: u32,
955 comp_def_offset: u32,
956 computation_offset: u64,
957 node_offset: u32,
958 callback_data: Vec<u8>,
959 failure_claim_offset: u32,
960 config: RpcSendTransactionConfig,
961) -> ClaimResult<Vec<Signature>> {
962 let mut txs = build_claim_failure_txs(
963 arcium_program,
964 &signer.pubkey(),
965 mxe_program,
966 cluster_offset,
967 comp_def_offset,
968 computation_offset,
969 node_offset,
970 callback_data,
971 failure_claim_offset,
972 )?
973 .into_iter();
974 let txs_len = txs.len();
975 let mut signatures = Vec::with_capacity(txs_len);
976 if let Some(first) = txs.next() {
977 signatures.push(send_tx(vec![signer.clone()], first, config).await);
978 }
979 for tx in txs.by_ref().take(txs_len.saturating_sub(2)) {
981 signatures.push(send_tx(vec![signer.clone()], tx, config).await);
982 }
983 if let Some(finalize_tx) = txs.next() {
984 signatures.push(send_tx(vec![signer], finalize_tx, config).await);
985 }
986 Ok(signatures)
987}
988
989pub async fn reclaim_failure_rent_idempotent(
996 arcium_program: &Program<Arc<Keypair>>,
997 signer: Arc<Keypair>,
998 recipient: &Pubkey,
999 mxe_program: &Pubkey,
1000 computation_offset: u64,
1001 config: RpcSendTransactionConfig,
1002) -> Result<Signature, anchor_client::ClientError> {
1003 let ix = reclaim_failure_rent_idempotent_ix(
1004 &signer.pubkey(),
1005 recipient,
1006 mxe_program,
1007 computation_offset,
1008 );
1009 arcium_program
1010 .request()
1011 .instruction(ix)
1012 .signer(signer)
1013 .send_with_spinner_and_config(config)
1014 .await
1015}
1016
1017pub async fn reclaim_failure_rent_idempotent_batch(
1026 arcium_program: &Program<Arc<Keypair>>,
1027 signer: Arc<Keypair>,
1028 recipient: &Pubkey,
1029 claims: &[(Pubkey, u64)],
1030 config: RpcSendTransactionConfig,
1031) -> Result<Signature, anchor_client::ClientError> {
1032 let mut request = arcium_program.request();
1033 for (mxe_program, computation_offset) in claims {
1034 request = request.instruction(reclaim_failure_rent_idempotent_ix(
1035 &signer.pubkey(),
1036 recipient,
1037 mxe_program,
1038 *computation_offset,
1039 ));
1040 }
1041 request
1042 .signer(signer)
1043 .send_with_spinner_and_config(config)
1044 .await
1045}
1046
1047pub async fn submit_reclaim_failure_rent_batch(
1054 rpc: &AsyncRpcClient,
1055 signer: &Keypair,
1056 recipient: &Pubkey,
1057 claims: &[(Pubkey, u64)],
1058 recent_blockhash: solana_program::hash::Hash,
1059) -> Result<Signature, solana_rpc_client_api::client_error::Error> {
1060 let ixs: Vec<_> = claims
1061 .iter()
1062 .map(|(mxe_program, computation_offset)| {
1063 reclaim_failure_rent_idempotent_ix(
1064 &signer.pubkey(),
1065 recipient,
1066 mxe_program,
1067 *computation_offset,
1068 )
1069 })
1070 .collect();
1071 let tx = Transaction::new_signed_with_payer(
1072 &ixs,
1073 Some(&signer.pubkey()),
1074 &[signer],
1075 recent_blockhash,
1076 );
1077 rpc.send_transaction_with_config(
1078 &tx,
1079 RpcSendTransactionConfig {
1080 skip_preflight: true,
1081 ..Default::default()
1082 },
1083 )
1084 .await
1085}
1086
1087#[allow(clippy::too_many_arguments, clippy::type_complexity)]
1088fn build_claim_failure_txs<'a>(
1089 arcium_program: &'a Program<Arc<Keypair>>,
1090 signer_pubkey: &Pubkey,
1091 mxe_program_id: &Pubkey,
1092 cluster_offset: u32,
1093 comp_def_offset: u32,
1094 computation_offset: u64,
1095 node_offset: u32,
1096 callback_data: Vec<u8>,
1097 failure_claim_offset: u32,
1098) -> ClaimResult<Vec<RequestBuilder<'a, Arc<Keypair>, Arc<dyn ThreadSafeSigner>>>> {
1099 if callback_data.len() <= MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE {
1100 let init_ix = claim_failure_init_ix(
1101 signer_pubkey,
1102 mxe_program_id,
1103 cluster_offset,
1104 comp_def_offset,
1105 computation_offset,
1106 node_offset,
1107 callback_data.len() as u32,
1108 );
1109 let append_ix = claim_failure_append_ix(
1110 signer_pubkey,
1111 mxe_program_id,
1112 computation_offset,
1113 callback_data,
1114 failure_claim_offset,
1115 );
1116 let finalize_ix = claim_failure_finalize_ix(
1117 signer_pubkey,
1118 mxe_program_id,
1119 cluster_offset,
1120 computation_offset,
1121 node_offset,
1122 );
1123 let tx = arcium_program
1124 .request()
1125 .instruction(init_ix)
1126 .instruction(append_ix)
1127 .instruction(finalize_ix);
1128 return Ok(vec![tx]);
1129 }
1130 Err(ClaimFailureError::CallbackDataTooLarge {
1131 size: callback_data.len(),
1132 limit: MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE,
1133 })
1134}
1135
1136pub async fn deactivate_computation_definition(
1137 arcium_program: &Program<Arc<Keypair>>,
1138 payer: Arc<Keypair>,
1139 comp_offset: u32,
1140 mxe_program: &Pubkey,
1141 config: RpcSendTransactionConfig,
1142) -> Signature {
1143 let ix = crate::instruction::deactivate_computation_definition_ix(
1144 &payer.pubkey(),
1145 comp_offset,
1146 mxe_program,
1147 );
1148 let tx = arcium_program.request().instruction(ix);
1149 send_tx(vec![payer], tx, config).await
1150}
1151
1152pub async fn close_computation_definition(
1153 arcium_program: &Program<Arc<Keypair>>,
1154 payer: Arc<Keypair>,
1155 comp_offset: u32,
1156 mxe_program: &Pubkey,
1157 cluster_offset: u32,
1158 config: RpcSendTransactionConfig,
1159) -> Signature {
1160 let ix = crate::instruction::close_computation_definition_ix(
1161 &payer.pubkey(),
1162 comp_offset,
1163 mxe_program,
1164 cluster_offset,
1165 );
1166 let tx = arcium_program.request().instruction(ix);
1167 send_tx(vec![payer], tx, config).await
1168}
1169
1170pub async fn close_computation_definition_buffers(
1171 arcium_program: &Program<Arc<Keypair>>,
1172 payer: Arc<Keypair>,
1173 comp_offset: u32,
1174 mxe_program: &Pubkey,
1175 raw_circuit_index: u8,
1176 config: RpcSendTransactionConfig,
1177) -> Signature {
1178 let ix = crate::instruction::close_computation_definition_buffers_ix(
1179 &payer.pubkey(),
1180 comp_offset,
1181 mxe_program,
1182 raw_circuit_index,
1183 );
1184 let tx = arcium_program.request().instruction(ix);
1185 send_tx(vec![payer], tx, config).await
1186}
1187
1188#[allow(clippy::too_many_arguments)]
1189pub async fn close_mxe(
1190 arcium_program: &Program<Arc<Keypair>>,
1191 payer: Arc<Keypair>,
1192 mxe_program: &Pubkey,
1193 cluster_offset: u32,
1194 keygen_offset: u64,
1195 key_recovery_init_offset: u64,
1196 kr_init_comp_def_exists: bool,
1197 config: RpcSendTransactionConfig,
1198) -> Signature {
1199 let ix = crate::instruction::close_mxe_ix(
1200 &payer.pubkey(),
1201 mxe_program,
1202 cluster_offset,
1203 keygen_offset,
1204 key_recovery_init_offset,
1205 kr_init_comp_def_exists,
1206 );
1207 let tx = arcium_program.request().instruction(ix);
1208 send_tx(vec![payer], tx, config).await
1209}
1210
1211pub fn arcium_program_client(
1213 cluster: SolanaCluster,
1214 signer: Arc<Keypair>,
1215 rpc: AsyncRpcClient,
1216) -> Program<Arc<Keypair>> {
1217 let client = Client::new(cluster, signer);
1218 client
1219 .program(ARCIUM_PROG_ID, rpc)
1220 .unwrap_or_else(|err| panic!("Failed to create program: {}", err))
1221}
1222
1223async fn send_tx<C: Deref<Target = impl Signer> + Clone>(
1224 signers: Vec<Arc<Keypair>>,
1225 ix: RequestBuilder<'_, C, Arc<dyn ThreadSafeSigner>>,
1226 config: RpcSendTransactionConfig,
1227) -> Signature {
1228 try_send_tx(signers, ix, config)
1229 .await
1230 .unwrap()
1232}
1233
1234async fn try_send_tx<C: Deref<Target = impl Signer> + Clone>(
1235 signers: Vec<Arc<Keypair>>,
1236 ix: RequestBuilder<'_, C, Arc<dyn ThreadSafeSigner>>,
1237 config: RpcSendTransactionConfig,
1238) -> Result<Signature, anchor_client::ClientError> {
1239 let signed = signers.into_iter().fold(ix, |ix, signer| ix.signer(signer));
1240 signed.send_with_spinner_and_config(config).await
1241}
1242
1243async fn current_unix_timestamp(client: &AsyncRpcClient) -> u64 {
1245 let latest_slot = client
1246 .get_slot()
1247 .await
1248 .unwrap_or_else(|err| panic!("Failed to fetch slot: {}", err));
1249 client
1250 .get_block_time(latest_slot)
1251 .await
1252 .unwrap_or_else(|err| panic!("Failed to fetch block time: {}", err))
1253 .try_into()
1254 .unwrap_or_else(|err| panic!("Failed to convert block time to u64: {}", err))
1255}
1256
1257pub mod staking {
1258 use super::*;
1259 use crate::{
1260 idl::arcium_staking::types::RewardClaim,
1261 instruction::staking::{
1262 activate_primary_stake_acc_ix,
1263 claim_primary_stake_rewards_ix,
1264 close_delegated_stake_ix,
1265 deactivate_primary_stake_acc_ix,
1266 delegate_stake_ix,
1267 finalize_epoch_rewards_ix,
1268 init_delegated_stake_acc_ix,
1269 init_delegated_stake_master_acc_ix,
1270 init_primary_stake_acc_ix,
1271 merge_delegated_stake_account_ix,
1272 modify_primary_stake_ix,
1273 split_delegated_stake_account_ix,
1274 undelegate_stake_ix,
1275 REWARDS_MT_HEIGHT,
1276 },
1277 };
1278
1279 const FINALIZE_COMPUTE_UNIT_LIMIT: u32 = 1_400_000;
1286
1287 fn set_compute_unit_limit_ix(
1290 limit: u32,
1291 ) -> anchor_lang::solana_program::instruction::Instruction {
1292 let mut data = Vec::with_capacity(5);
1293 data.push(2u8);
1294 data.extend_from_slice(&limit.to_le_bytes());
1295 anchor_lang::solana_program::instruction::Instruction {
1296 program_id: Pubkey::from_str_const("ComputeBudget111111111111111111111111111111"),
1297 accounts: Vec::new(),
1298 data,
1299 }
1300 }
1301
1302 pub async fn init_primary_stake_acc(
1303 arcium_program: &Program<Arc<Keypair>>,
1304 signer: Arc<Keypair>,
1305 amount: u64,
1306 fee_basis_points: u16,
1307 config: RpcSendTransactionConfig,
1308 ) -> Signature {
1309 let ix = init_primary_stake_acc_ix(&signer.pubkey(), amount, fee_basis_points);
1310 let tx = arcium_program.request().instruction(ix);
1311 send_tx(vec![signer], tx, config).await
1312 }
1313
1314 pub async fn activate_primary_stake_acc(
1315 arcium_program: &Program<Arc<Keypair>>,
1316 signer: Arc<Keypair>,
1317 lockup_epochs: u64,
1318
1319 config: RpcSendTransactionConfig,
1320 ) -> Signature {
1321 let ix = activate_primary_stake_acc_ix(&signer.pubkey(), lockup_epochs);
1322 let tx = arcium_program.request().instruction(ix);
1323 send_tx(vec![signer], tx, config).await
1324 }
1325
1326 pub async fn deactivate_primary_stake_acc(
1327 arcium_program: &Program<Arc<Keypair>>,
1328 signer: Arc<Keypair>,
1329 arx_node_offset: Option<u32>,
1330
1331 config: RpcSendTransactionConfig,
1332 ) -> Signature {
1333 let ix = deactivate_primary_stake_acc_ix(&signer.pubkey(), arx_node_offset);
1334 let tx = arcium_program.request().instruction(ix);
1335 send_tx(vec![signer], tx, config).await
1336 }
1337
1338 pub async fn modify_primary_stake_acc(
1343 arcium_program: &Program<Arc<Keypair>>,
1344 signer: Arc<Keypair>,
1345 diff: i64,
1346 arx_node: Option<Pubkey>,
1347 mempool: Option<Pubkey>,
1348 config: RpcSendTransactionConfig,
1349 ) -> Signature {
1350 let ix = modify_primary_stake_ix(&signer.pubkey(), diff, arx_node, mempool);
1351 let tx = arcium_program.request().instruction(ix);
1352 send_tx(vec![signer], tx, config).await
1353 }
1354
1355 pub async fn finalize_epoch_rewards(
1364 arcium_program: &Program<Arc<Keypair>>,
1365 signer: Arc<Keypair>,
1366 primary_stake_owner: &Pubkey,
1367 cluster_offset: u32,
1368 node_offset: u32,
1369 config: RpcSendTransactionConfig,
1370 ) -> Signature {
1371 let ix = finalize_epoch_rewards_ix(
1372 &signer.pubkey(),
1373 primary_stake_owner,
1374 cluster_offset,
1375 node_offset,
1376 );
1377 let tx = arcium_program
1380 .request()
1381 .instruction(set_compute_unit_limit_ix(FINALIZE_COMPUTE_UNIT_LIMIT))
1382 .instruction(ix);
1383 send_tx(vec![signer], tx, config).await
1384 }
1385
1386 pub async fn claim_primary_stake_rewards(
1392 arcium_program: &Program<Arc<Keypair>>,
1393 signer: Arc<Keypair>,
1394 destination: &Pubkey,
1395 leaf_index: u16,
1396 opening: [[u8; 32]; REWARDS_MT_HEIGHT],
1397 claim: RewardClaim,
1398 config: RpcSendTransactionConfig,
1399 ) -> Signature {
1400 let ix = claim_primary_stake_rewards_ix(
1401 &signer.pubkey(),
1402 destination,
1403 leaf_index,
1404 opening,
1405 claim,
1406 );
1407 let tx = arcium_program.request().instruction(ix);
1408 send_tx(vec![signer], tx, config).await
1409 }
1410
1411 pub async fn init_delegated_stake_master_acc(
1412 arcium_program: &Program<Arc<Keypair>>,
1413 signer: Arc<Keypair>,
1414 owner: &Pubkey,
1415 config: RpcSendTransactionConfig,
1416 ) -> Signature {
1417 let ix = init_delegated_stake_master_acc_ix(&signer.pubkey(), owner);
1418 let tx = arcium_program.request().instruction(ix);
1419 send_tx(vec![signer], tx, config).await
1420 }
1421
1422 pub async fn init_delegated_stake_acc(
1423 arcium_program: &Program<Arc<Keypair>>,
1424 signer: Arc<Keypair>,
1425 stake_offset: u128,
1426 amount: u64,
1427 config: RpcSendTransactionConfig,
1428 ) -> Signature {
1429 let ix = init_delegated_stake_acc_ix(&signer.pubkey(), stake_offset, amount);
1430 let tx = arcium_program.request().instruction(ix);
1431 send_tx(vec![signer], tx, config).await
1432 }
1433
1434 pub async fn delegate_stake(
1435 arcium_program: &Program<Arc<Keypair>>,
1436 signer: Arc<Keypair>,
1437 stake_offset: u128,
1438 primary_stake_owner: &Pubkey,
1439 lockup_epochs: u64,
1440 config: RpcSendTransactionConfig,
1441 ) -> Signature {
1442 let ix = delegate_stake_ix(
1443 &signer.pubkey(),
1444 stake_offset,
1445 primary_stake_owner,
1446 lockup_epochs,
1447 );
1448 let tx = arcium_program.request().instruction(ix);
1449 send_tx(vec![signer], tx, config).await
1450 }
1451
1452 pub async fn undelegate_stake(
1453 arcium_program: &Program<Arc<Keypair>>,
1454 signer: Arc<Keypair>,
1455 stake_offset: u128,
1456 primary_stake_owner: &Pubkey,
1457 config: RpcSendTransactionConfig,
1458 ) -> Signature {
1459 let ix = undelegate_stake_ix(&signer.pubkey(), stake_offset, primary_stake_owner);
1460 let tx = arcium_program.request().instruction(ix);
1461 send_tx(vec![signer], tx, config).await
1462 }
1463
1464 #[allow(clippy::too_many_arguments)]
1465 pub async fn split_delegated_stake_account(
1466 arcium_program: &Program<Arc<Keypair>>,
1467 primary_stake_owner_target: &Pubkey,
1468 delegation_authority: Arc<Keypair>,
1469 withdrawal_authority: Arc<Keypair>,
1470 stake_offset: u128,
1471 stake_offset_new: u128,
1472 new_acc_balance: u64,
1473 config: RpcSendTransactionConfig,
1474 ) -> Signature {
1475 let ix = split_delegated_stake_account_ix(
1476 primary_stake_owner_target,
1477 &delegation_authority.pubkey(),
1478 &withdrawal_authority.pubkey(),
1479 stake_offset,
1480 stake_offset_new,
1481 new_acc_balance,
1482 );
1483 let tx = arcium_program.request().instruction(ix);
1484 send_tx(vec![delegation_authority, withdrawal_authority], tx, config).await
1485 }
1486
1487 pub async fn merge_delegated_stake_account(
1488 arcium_program: &Program<Arc<Keypair>>,
1489 primary_stake_owner_target: &Pubkey,
1490 delegation_authority: Arc<Keypair>,
1491 withdrawal_authority: Arc<Keypair>,
1492 stake_offset_keep: u128,
1493 stake_offset_close: u128,
1494 config: RpcSendTransactionConfig,
1495 ) -> Signature {
1496 let ix = merge_delegated_stake_account_ix(
1497 primary_stake_owner_target,
1498 &delegation_authority.pubkey(),
1499 &withdrawal_authority.pubkey(),
1500 stake_offset_keep,
1501 stake_offset_close,
1502 );
1503 let tx = arcium_program.request().instruction(ix);
1504 send_tx(vec![delegation_authority, withdrawal_authority], tx, config).await
1505 }
1506
1507 pub async fn close_delegated_stake(
1508 arcium_program: &Program<Arc<Keypair>>,
1509 signer: Arc<Keypair>,
1510 delegation_owner: &Pubkey,
1511 stake_offset: u128,
1512 primary_stake_owner: Option<&Pubkey>,
1513 config: RpcSendTransactionConfig,
1514 ) -> Signature {
1515 let ix = close_delegated_stake_ix(
1516 &signer.pubkey(),
1517 delegation_owner,
1518 stake_offset,
1519 primary_stake_owner,
1520 );
1521 let tx = arcium_program.request().instruction(ix);
1522 send_tx(vec![signer], tx, config).await
1523 }
1524}
1525
1526#[cfg(feature = "permissioned-mainnet")]
1527pub mod permissioned_mainnet {
1528 use crate::{
1529 instruction::permissioned_mainnet::{
1530 add_permissioned_recovery_peers_ix,
1531 close_permissioned_recovery_peers_acc_ix,
1532 init_permissioned_recovery_peers_acc_ix,
1533 remove_permissioned_recovery_peers_ix,
1534 },
1535 transactions::send_tx,
1536 };
1537 use anchor_client::Program;
1538 use anchor_lang::prelude::Pubkey;
1539 use solana_keypair::Keypair;
1540 use solana_rpc_client_api::config::RpcSendTransactionConfig;
1541 use solana_signature::Signature;
1542 use solana_signer::Signer;
1543 use std::sync::Arc;
1544
1545 pub async fn init_permissioned_recovery_peers_acc(
1546 arcium_program: &Program<Arc<Keypair>>,
1547 signer: Arc<Keypair>,
1548 config: RpcSendTransactionConfig,
1549 ) -> Signature {
1550 let ix = init_permissioned_recovery_peers_acc_ix(&signer.pubkey());
1551 let tx = arcium_program.request().instruction(ix);
1552 send_tx(vec![signer], tx, config).await
1553 }
1554
1555 pub async fn close_permissioned_recovery_peers_acc(
1556 arcium_program: &Program<Arc<Keypair>>,
1557 signer: Arc<Keypair>,
1558 config: RpcSendTransactionConfig,
1559 ) -> Signature {
1560 let ix = close_permissioned_recovery_peers_acc_ix(&signer.pubkey());
1561 let tx = arcium_program.request().instruction(ix);
1562 send_tx(vec![signer], tx, config).await
1563 }
1564
1565 pub async fn add_permissioned_recovery_peers(
1566 arcium_program: &Program<Arc<Keypair>>,
1567 signer: Arc<Keypair>,
1568 peers: Vec<Pubkey>,
1569 config: RpcSendTransactionConfig,
1570 ) -> Signature {
1571 let ix = add_permissioned_recovery_peers_ix(&signer.pubkey(), peers);
1572 let tx = arcium_program.request().instruction(ix);
1573 send_tx(vec![signer], tx, config).await
1574 }
1575
1576 pub async fn remove_permissioned_recovery_peers(
1577 arcium_program: &Program<Arc<Keypair>>,
1578 signer: Arc<Keypair>,
1579 peers: Vec<Pubkey>,
1580 config: RpcSendTransactionConfig,
1581 ) -> Signature {
1582 let ix = remove_permissioned_recovery_peers_ix(&signer.pubkey(), peers);
1583 let tx = arcium_program.request().instruction(ix);
1584 send_tx(vec![signer], tx, config).await
1585 }
1586}
1587
1588#[cfg(test)]
1589mod tests {
1590 use super::*;
1591 use crate::{
1592 idl::{arcium::ID as ARCIUM_ID, arcium_staking::ID as ARCIUM_STAKING_ID},
1593 pda::staking::cluster_stake_state_acc,
1594 };
1595 use solana_keypair::Keypair;
1596 use solana_program::hash::Hash;
1597
1598 #[test]
1599 fn init_cluster_instructions_include_cluster_stake_state() {
1600 let payer = Pubkey::new_unique();
1601 let cluster_authority = Pubkey::new_unique();
1602 let cluster_offset = 42;
1603
1604 let [cluster_ix, stake_state_ix] = init_cluster_instructions(
1605 &payer,
1606 cluster_authority,
1607 cluster_offset,
1608 3,
1609 DEFAULT_CU_PRICE,
1610 MempoolSize::Tiny,
1611 None,
1612 );
1613
1614 assert_eq!(cluster_ix.program_id, ARCIUM_ID);
1615 assert_eq!(stake_state_ix.program_id, ARCIUM_STAKING_ID);
1616 assert_eq!(stake_state_ix.accounts[0].pubkey, payer);
1617 assert_eq!(
1618 stake_state_ix.accounts[1].pubkey,
1619 cluster_stake_state_acc(cluster_offset)
1620 );
1621 }
1622
1623 #[test]
1624 fn test_build_claim_failure_txs_single_tx_at_max_size() {
1625 let payer = Arc::new(Keypair::new());
1626 let rpc = AsyncRpcClient::new("http://localhost:8899".to_string());
1627 let arcium_program = arcium_program_client(SolanaCluster::Localnet, payer.clone(), rpc);
1628
1629 let signer_pubkey = payer.pubkey();
1630 let mxe_program_id = Pubkey::new_unique();
1631 let callback_data = vec![0u8; MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE];
1632
1633 let txs = build_claim_failure_txs(
1634 &arcium_program,
1635 &signer_pubkey,
1636 &mxe_program_id,
1637 0,
1638 0,
1639 0,
1640 0,
1641 callback_data,
1642 0,
1643 )
1644 .expect("Should succeed for data within single-tx limit");
1645
1646 assert_eq!(
1647 txs.len(),
1648 1,
1649 "Expected exactly 1 transaction for data at MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE"
1650 );
1651 let mut tx = txs[0].transaction();
1652 tx.sign(&[payer], Hash::new_unique());
1653 let serialized = bincode::serialize(&tx).unwrap();
1654 assert_eq!(
1655 serialized.len(),
1656 1232,
1657 "Expected max size of serialized transaction to be 1232 bytes but got {}",
1658 serialized.len()
1659 );
1660 }
1661
1662 #[test]
1663 fn test_build_claim_failure_txs_oversized_returns_error() {
1664 let payer = Arc::new(Keypair::new());
1665 let rpc = AsyncRpcClient::new("http://localhost:8899".to_string());
1666 let arcium_program = arcium_program_client(SolanaCluster::Localnet, payer.clone(), rpc);
1667
1668 let signer_pubkey = payer.pubkey();
1669 let mxe_program_id = Pubkey::new_unique();
1670 let callback_data = vec![0u8; MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE + 1];
1671
1672 let result = build_claim_failure_txs(
1673 &arcium_program,
1674 &signer_pubkey,
1675 &mxe_program_id,
1676 0,
1677 0,
1678 0,
1679 0,
1680 callback_data,
1681 0,
1682 );
1683
1684 assert!(matches!(
1685 result,
1686 Err(ClaimFailureError::CallbackDataTooLarge { size, limit })
1687 if size == MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE + 1
1688 && limit == MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE
1689 ));
1690 }
1691}