1use crate::{
2 StateProof,
3 tn_public_address::tn_pubkey_to_address_string,
4 txn_lib::{TnPubkey, Transaction},
5};
6use anyhow::Result;
7use hex;
8use std::collections::HashMap;
9
10pub const NOOP_PROGRAM: [u8; 32] = {
12 let mut arr = [0u8; 32];
13 arr[31] = 0x03;
14 arr
15};
16pub const SYSTEM_PROGRAM: [u8; 32] = {
17 let mut arr = [0u8; 32];
18 arr[31] = 0x01;
19 arr
20};
21pub const EOA_PROGRAM: [u8; 32] = {
22 let arr = [0u8; 32];
23 arr
24};
25
26pub const UPLOADER_PROGRAM: [u8; 32] = {
27 let mut arr = [0u8; 32];
28 arr[31] = 0x02;
29 arr
30};
31pub const FAUCET_PROGRAM: [u8; 32] = {
32 let mut arr = [0u8; 32];
33 arr[31] = 0xFA;
34 arr
35};
36
37#[derive(Debug, Clone)]
38pub struct TransactionBuilder {
39 }
44
45impl TransactionBuilder {
46 pub fn build_create_with_fee_payer_proof(
48 fee_payer: TnPubkey,
49 start_slot: u64,
50 fee_payer_state_proof: &StateProof,
51 ) -> Result<Transaction> {
52 let tx = Transaction::new(fee_payer, NOOP_PROGRAM, 0, 0)
53 .with_fee_payer_state_proof(fee_payer_state_proof)
54 .with_start_slot(start_slot)
55 .with_expiry_after(100)
56 .with_compute_units(10_000)
57 .with_memory_units(10_000)
58 .with_state_units(10_000);
59 Ok(tx)
60 }
61
62 pub fn build_transfer(
76 fee_payer: TnPubkey,
77 program: TnPubkey,
78 to_account: TnPubkey,
79 amount: u64,
80 fee: u64,
81 nonce: u64,
82 start_slot: u64,
83 ) -> Result<Transaction> {
84 let from_account_idx = 0u16; let to_account_idx = 2u16; let instruction_data =
89 build_transfer_instruction(from_account_idx, to_account_idx, amount)?;
90
91 let tx = Transaction::new(fee_payer, program, fee, nonce)
92 .with_start_slot(start_slot)
93 .add_rw_account(to_account) .with_instructions(instruction_data)
95 .with_expiry_after(100)
96 .with_compute_units(10000)
97 .with_memory_units(10000)
98 .with_state_units(10000);
99
100 Ok(tx)
101 }
102
103 pub fn build_create_account(
105 fee_payer: TnPubkey,
106 program: TnPubkey,
107 target_account: TnPubkey,
108 seed: &str,
109 state_proof: Option<&[u8]>,
110 fee: u64,
111 nonce: u64,
112 start_slot: u64,
113 ) -> Result<Transaction> {
114 let target_account_idx = 2u16; let instruction_data =
117 build_create_account_instruction(target_account_idx, seed, state_proof)?;
118
119 let tx = Transaction::new(fee_payer, program, fee, nonce)
120 .with_start_slot(start_slot)
121 .add_rw_account(target_account)
122 .with_instructions(instruction_data)
123 .with_expiry_after(100)
124 .with_compute_units(10_000)
125 .with_memory_units(10_000)
126 .with_state_units(10_000);
127
128 Ok(tx)
129 }
130
131 pub fn build_create_ephemeral_account(
133 fee_payer: TnPubkey,
134 program: TnPubkey,
135 target_account: TnPubkey,
136 seed: &[u8; 32],
137 fee: u64,
138 nonce: u64,
139 start_slot: u64,
140 ) -> Result<Transaction> {
141 let target_account_idx = 2u16; let instruction_data = build_ephemeral_account_instruction(target_account_idx, seed)?;
144
145 let tx = Transaction::new(fee_payer, program, fee, nonce)
146 .with_start_slot(start_slot)
147 .add_rw_account(target_account)
148 .with_instructions(instruction_data)
149 .with_expiry_after(100)
150 .with_compute_units(50_000)
151 .with_memory_units(10_000)
152 .with_state_units(10_000);
153 Ok(tx)
154 }
155
156 pub fn build_resize_account(
158 fee_payer: TnPubkey,
159 program: TnPubkey,
160 target_account: TnPubkey,
161 new_size: u64,
162 fee: u64,
163 nonce: u64,
164 start_slot: u64,
165 ) -> Result<Transaction> {
166 let target_account_idx = 2u16; let instruction_data = build_resize_instruction(target_account_idx, new_size)?;
169
170 let tx = Transaction::new(fee_payer, program, fee, nonce)
171 .with_start_slot(start_slot)
172 .with_expiry_after(100)
173 .with_compute_units(100032)
174 .with_state_units(1 + new_size.checked_div(4096).unwrap() as u16)
175 .with_memory_units(10000)
176 .add_rw_account(target_account)
177 .with_instructions(instruction_data)
178 .with_expiry_after(100)
179 .with_compute_units(10_000 + 2 * new_size as u32)
180 .with_memory_units(10_000)
181 .with_state_units(10_000);
182
183 Ok(tx)
184 }
185
186 pub fn build_compress_account(
188 fee_payer: TnPubkey,
189 program: TnPubkey,
190 target_account: TnPubkey,
191 state_proof: &[u8],
192 fee: u64,
193 nonce: u64,
194 start_slot: u64,
195 account_size: u32,
196 ) -> Result<Transaction> {
197 let target_account_idx = 2u16; let instruction_data = build_compress_instruction(target_account_idx, state_proof)?;
200
201 let tx = Transaction::new(fee_payer, program, fee, nonce)
202 .with_start_slot(start_slot)
203 .with_may_compress_account()
204 .add_rw_account(target_account)
205 .with_instructions(instruction_data)
206 .with_expiry_after(100)
207 .with_compute_units(100_300 + account_size * 2)
208 .with_memory_units(10000)
209 .with_state_units(10000);
210
211 Ok(tx)
212 }
213
214 pub fn build_decompress_account(
216 fee_payer: TnPubkey,
217 program: TnPubkey,
218 target_account: TnPubkey,
219 account_data: &[u8],
220 state_proof: &[u8],
221 fee: u64,
222 nonce: u64,
223 start_slot: u64,
224 ) -> Result<Transaction> {
225 let target_account_idx = 2u16; let instruction_data =
228 build_decompress_instruction(target_account_idx, account_data, state_proof)?;
229
230 let tx = Transaction::new(fee_payer, program, fee, nonce)
231 .with_start_slot(start_slot)
232 .add_rw_account(target_account)
233 .with_instructions(instruction_data)
234 .with_compute_units(100_300 + account_data.len() as u32 * 2)
235 .with_state_units(10_000)
236 .with_memory_units(10_000)
237 .with_expiry_after(100);
238 Ok(tx)
239 }
240
241 pub fn build_write_data(
243 fee_payer: TnPubkey,
244 program: TnPubkey,
245 target_account: TnPubkey,
246 offset: u16,
247 data: &[u8],
248 fee: u64,
249 nonce: u64,
250 start_slot: u64,
251 ) -> Result<Transaction> {
252 let target_account_idx = 2u16; let instruction_data = build_write_instruction(target_account_idx, offset, data)?;
255
256 let tx = Transaction::new(fee_payer, program, fee, nonce)
257 .with_start_slot(start_slot)
258 .with_expiry_after(100)
259 .with_compute_units(100045)
260 .with_state_units(10000)
261 .with_memory_units(10000)
262 .add_rw_account(target_account)
263 .with_instructions(instruction_data);
264
265 Ok(tx)
266 }
267}
268
269fn build_transfer_instruction(
278 from_account_idx: u16,
279 to_account_idx: u16,
280 amount: u64,
281) -> Result<Vec<u8>> {
282 let mut instruction = Vec::new();
283
284 instruction.extend_from_slice(&1u32.to_le_bytes());
286
287 instruction.extend_from_slice(&amount.to_le_bytes());
290
291 instruction.extend_from_slice(&from_account_idx.to_le_bytes());
293
294 instruction.extend_from_slice(&to_account_idx.to_le_bytes());
296
297 Ok(instruction)
298}
299
300fn build_create_account_instruction(
302 target_account_idx: u16,
303 seed: &str,
304 state_proof: Option<&[u8]>,
305) -> Result<Vec<u8>> {
306 let mut instruction = Vec::new();
307
308 instruction.push(0x00);
310
311 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
313
314 let seed_bytes =
316 hex::decode(seed).map_err(|e| anyhow::anyhow!("Failed to decode hex seed: {}", e))?;
317
318 instruction.extend_from_slice(&(seed_bytes.len() as u64).to_le_bytes());
320
321 let has_proof = state_proof.is_some();
323 instruction.push(if has_proof { 1u8 } else { 0u8 });
324
325 instruction.extend_from_slice(&seed_bytes);
327
328 if let Some(proof) = state_proof {
330 instruction.extend_from_slice(proof);
331 }
332
333 Ok(instruction)
334}
335
336fn build_ephemeral_account_instruction(
338 target_account_idx: u16,
339 seed: &[u8; 32],
340) -> Result<Vec<u8>> {
341 let mut instruction = Vec::new();
342
343 instruction.push(0x01);
345
346 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
348
349 instruction.extend_from_slice(&(seed.len() as u64).to_le_bytes());
351
352 instruction.extend_from_slice(seed);
354
355 Ok(instruction)
356}
357
358fn build_resize_instruction(target_account_idx: u16, new_size: u64) -> Result<Vec<u8>> {
360 let mut instruction = Vec::new();
361
362 instruction.push(0x04);
364
365 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
367
368 instruction.extend_from_slice(&new_size.to_le_bytes());
370
371 Ok(instruction)
372}
373
374fn build_write_instruction(target_account_idx: u16, offset: u16, data: &[u8]) -> Result<Vec<u8>> {
376 let mut instruction = Vec::new();
377
378 instruction.push(0xC8);
380
381 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
383
384 instruction.extend_from_slice(&offset.to_le_bytes());
386
387 instruction.extend_from_slice(&(data.len() as u16).to_le_bytes());
389
390 instruction.extend_from_slice(data);
392
393 Ok(instruction)
394}
395
396fn build_compress_instruction(target_account_idx: u16, state_proof: &[u8]) -> Result<Vec<u8>> {
398 let mut instruction = Vec::new();
399
400 instruction.push(0x05);
403
404 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
406
407 instruction.extend_from_slice(state_proof);
409
410 Ok(instruction)
411}
412
413fn build_decompress_instruction(
414 target_account_idx: u16,
415 account_data: &[u8],
416 state_proof: &[u8],
417) -> Result<Vec<u8>> {
418 let mut instruction = Vec::new();
419
420 instruction.push(0x06);
422
423 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
425 instruction.extend_from_slice(&(account_data.len() as u64).to_le_bytes());
426
427 instruction.extend_from_slice(account_data);
429
430 instruction.extend_from_slice(state_proof);
432
433 Ok(instruction)
434}
435
436pub fn generate_ephemeral_address(seed: &str) -> Result<String> {
441 let owner_pubkey = [0u8; 32];
443
444 let seed_bytes =
446 hex::decode(seed).map_err(|e| anyhow::anyhow!("Failed to decode hex seed: {}", e))?;
447
448 let mut seed_32 = [0u8; 32];
450 let copy_len = std::cmp::min(seed_bytes.len(), 32);
451 seed_32[..copy_len].copy_from_slice(&seed_bytes[..copy_len]);
452
453 Ok(
455 crate::tn_public_address::create_program_defined_account_address_string(
456 &owner_pubkey,
457 true, &seed_32,
459 ),
460 )
461}
462
463pub fn generate_system_derived_address(seed: &str, is_ephemeral: bool) -> Result<String> {
464 let seed_bytes =
466 hex::decode(seed).map_err(|e| anyhow::anyhow!("Failed to decode hex seed: {}", e))?;
467
468 let pubkey = generate_derived_address(&seed_bytes, &[0u8; 32], is_ephemeral)?;
469
470 Ok(tn_pubkey_to_address_string(&pubkey))
471}
472
473pub fn generate_derived_address(
474 seed: &[u8],
475 owner_pubkey: &[u8; 32],
476 is_ephemeral: bool,
477) -> Result<[u8; 32]> {
478 use sha2::{Digest, Sha256};
479
480 let mut hasher = Sha256::new();
482
483 hasher.update(&owner_pubkey);
485
486 hasher.update(&[is_ephemeral as u8]);
488
489 hasher.update(&seed);
491
492 Ok(hasher.finalize().into())
494}
495
496#[cfg(test)]
497mod tests {
498 use super::*;
499
500 #[test]
501 fn test_ephemeral_address_generation() {
502 let hex_seed1 = hex::encode("test_seed_123");
504 let hex_seed2 = hex::encode("test_seed_123");
505 let hex_seed3 = hex::encode("different_seed");
506
507 let addr1 = generate_ephemeral_address(&hex_seed1).unwrap();
508 let addr2 = generate_ephemeral_address(&hex_seed2).unwrap();
509 let addr3 = generate_ephemeral_address(&hex_seed3).unwrap();
510
511 assert_eq!(addr1, addr2);
513
514 assert_ne!(addr1, addr3);
516
517 assert!(addr1.starts_with("ta"));
519 assert!(addr2.starts_with("ta"));
520 assert!(addr3.starts_with("ta"));
521
522 assert_eq!(addr1.len(), 46);
524 assert_eq!(addr2.len(), 46);
525 assert_eq!(addr3.len(), 46);
526 }
527
528 #[test]
529 fn test_eoa_transfer_instruction_format() {
530 let from_idx = 0u16;
532 let to_idx = 2u16;
533 let amount = 1000u64;
534
535 let instruction = build_transfer_instruction(from_idx, to_idx, amount).unwrap();
536
537 assert_eq!(instruction.len(), 16, "Instruction should be 16 bytes");
545
546 let discriminant = u32::from_le_bytes([
548 instruction[0],
549 instruction[1],
550 instruction[2],
551 instruction[3],
552 ]);
553 assert_eq!(discriminant, 1, "Discriminant should be 1 for TRANSFER");
554
555 let parsed_amount = u64::from_le_bytes([
557 instruction[4],
558 instruction[5],
559 instruction[6],
560 instruction[7],
561 instruction[8],
562 instruction[9],
563 instruction[10],
564 instruction[11],
565 ]);
566 assert_eq!(parsed_amount, amount, "Amount should match input");
567
568 let parsed_from = u16::from_le_bytes([instruction[12], instruction[13]]);
570 assert_eq!(parsed_from, from_idx, "From index should match input");
571
572 let parsed_to = u16::from_le_bytes([instruction[14], instruction[15]]);
574 assert_eq!(parsed_to, to_idx, "To index should match input");
575 }
576
577 #[test]
578 fn test_faucet_deposit_instruction_layout_with_fee_payer_depositor() {
579 let fee_payer = [1u8; 32];
580 let faucet_program = FAUCET_PROGRAM;
581 let faucet_account = [2u8; 32];
582 let depositor_account = fee_payer;
583 let amount = 500u64;
584
585 let tx = TransactionBuilder::build_faucet_deposit(
586 fee_payer,
587 faucet_program,
588 faucet_account,
589 depositor_account,
590 EOA_PROGRAM,
591 amount,
592 0,
593 42,
594 100,
595 )
596 .expect("build faucet deposit");
597
598 let rw_accs = tx.rw_accs.expect("rw accounts must exist");
599 assert_eq!(rw_accs.len(), 1);
600 assert_eq!(rw_accs[0], faucet_account);
601
602 let ro_accs = tx.r_accs.expect("ro accounts must exist");
603 assert_eq!(ro_accs.len(), 1);
604 assert_eq!(ro_accs[0], EOA_PROGRAM);
605
606 let instruction = tx.instructions.expect("instruction bytes must exist");
607 assert_eq!(instruction.len(), 18, "Deposit instruction must be 18 bytes");
608
609 let discriminant =
610 u32::from_le_bytes([instruction[0], instruction[1], instruction[2], instruction[3]]);
611 assert_eq!(discriminant, 0, "Deposit discriminant should be 0");
612
613 let faucet_idx = u16::from_le_bytes([instruction[4], instruction[5]]);
614 let depositor_idx = u16::from_le_bytes([instruction[6], instruction[7]]);
615 let eoa_idx = u16::from_le_bytes([instruction[8], instruction[9]]);
616 let parsed_amount = u64::from_le_bytes([
617 instruction[10],
618 instruction[11],
619 instruction[12],
620 instruction[13],
621 instruction[14],
622 instruction[15],
623 instruction[16],
624 instruction[17],
625 ]);
626
627 assert_eq!(faucet_idx, 2, "Faucet account should be first RW account");
628 assert_eq!(depositor_idx, 0, "Depositor shares the fee payer index");
629 assert_eq!(eoa_idx, 3, "EOA program should follow RW accounts");
630 assert_eq!(parsed_amount, amount, "Amount should match input");
631 }
632
633 #[test]
634 fn test_build_token_initialize_mint() {
635 let fee_payer = [1u8; 32];
637 let token_program = [2u8; 32];
638 let mint_account = [3u8; 32];
639 let creator = [4u8; 32];
640 let mint_authority = [5u8; 32];
641 let freeze_authority = [6u8; 32];
642
643 let decimals = 9u8;
644 let ticker = "TEST";
645 let seed = [7u8; 32];
646 let state_proof = vec![8u8; 64];
647
648 let result = TransactionBuilder::build_token_initialize_mint(
650 fee_payer,
651 token_program,
652 mint_account,
653 creator,
654 mint_authority,
655 Some(freeze_authority),
656 decimals,
657 ticker,
658 seed,
659 state_proof.clone(),
660 1000, 1, 100, );
664
665 assert!(result.is_ok(), "Should build valid transaction with freeze authority");
666 let tx = result.unwrap();
667 assert!(tx.instructions.is_some(), "Transaction should have instructions");
668
669 let result_no_freeze = TransactionBuilder::build_token_initialize_mint(
671 fee_payer,
672 token_program,
673 mint_account,
674 creator,
675 mint_authority,
676 None,
677 decimals,
678 ticker,
679 seed,
680 state_proof,
681 1000,
682 1,
683 100,
684 );
685
686 assert!(result_no_freeze.is_ok(), "Should build valid transaction without freeze authority");
687 }
688
689 #[test]
690 fn test_build_token_initialize_mint_instruction_format() {
691 let mint_account_idx = 2u16;
692 let decimals = 9u8;
693 let creator = [1u8; 32];
694 let mint_authority = [2u8; 32];
695 let freeze_authority = [3u8; 32];
696 let ticker = "TST";
697 let seed = [4u8; 32];
698 let state_proof = vec![5u8; 10];
699
700 let instruction = build_token_initialize_mint_instruction(
701 mint_account_idx,
702 decimals,
703 creator,
704 mint_authority,
705 Some(freeze_authority),
706 ticker,
707 seed,
708 state_proof.clone(),
709 )
710 .unwrap();
711
712 let expected_min_size = 1 + 2 + 1 + 32 + 32 + 32 + 1 + 1 + 8 + 32 + state_proof.len();
716 assert_eq!(instruction.len(), expected_min_size);
717
718 assert_eq!(instruction[0], 0, "First byte should be InitializeMint tag (0)");
720
721 let parsed_idx = u16::from_le_bytes([instruction[1], instruction[2]]);
723 assert_eq!(parsed_idx, mint_account_idx);
724
725 assert_eq!(instruction[3], decimals);
727
728 assert_eq!(&instruction[4..36], &creator);
730
731 assert_eq!(&instruction[36..68], &mint_authority);
733
734 assert_eq!(&instruction[68..100], &freeze_authority);
736
737 assert_eq!(instruction[100], 1);
739 }
740
741 #[test]
742 fn test_token_initialize_mint_creator_vs_mint_authority() {
743 let fee_payer = [1u8; 32];
745 let token_program = [2u8; 32];
746 let mint_account = [3u8; 32];
747 let creator = [4u8; 32];
748 let mint_authority = [5u8; 32]; let seed = [6u8; 32];
750 let state_proof = vec![7u8; 32];
751
752 let result = TransactionBuilder::build_token_initialize_mint(
753 fee_payer,
754 token_program,
755 mint_account,
756 creator,
757 mint_authority,
758 None,
759 9,
760 "TEST",
761 seed,
762 state_proof,
763 1000,
764 1,
765 100,
766 );
767
768 assert!(result.is_ok(), "Should allow different creator and mint_authority");
769
770 let result_same = TransactionBuilder::build_token_initialize_mint(
772 fee_payer,
773 token_program,
774 mint_account,
775 creator,
776 creator, None,
778 9,
779 "TEST",
780 seed,
781 vec![7u8; 32],
782 1000,
783 1,
784 100,
785 );
786
787 assert!(result_same.is_ok(), "Should allow same creator and mint_authority");
788 }
789}
790
791pub const TN_UPLOADER_PROGRAM_INSTRUCTION_CREATE: u32 = 0x00;
793pub const TN_UPLOADER_PROGRAM_INSTRUCTION_WRITE: u32 = 0x01;
794pub const TN_UPLOADER_PROGRAM_INSTRUCTION_DESTROY: u32 = 0x02;
795pub const TN_UPLOADER_PROGRAM_INSTRUCTION_FINALIZE: u32 = 0x03;
796
797#[repr(C, packed)]
799#[derive(Debug, Clone, Copy)]
800pub struct UploaderCreateArgs {
801 pub buffer_account_idx: u16,
802 pub meta_account_idx: u16,
803 pub authority_account_idx: u16,
804 pub buffer_account_sz: u32,
805 pub expected_account_hash: [u8; 32],
806 pub seed_len: u32,
807 }
809
810#[repr(C, packed)]
812#[derive(Debug, Clone, Copy)]
813pub struct UploaderWriteArgs {
814 pub buffer_account_idx: u16,
815 pub meta_account_idx: u16,
816 pub data_len: u32,
817 pub data_offset: u32,
818 }
820
821#[repr(C, packed)]
823#[derive(Debug, Clone, Copy)]
824pub struct UploaderFinalizeArgs {
825 pub buffer_account_idx: u16,
826 pub meta_account_idx: u16,
827 pub expected_account_hash: [u8; 32],
828}
829
830#[repr(C, packed)]
832#[derive(Debug, Clone, Copy)]
833pub struct UploaderDestroyArgs {
834 pub buffer_account_idx: u16,
835 pub meta_account_idx: u16,
836}
837
838pub const MANAGER_INSTRUCTION_CREATE_PERMANENT: u8 = 0x00;
840pub const MANAGER_INSTRUCTION_CREATE_EPHEMERAL: u8 = 0x01;
841pub const MANAGER_INSTRUCTION_UPGRADE: u8 = 0x02;
842pub const MANAGER_INSTRUCTION_SET_PAUSE: u8 = 0x03;
843pub const MANAGER_INSTRUCTION_DESTROY: u8 = 0x04;
844pub const MANAGER_INSTRUCTION_FINALIZE: u8 = 0x05;
845pub const MANAGER_INSTRUCTION_SET_AUTHORITY: u8 = 0x06;
846pub const MANAGER_INSTRUCTION_CLAIM_AUTHORITY: u8 = 0x07;
847
848#[repr(C, packed)]
850#[derive(Debug, Clone, Copy)]
851pub struct ManagerHeaderArgs {
852 pub discriminant: u8,
853 pub meta_account_idx: u16,
854 pub program_account_idx: u16,
855}
856
857#[repr(C, packed)]
859#[derive(Debug, Clone, Copy)]
860pub struct ManagerCreateArgs {
861 pub discriminant: u8,
862 pub meta_account_idx: u16,
863 pub program_account_idx: u16,
864 pub srcbuf_account_idx: u16,
865 pub srcbuf_offset: u32,
866 pub srcbuf_size: u32,
867 pub authority_account_idx: u16,
868 pub seed_len: u32,
869 }
871
872#[repr(C, packed)]
874#[derive(Debug, Clone, Copy)]
875pub struct ManagerUpgradeArgs {
876 pub discriminant: u8,
877 pub meta_account_idx: u16,
878 pub program_account_idx: u16,
879 pub srcbuf_account_idx: u16,
880 pub srcbuf_offset: u32,
881 pub srcbuf_size: u32,
882}
883
884#[repr(C, packed)]
886#[derive(Debug, Clone, Copy)]
887pub struct ManagerSetPauseArgs {
888 pub discriminant: u8,
889 pub meta_account_idx: u16,
890 pub program_account_idx: u16,
891 pub is_paused: u8,
892}
893
894#[repr(C, packed)]
896#[derive(Debug, Clone, Copy)]
897pub struct ManagerSetAuthorityArgs {
898 pub discriminant: u8,
899 pub meta_account_idx: u16,
900 pub program_account_idx: u16,
901 pub authority_candidate: [u8; 32],
902}
903
904pub const TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_CREATE: u8 = 0x00;
906pub const TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_WRITE: u8 = 0x01;
907
908#[repr(C, packed)]
910#[derive(Debug, Clone, Copy)]
911pub struct TestUploaderCreateArgs {
912 pub account_idx: u16,
913 pub is_ephemeral: u8,
914 pub account_sz: u32,
915 pub seed_len: u32,
916 }
918
919#[repr(C, packed)]
921#[derive(Debug, Clone, Copy)]
922pub struct TestUploaderWriteArgs {
923 pub target_account_idx: u16,
924 pub target_offset: u32,
925 pub data_len: u32,
926 }
928
929#[repr(C, packed)]
931#[derive(Debug, Clone, Copy)]
932pub struct SystemProgramDecompress2Args {
933 pub target_account_idx: u16,
934 pub meta_account_idx: u16,
935 pub data_account_idx: u16,
936 pub data_offset: u32,
937}
938
939impl TransactionBuilder {
940 pub fn build_uploader_create(
942 fee_payer: TnPubkey,
943 uploader_program: TnPubkey,
944 meta_account: TnPubkey,
945 buffer_account: TnPubkey,
946 buffer_size: u32,
947 expected_hash: [u8; 32],
948 seed: &[u8],
949 fee: u64,
950 nonce: u64,
951 start_slot: u64,
952 ) -> Result<Transaction> {
953 let authority_account_idx = 0u16;
955
956 let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
957 .with_start_slot(start_slot)
958 .with_expiry_after(10)
959 .with_compute_units(50_000 + 2 * buffer_size as u32)
960 .with_memory_units(10_000)
961 .with_state_units(10_000);
962
963 let mut meta_account_idx = 2u16;
964 let mut buffer_account_idx = 3u16;
965 if meta_account > buffer_account {
966 meta_account_idx = 3u16;
967 buffer_account_idx = 2u16;
968 tx = tx
969 .add_rw_account(buffer_account)
970 .add_rw_account(meta_account)
971 } else {
972 tx = tx
973 .add_rw_account(meta_account)
974 .add_rw_account(buffer_account)
975 }
976
977 let instruction_data = build_uploader_create_instruction(
978 buffer_account_idx,
979 meta_account_idx,
980 authority_account_idx,
981 buffer_size,
982 expected_hash,
983 seed,
984 )?;
985
986 tx = tx.with_instructions(instruction_data);
987
988 Ok(tx)
989 }
990
991 pub fn build_uploader_write(
993 fee_payer: TnPubkey,
994 uploader_program: TnPubkey,
995 meta_account: TnPubkey,
996 buffer_account: TnPubkey,
997 data: &[u8],
998 offset: u32,
999 fee: u64,
1000 nonce: u64,
1001 start_slot: u64,
1002 ) -> Result<Transaction> {
1003 let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
1005 .with_start_slot(start_slot)
1006 .with_expiry_after(10000)
1007 .with_compute_units(500_000_000)
1008 .with_memory_units(5000)
1009 .with_state_units(5000);
1010
1011 let mut meta_account_idx = 2u16;
1012 let mut buffer_account_idx = 3u16;
1013 if meta_account > buffer_account {
1014 meta_account_idx = 3u16;
1015 buffer_account_idx = 2u16;
1016 tx = tx
1017 .add_rw_account(buffer_account)
1018 .add_rw_account(meta_account)
1019 } else {
1020 tx = tx
1021 .add_rw_account(meta_account)
1022 .add_rw_account(buffer_account)
1023 }
1024
1025 let instruction_data =
1026 build_uploader_write_instruction(buffer_account_idx, meta_account_idx, data, offset)?;
1027
1028 tx = tx.with_instructions(instruction_data);
1029
1030 Ok(tx)
1031 }
1032
1033 pub fn build_uploader_finalize(
1035 fee_payer: TnPubkey,
1036 uploader_program: TnPubkey,
1037 meta_account: TnPubkey,
1038 buffer_account: TnPubkey,
1039 buffer_size: u32,
1040 expected_hash: [u8; 32],
1041 fee: u64,
1042 nonce: u64,
1043 start_slot: u64,
1044 ) -> Result<Transaction> {
1045 let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
1046 .with_start_slot(start_slot)
1047 .with_expiry_after(10000)
1048 .with_compute_units(50_000 + 200 * buffer_size as u32)
1049 .with_memory_units(5000)
1050 .with_state_units(5000);
1051
1052 let mut meta_account_idx = 2u16;
1054 let mut buffer_account_idx = 3u16;
1055 if meta_account > buffer_account {
1056 meta_account_idx = 3u16;
1057 buffer_account_idx = 2u16;
1058 tx = tx
1059 .add_rw_account(buffer_account)
1060 .add_rw_account(meta_account)
1061 } else {
1062 tx = tx
1063 .add_rw_account(meta_account)
1064 .add_rw_account(buffer_account)
1065 }
1066
1067 let instruction_data = build_uploader_finalize_instruction(
1068 buffer_account_idx,
1069 meta_account_idx,
1070 expected_hash,
1071 )?;
1072
1073 tx = tx.with_instructions(instruction_data);
1074
1075 Ok(tx)
1076 }
1077
1078 pub fn build_uploader_destroy(
1080 fee_payer: TnPubkey,
1081 uploader_program: TnPubkey,
1082 meta_account: TnPubkey,
1083 buffer_account: TnPubkey,
1084 fee: u64,
1085 nonce: u64,
1086 start_slot: u64,
1087 ) -> Result<Transaction> {
1088 let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
1089 .with_start_slot(start_slot)
1090 .with_expiry_after(10000)
1091 .with_compute_units(50000)
1092 .with_memory_units(5000)
1093 .with_state_units(5000);
1094
1095 let mut meta_account_idx = 2u16;
1097 let mut buffer_account_idx = 3u16;
1098 if meta_account > buffer_account {
1099 meta_account_idx = 3u16;
1100 buffer_account_idx = 2u16;
1101 tx = tx
1102 .add_rw_account(buffer_account)
1103 .add_rw_account(meta_account)
1104 } else {
1105 tx = tx
1106 .add_rw_account(meta_account)
1107 .add_rw_account(buffer_account)
1108 }
1109
1110 let instruction_data =
1111 build_uploader_destroy_instruction(buffer_account_idx, meta_account_idx)?;
1112
1113 tx = tx.with_instructions(instruction_data);
1114 Ok(tx)
1115 }
1116
1117 pub fn build_manager_create(
1119 fee_payer: TnPubkey,
1120 manager_program: TnPubkey,
1121 meta_account: TnPubkey,
1122 program_account: TnPubkey,
1123 srcbuf_account: TnPubkey,
1124 authority_account: TnPubkey,
1125 srcbuf_offset: u32,
1126 srcbuf_size: u32,
1127 seed: &[u8],
1128 is_ephemeral: bool,
1129 meta_proof: Option<&[u8]>,
1130 program_proof: Option<&[u8]>,
1131 fee: u64,
1132 nonce: u64,
1133 start_slot: u64,
1134 ) -> Result<Transaction> {
1135 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
1136 .with_start_slot(start_slot)
1137 .with_expiry_after(10000)
1138 .with_compute_units(500_000_000)
1139 .with_memory_units(5000)
1140 .with_state_units(5000);
1141
1142 let authority_is_fee_payer = authority_account == fee_payer;
1144
1145 let mut rw_accounts = vec![(meta_account, "meta"), (program_account, "program")];
1147
1148 let mut r_accounts = vec![(srcbuf_account, "srcbuf")];
1149
1150 if !authority_is_fee_payer {
1152 r_accounts.push((authority_account, "authority"));
1153 }
1154
1155 rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
1157
1158 r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
1160
1161 let mut accounts = rw_accounts;
1163 accounts.extend(r_accounts);
1164
1165 let mut meta_account_idx = 0u16;
1166 let mut program_account_idx = 0u16;
1167 let mut srcbuf_account_idx = 0u16;
1168 let mut authority_account_idx = if authority_is_fee_payer {
1169 0u16 } else {
1171 0u16 };
1173
1174 for (i, (account, account_type)) in accounts.iter().enumerate() {
1175 let idx = (i + 2) as u16; match *account_type {
1177 "meta" => {
1178 meta_account_idx = idx;
1179 tx = tx.add_rw_account(*account);
1180 }
1181 "program" => {
1182 program_account_idx = idx;
1183 tx = tx.add_rw_account(*account);
1184 }
1185 "srcbuf" => {
1186 srcbuf_account_idx = idx;
1187 tx = tx.add_r_account(*account);
1188 }
1189 "authority" => {
1190 authority_account_idx = idx;
1191 tx = tx.add_r_account(*account);
1192 }
1193 _ => unreachable!(),
1194 }
1195 }
1196
1197 let discriminant = if is_ephemeral {
1198 MANAGER_INSTRUCTION_CREATE_EPHEMERAL
1199 } else {
1200 MANAGER_INSTRUCTION_CREATE_PERMANENT
1201 };
1202
1203 let combined_proof = if let (Some(meta), Some(program)) = (meta_proof, program_proof) {
1205 let mut combined = Vec::with_capacity(meta.len() + program.len());
1206 combined.extend_from_slice(meta);
1207 combined.extend_from_slice(program);
1208 Some(combined)
1209 } else {
1210 None
1211 };
1212
1213 let instruction_data = build_manager_create_instruction(
1214 discriminant,
1215 meta_account_idx,
1216 program_account_idx,
1217 srcbuf_account_idx,
1218 authority_account_idx,
1219 srcbuf_offset,
1220 srcbuf_size,
1221 seed,
1222 combined_proof.as_deref(),
1223 )?;
1224
1225 tx = tx.with_instructions(instruction_data);
1226 Ok(tx)
1227 }
1228
1229 pub fn build_manager_upgrade(
1231 fee_payer: TnPubkey,
1232 manager_program: TnPubkey,
1233 meta_account: TnPubkey,
1234 program_account: TnPubkey,
1235 srcbuf_account: TnPubkey,
1236 srcbuf_offset: u32,
1237 srcbuf_size: u32,
1238 fee: u64,
1239 nonce: u64,
1240 start_slot: u64,
1241 ) -> Result<Transaction> {
1242 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
1243 .with_start_slot(start_slot)
1244 .with_expiry_after(10000)
1245 .with_compute_units(500_000_000)
1246 .with_memory_units(5000)
1247 .with_state_units(5000);
1248
1249 let mut rw_accounts = vec![(meta_account, "meta"), (program_account, "program")];
1251
1252 let mut r_accounts = vec![(srcbuf_account, "srcbuf")];
1253
1254 rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
1256
1257 r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
1259
1260 let mut accounts = rw_accounts;
1262 accounts.extend(r_accounts);
1263
1264 let mut meta_account_idx = 0u16;
1265 let mut program_account_idx = 0u16;
1266 let mut srcbuf_account_idx = 0u16;
1267
1268 for (i, (account, account_type)) in accounts.iter().enumerate() {
1269 let idx = (i + 2) as u16; match *account_type {
1271 "meta" => {
1272 meta_account_idx = idx;
1273 tx = tx.add_rw_account(*account);
1274 }
1275 "program" => {
1276 program_account_idx = idx;
1277 tx = tx.add_rw_account(*account);
1278 }
1279 "srcbuf" => {
1280 srcbuf_account_idx = idx;
1281 tx = tx.add_r_account(*account);
1282 }
1283 _ => unreachable!(),
1284 }
1285 }
1286
1287 let instruction_data = build_manager_upgrade_instruction(
1288 meta_account_idx,
1289 program_account_idx,
1290 srcbuf_account_idx,
1291 srcbuf_offset,
1292 srcbuf_size,
1293 )?;
1294
1295 tx = tx.with_instructions(instruction_data);
1296 Ok(tx)
1297 }
1298
1299 pub fn build_manager_set_pause(
1301 fee_payer: TnPubkey,
1302 manager_program: TnPubkey,
1303 meta_account: TnPubkey,
1304 program_account: TnPubkey,
1305 is_paused: bool,
1306 fee: u64,
1307 nonce: u64,
1308 start_slot: u64,
1309 ) -> Result<Transaction> {
1310 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
1311 .with_start_slot(start_slot)
1312 .with_expiry_after(10000)
1313 .with_compute_units(100_000_000)
1314 .with_memory_units(5000)
1315 .with_state_units(5000);
1316
1317 let mut accounts = vec![(meta_account, "meta"), (program_account, "program")];
1319 accounts.sort_by(|a, b| a.0.cmp(&b.0));
1320
1321 let mut meta_account_idx = 0u16;
1322 let mut program_account_idx = 0u16;
1323
1324 for (i, (account, account_type)) in accounts.iter().enumerate() {
1325 let idx = (i + 2) as u16;
1326 match *account_type {
1327 "meta" => {
1328 meta_account_idx = idx;
1329 tx = tx.add_rw_account(*account);
1330 }
1331 "program" => {
1332 program_account_idx = idx;
1333 tx = tx.add_rw_account(*account);
1334 }
1335 _ => unreachable!(),
1336 }
1337 }
1338
1339 let instruction_data =
1340 build_manager_set_pause_instruction(meta_account_idx, program_account_idx, is_paused)?;
1341
1342 tx = tx.with_instructions(instruction_data);
1343 Ok(tx)
1344 }
1345
1346 pub fn build_manager_simple(
1348 fee_payer: TnPubkey,
1349 manager_program: TnPubkey,
1350 meta_account: TnPubkey,
1351 program_account: TnPubkey,
1352 instruction_type: u8,
1353 fee: u64,
1354 nonce: u64,
1355 start_slot: u64,
1356 ) -> Result<Transaction> {
1357 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
1358 .with_start_slot(start_slot)
1359 .with_expiry_after(10000)
1360 .with_compute_units(100_000_000)
1361 .with_memory_units(5000)
1362 .with_state_units(5000);
1363
1364 let mut accounts = vec![(meta_account, "meta"), (program_account, "program")];
1366 accounts.sort_by(|a, b| a.0.cmp(&b.0));
1367
1368 let mut meta_account_idx = 0u16;
1369 let mut program_account_idx = 0u16;
1370
1371 for (i, (account, account_type)) in accounts.iter().enumerate() {
1372 let idx = (i + 2) as u16;
1373 match *account_type {
1374 "meta" => {
1375 meta_account_idx = idx;
1376 tx = tx.add_rw_account(*account);
1377 }
1378 "program" => {
1379 program_account_idx = idx;
1380 tx = tx.add_rw_account(*account);
1381 }
1382 _ => unreachable!(),
1383 }
1384 }
1385
1386 let instruction_data = build_manager_header_instruction(
1387 instruction_type,
1388 meta_account_idx,
1389 program_account_idx,
1390 )?;
1391
1392 tx = tx.with_instructions(instruction_data);
1393 Ok(tx)
1394 }
1395
1396 pub fn build_manager_set_authority(
1398 fee_payer: TnPubkey,
1399 manager_program: TnPubkey,
1400 meta_account: TnPubkey,
1401 program_account: TnPubkey,
1402 authority_candidate: [u8; 32],
1403 fee: u64,
1404 nonce: u64,
1405 start_slot: u64,
1406 ) -> Result<Transaction> {
1407 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
1408 .with_start_slot(start_slot)
1409 .with_expiry_after(10000)
1410 .with_compute_units(100_000_000)
1411 .with_memory_units(5000)
1412 .with_state_units(5000);
1413
1414 let mut accounts = vec![(meta_account, "meta"), (program_account, "program")];
1416 accounts.sort_by(|a, b| a.0.cmp(&b.0));
1417
1418 let mut meta_account_idx = 0u16;
1419 let mut program_account_idx = 0u16;
1420
1421 for (i, (account, account_type)) in accounts.iter().enumerate() {
1422 let idx = (i + 2) as u16;
1423 match *account_type {
1424 "meta" => {
1425 meta_account_idx = idx;
1426 tx = tx.add_rw_account(*account);
1427 }
1428 "program" => {
1429 program_account_idx = idx;
1430 tx = tx.add_rw_account(*account);
1431 }
1432 _ => unreachable!(),
1433 }
1434 }
1435
1436 let instruction_data = build_manager_set_authority_instruction(
1437 meta_account_idx,
1438 program_account_idx,
1439 authority_candidate,
1440 )?;
1441
1442 tx = tx.with_instructions(instruction_data);
1443 Ok(tx)
1444 }
1445
1446 pub fn build_test_uploader_create(
1448 fee_payer: TnPubkey,
1449 test_uploader_program: TnPubkey,
1450 target_account: TnPubkey,
1451 account_sz: u32,
1452 seed: &[u8],
1453 is_ephemeral: bool,
1454 state_proof: Option<&[u8]>,
1455 fee: u64,
1456 nonce: u64,
1457 start_slot: u64,
1458 ) -> Result<Transaction> {
1459 let target_account_idx = 2u16;
1461
1462 let tx = Transaction::new(fee_payer, test_uploader_program, fee, nonce)
1463 .with_start_slot(start_slot)
1464 .with_expiry_after(100)
1465 .with_compute_units(100_000 + account_sz)
1466 .with_memory_units(10_000)
1467 .with_state_units(10_000)
1468 .add_rw_account(target_account);
1469
1470 let instruction_data = build_test_uploader_create_instruction(
1471 target_account_idx,
1472 account_sz,
1473 seed,
1474 is_ephemeral,
1475 state_proof,
1476 )?;
1477
1478 let tx = tx.with_instructions(instruction_data);
1479 Ok(tx)
1480 }
1481
1482 pub fn build_test_uploader_write(
1484 fee_payer: TnPubkey,
1485 test_uploader_program: TnPubkey,
1486 target_account: TnPubkey,
1487 offset: u32,
1488 data: &[u8],
1489 fee: u64,
1490 nonce: u64,
1491 start_slot: u64,
1492 ) -> Result<Transaction> {
1493 let target_account_idx = 2u16;
1495
1496 let tx = Transaction::new(fee_payer, test_uploader_program, fee, nonce)
1497 .with_start_slot(start_slot)
1498 .with_expiry_after(10_000)
1499 .with_compute_units(100_000 + 18 * data.len() as u32)
1500 .with_memory_units(10_000)
1501 .with_state_units(10_000)
1502 .add_rw_account(target_account);
1503
1504 let instruction_data =
1505 build_test_uploader_write_instruction(target_account_idx, offset, data)?;
1506
1507 let tx = tx.with_instructions(instruction_data);
1508 Ok(tx)
1509 }
1510
1511 pub fn build_decompress2(
1513 fee_payer: TnPubkey,
1514 program: TnPubkey,
1515 target_account: TnPubkey,
1516 meta_account: TnPubkey,
1517 data_account: TnPubkey,
1518 data_offset: u32,
1519 state_proof: &[u8],
1520 fee: u64,
1521 nonce: u64,
1522 start_slot: u64,
1523 data_sz: u32,
1524 ) -> Result<Transaction> {
1525 let mut tx = Transaction::new(fee_payer, program, fee, nonce)
1527 .with_start_slot(start_slot)
1528 .with_expiry_after(100)
1529 .with_compute_units(10_000 + 2 * data_sz)
1530 .with_memory_units(10_000)
1531 .with_state_units(10);
1532
1533 let target_account_idx = 2u16;
1535 tx = tx.add_rw_account(target_account);
1536
1537 let mut meta_account_idx = 0u16;
1538 let mut data_account_idx = 0u16;
1539
1540 if meta_account == data_account {
1542 let account_idx = 3u16;
1544 meta_account_idx = account_idx;
1545 data_account_idx = account_idx;
1546 tx = tx.add_r_account(meta_account);
1547 } else {
1548 let mut read_accounts = vec![(meta_account, "meta"), (data_account, "data")];
1550 read_accounts.sort_by(|a, b| a.0.cmp(&b.0));
1551
1552 for (i, (account, account_type)) in read_accounts.iter().enumerate() {
1553 let idx = (3 + i) as u16; match *account_type {
1555 "meta" => {
1556 meta_account_idx = idx;
1557 tx = tx.add_r_account(*account);
1558 }
1559 "data" => {
1560 data_account_idx = idx;
1561 tx = tx.add_r_account(*account);
1562 }
1563 _ => unreachable!(),
1564 }
1565 }
1566 }
1567
1568 let instruction_data = build_decompress2_instruction(
1569 target_account_idx,
1570 meta_account_idx,
1571 data_account_idx,
1572 data_offset,
1573 state_proof,
1574 )?;
1575
1576 tx = tx.with_instructions(instruction_data);
1577 Ok(tx)
1578 }
1579}
1580
1581fn build_uploader_create_instruction(
1583 buffer_account_idx: u16,
1584 meta_account_idx: u16,
1585 authority_account_idx: u16,
1586 buffer_size: u32,
1587 expected_hash: [u8; 32],
1588 seed: &[u8],
1589) -> Result<Vec<u8>> {
1590 let mut instruction = Vec::new();
1591
1592 instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_CREATE.to_le_bytes());
1594
1595 let args = UploaderCreateArgs {
1597 buffer_account_idx,
1598 meta_account_idx,
1599 authority_account_idx,
1600 buffer_account_sz: buffer_size,
1601 expected_account_hash: expected_hash,
1602 seed_len: seed.len() as u32,
1603 };
1604
1605 let args_bytes = unsafe {
1607 std::slice::from_raw_parts(
1608 &args as *const _ as *const u8,
1609 std::mem::size_of::<UploaderCreateArgs>(),
1610 )
1611 };
1612 instruction.extend_from_slice(args_bytes);
1613
1614 instruction.extend_from_slice(seed);
1616
1617 Ok(instruction)
1618}
1619
1620fn build_uploader_write_instruction(
1622 buffer_account_idx: u16,
1623 meta_account_idx: u16,
1624 data: &[u8],
1625 offset: u32,
1626) -> Result<Vec<u8>> {
1627 let mut instruction = Vec::new();
1628
1629 instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_WRITE.to_le_bytes());
1631
1632 let args = UploaderWriteArgs {
1634 buffer_account_idx,
1635 meta_account_idx,
1636 data_len: data.len() as u32,
1637 data_offset: offset,
1638 };
1639
1640 let args_bytes = unsafe {
1642 std::slice::from_raw_parts(
1643 &args as *const _ as *const u8,
1644 std::mem::size_of::<UploaderWriteArgs>(),
1645 )
1646 };
1647 instruction.extend_from_slice(args_bytes);
1648
1649 instruction.extend_from_slice(data);
1651
1652 Ok(instruction)
1653}
1654
1655fn build_uploader_finalize_instruction(
1657 buffer_account_idx: u16,
1658 meta_account_idx: u16,
1659 expected_hash: [u8; 32],
1660) -> Result<Vec<u8>> {
1661 let mut instruction = Vec::new();
1662
1663 instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_FINALIZE.to_le_bytes());
1665
1666 let args = UploaderFinalizeArgs {
1668 buffer_account_idx,
1669 meta_account_idx,
1670 expected_account_hash: expected_hash,
1671 };
1672
1673 let args_bytes = unsafe {
1675 std::slice::from_raw_parts(
1676 &args as *const _ as *const u8,
1677 std::mem::size_of::<UploaderFinalizeArgs>(),
1678 )
1679 };
1680 instruction.extend_from_slice(args_bytes);
1681
1682 Ok(instruction)
1683}
1684
1685fn build_uploader_destroy_instruction(
1687 buffer_account_idx: u16,
1688 meta_account_idx: u16,
1689) -> Result<Vec<u8>> {
1690 let mut instruction = Vec::new();
1691
1692 instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_DESTROY.to_le_bytes());
1694
1695 let args = UploaderDestroyArgs {
1697 buffer_account_idx,
1698 meta_account_idx,
1699 };
1700
1701 let args_bytes = unsafe {
1703 std::slice::from_raw_parts(
1704 &args as *const _ as *const u8,
1705 std::mem::size_of::<UploaderDestroyArgs>(),
1706 )
1707 };
1708 instruction.extend_from_slice(args_bytes);
1709
1710 Ok(instruction)
1711}
1712
1713fn build_manager_create_instruction(
1715 discriminant: u8,
1716 meta_account_idx: u16,
1717 program_account_idx: u16,
1718 srcbuf_account_idx: u16,
1719 authority_account_idx: u16,
1720 srcbuf_offset: u32,
1721 srcbuf_size: u32,
1722 seed: &[u8],
1723 proof: Option<&[u8]>,
1724) -> Result<Vec<u8>> {
1725 let mut instruction = Vec::new();
1726
1727 let args = ManagerCreateArgs {
1729 discriminant,
1730 meta_account_idx,
1731 program_account_idx,
1732 srcbuf_account_idx,
1733 srcbuf_offset,
1734 srcbuf_size,
1735 authority_account_idx,
1736 seed_len: seed.len() as u32,
1737 };
1738
1739 let args_bytes = unsafe {
1741 std::slice::from_raw_parts(
1742 &args as *const ManagerCreateArgs as *const u8,
1743 std::mem::size_of::<ManagerCreateArgs>(),
1744 )
1745 };
1746 instruction.extend_from_slice(args_bytes);
1747
1748 instruction.extend_from_slice(seed);
1750
1751 if let Some(proof_bytes) = proof {
1753 instruction.extend_from_slice(proof_bytes);
1754 }
1755
1756 Ok(instruction)
1757}
1758
1759fn build_manager_upgrade_instruction(
1761 meta_account_idx: u16,
1762 program_account_idx: u16,
1763 srcbuf_account_idx: u16,
1764 srcbuf_offset: u32,
1765 srcbuf_size: u32,
1766) -> Result<Vec<u8>> {
1767 let mut instruction = Vec::new();
1768
1769 let args = ManagerUpgradeArgs {
1770 discriminant: MANAGER_INSTRUCTION_UPGRADE,
1771 meta_account_idx,
1772 program_account_idx,
1773 srcbuf_account_idx,
1774 srcbuf_offset,
1775 srcbuf_size,
1776 };
1777
1778 let args_bytes = unsafe {
1779 std::slice::from_raw_parts(
1780 &args as *const ManagerUpgradeArgs as *const u8,
1781 std::mem::size_of::<ManagerUpgradeArgs>(),
1782 )
1783 };
1784 instruction.extend_from_slice(args_bytes);
1785
1786 Ok(instruction)
1787}
1788
1789fn build_manager_set_pause_instruction(
1791 meta_account_idx: u16,
1792 program_account_idx: u16,
1793 is_paused: bool,
1794) -> Result<Vec<u8>> {
1795 let mut instruction = Vec::new();
1796
1797 let args = ManagerSetPauseArgs {
1798 discriminant: MANAGER_INSTRUCTION_SET_PAUSE,
1799 meta_account_idx,
1800 program_account_idx,
1801 is_paused: if is_paused { 1 } else { 0 },
1802 };
1803
1804 let args_bytes = unsafe {
1805 std::slice::from_raw_parts(
1806 &args as *const ManagerSetPauseArgs as *const u8,
1807 std::mem::size_of::<ManagerSetPauseArgs>(),
1808 )
1809 };
1810 instruction.extend_from_slice(args_bytes);
1811
1812 Ok(instruction)
1813}
1814
1815fn build_manager_header_instruction(
1817 discriminant: u8,
1818 meta_account_idx: u16,
1819 program_account_idx: u16,
1820) -> Result<Vec<u8>> {
1821 let mut instruction = Vec::new();
1822
1823 let args = ManagerHeaderArgs {
1824 discriminant,
1825 meta_account_idx,
1826 program_account_idx,
1827 };
1828
1829 let args_bytes = unsafe {
1830 std::slice::from_raw_parts(
1831 &args as *const ManagerHeaderArgs as *const u8,
1832 std::mem::size_of::<ManagerHeaderArgs>(),
1833 )
1834 };
1835 instruction.extend_from_slice(args_bytes);
1836
1837 Ok(instruction)
1838}
1839
1840fn build_manager_set_authority_instruction(
1842 meta_account_idx: u16,
1843 program_account_idx: u16,
1844 authority_candidate: [u8; 32],
1845) -> Result<Vec<u8>> {
1846 let mut instruction = Vec::new();
1847
1848 let args = ManagerSetAuthorityArgs {
1849 discriminant: MANAGER_INSTRUCTION_SET_AUTHORITY,
1850 meta_account_idx,
1851 program_account_idx,
1852 authority_candidate,
1853 };
1854
1855 let args_bytes = unsafe {
1856 std::slice::from_raw_parts(
1857 &args as *const ManagerSetAuthorityArgs as *const u8,
1858 std::mem::size_of::<ManagerSetAuthorityArgs>(),
1859 )
1860 };
1861 instruction.extend_from_slice(args_bytes);
1862
1863 Ok(instruction)
1864}
1865
1866fn build_test_uploader_create_instruction(
1868 account_idx: u16,
1869 account_sz: u32,
1870 seed: &[u8],
1871 is_ephemeral: bool,
1872 state_proof: Option<&[u8]>,
1873) -> Result<Vec<u8>> {
1874 let mut instruction = Vec::new();
1875
1876 instruction.push(TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_CREATE);
1878
1879 let args = TestUploaderCreateArgs {
1881 account_idx,
1882 is_ephemeral: if is_ephemeral { 1u8 } else { 0u8 },
1883 account_sz,
1884 seed_len: seed.len() as u32,
1885 };
1886
1887 let args_bytes = unsafe {
1889 std::slice::from_raw_parts(
1890 &args as *const _ as *const u8,
1891 std::mem::size_of::<TestUploaderCreateArgs>(),
1892 )
1893 };
1894 instruction.extend_from_slice(args_bytes);
1895
1896 instruction.extend_from_slice(seed);
1898
1899 if let Some(proof) = state_proof {
1901 instruction.extend_from_slice(proof);
1902 }
1903
1904 Ok(instruction)
1905}
1906
1907fn build_test_uploader_write_instruction(
1909 target_account_idx: u16,
1910 target_offset: u32,
1911 data: &[u8],
1912) -> Result<Vec<u8>> {
1913 let mut instruction = Vec::new();
1914
1915 instruction.push(TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_WRITE);
1917
1918 let args = TestUploaderWriteArgs {
1920 target_account_idx,
1921 target_offset,
1922 data_len: data.len() as u32,
1923 };
1924
1925 let args_bytes = unsafe {
1927 std::slice::from_raw_parts(
1928 &args as *const _ as *const u8,
1929 std::mem::size_of::<TestUploaderWriteArgs>(),
1930 )
1931 };
1932 instruction.extend_from_slice(args_bytes);
1933
1934 instruction.extend_from_slice(data);
1936
1937 Ok(instruction)
1938}
1939
1940pub fn build_decompress2_instruction(
1942 target_account_idx: u16,
1943 meta_account_idx: u16,
1944 data_account_idx: u16,
1945 data_offset: u32,
1946 state_proof: &[u8],
1947) -> Result<Vec<u8>> {
1948 let mut instruction = Vec::new();
1949
1950 instruction.push(0x08);
1952
1953 let args = SystemProgramDecompress2Args {
1955 target_account_idx,
1956 meta_account_idx,
1957 data_account_idx,
1958 data_offset,
1959 };
1960
1961 let args_bytes = unsafe {
1963 std::slice::from_raw_parts(
1964 &args as *const _ as *const u8,
1965 std::mem::size_of::<SystemProgramDecompress2Args>(),
1966 )
1967 };
1968 instruction.extend_from_slice(args_bytes);
1969
1970 instruction.extend_from_slice(state_proof);
1972
1973 Ok(instruction)
1974}
1975
1976pub const TOKEN_INSTRUCTION_INITIALIZE_MINT: u8 = 0x00;
1978pub const TOKEN_INSTRUCTION_INITIALIZE_ACCOUNT: u8 = 0x01;
1979pub const TOKEN_INSTRUCTION_TRANSFER: u8 = 0x02;
1980pub const TOKEN_INSTRUCTION_MINT_TO: u8 = 0x03;
1981pub const TOKEN_INSTRUCTION_BURN: u8 = 0x04;
1982pub const TOKEN_INSTRUCTION_CLOSE_ACCOUNT: u8 = 0x05;
1983pub const TOKEN_INSTRUCTION_FREEZE_ACCOUNT: u8 = 0x06;
1984pub const TOKEN_INSTRUCTION_THAW_ACCOUNT: u8 = 0x07;
1985
1986pub const TN_WTHRU_INSTRUCTION_INITIALIZE_MINT: u32 = 0;
1988pub const TN_WTHRU_INSTRUCTION_DEPOSIT: u32 = 1;
1989pub const TN_WTHRU_INSTRUCTION_WITHDRAW: u32 = 2;
1990pub const TN_NAME_SERVICE_INSTRUCTION_INITIALIZE_ROOT: u32 = 0;
1996pub const TN_NAME_SERVICE_INSTRUCTION_REGISTER_SUBDOMAIN: u32 = 1;
1997pub const TN_NAME_SERVICE_INSTRUCTION_APPEND_RECORD: u32 = 2;
1998pub const TN_NAME_SERVICE_INSTRUCTION_DELETE_RECORD: u32 = 3;
1999pub const TN_NAME_SERVICE_INSTRUCTION_UNREGISTER: u32 = 4;
2000
2001pub const TN_NAME_SERVICE_PROOF_INLINE: u32 = 0;
2003
2004pub const TN_NAME_SERVICE_MAX_DOMAIN_LENGTH: usize = 64;
2006pub const TN_NAME_SERVICE_MAX_KEY_LENGTH: usize = 32;
2007pub const TN_NAME_SERVICE_MAX_VALUE_LENGTH: usize = 256;
2008
2009pub const TN_THRU_REGISTRAR_INSTRUCTION_INITIALIZE_REGISTRY: u32 = 0;
2011pub const TN_THRU_REGISTRAR_INSTRUCTION_PURCHASE_DOMAIN: u32 = 1;
2012pub const TN_THRU_REGISTRAR_INSTRUCTION_RENEW_LEASE: u32 = 2;
2013pub const TN_THRU_REGISTRAR_INSTRUCTION_CLAIM_EXPIRED_DOMAIN: u32 = 3;
2014
2015fn add_sorted_accounts(tx: Transaction, accounts: &[(TnPubkey, bool)]) -> (Transaction, Vec<u16>) {
2017 let mut rw_accounts: Vec<_> = accounts.iter().enumerate()
2019 .filter(|(_, (_, writable))| *writable)
2020 .collect();
2021 let mut ro_accounts: Vec<_> = accounts.iter().enumerate()
2022 .filter(|(_, (_, writable))| !*writable)
2023 .collect();
2024
2025 rw_accounts.sort_by(|a, b| a.1.0.cmp(&b.1.0));
2027 ro_accounts.sort_by(|a, b| a.1.0.cmp(&b.1.0));
2028
2029 let mut updated_tx = tx;
2030 let mut indices = vec![0u16; accounts.len()];
2031 let mut seen: HashMap<TnPubkey, u16> = HashMap::new();
2032 seen.insert(updated_tx.fee_payer, 0u16);
2033 seen.insert(updated_tx.program, 1u16);
2034
2035 let mut next_idx = 2u16;
2036
2037 for (i, (account, _)) in rw_accounts.iter() {
2039 if let Some(idx) = seen.get(account) {
2040 indices[*i] = *idx;
2041 continue;
2042 }
2043
2044 let account_idx = next_idx;
2045 next_idx = next_idx.saturating_add(1);
2046 seen.insert(*account, account_idx);
2047 indices[*i] = account_idx;
2048
2049 updated_tx = updated_tx.add_rw_account(*account);
2050 }
2051
2052 for (i, (account, _)) in ro_accounts.iter() {
2054 if let Some(idx) = seen.get(account) {
2055 indices[*i] = *idx;
2056 continue;
2057 }
2058
2059 let account_idx = next_idx;
2060 next_idx = next_idx.saturating_add(1);
2061 seen.insert(*account, account_idx);
2062 indices[*i] = account_idx;
2063
2064 updated_tx = updated_tx.add_r_account(*account);
2065 }
2066
2067 (updated_tx, indices)
2068}
2069
2070fn add_sorted_rw_accounts(mut tx: Transaction, accounts: &[TnPubkey]) -> (Transaction, Vec<u16>) {
2072 if accounts.is_empty() {
2073 return (tx, Vec::new());
2074 }
2075
2076 let mut sorted: Vec<(usize, TnPubkey)> = accounts.iter().cloned().enumerate().collect();
2077 sorted.sort_by(|a, b| a.1.cmp(&b.1));
2078
2079 let mut indices = vec![0u16; accounts.len()];
2080 for (pos, (orig_idx, account)) in sorted.into_iter().enumerate() {
2081 let idx = (2 + pos) as u16;
2082 indices[orig_idx] = idx;
2083 tx = tx.add_rw_account(account);
2084 }
2085
2086 (tx, indices)
2087}
2088
2089fn add_sorted_ro_accounts(
2090 mut tx: Transaction,
2091 base_idx: u16,
2092 accounts: &[TnPubkey],
2093) -> (Transaction, Vec<u16>) {
2094 if accounts.is_empty() {
2095 return (tx, Vec::new());
2096 }
2097
2098 let mut sorted: Vec<(usize, TnPubkey)> = accounts.iter().cloned().enumerate().collect();
2099 sorted.sort_by(|a, b| a.1.cmp(&b.1));
2100
2101 let mut indices = vec![0u16; accounts.len()];
2102 for (pos, (orig_idx, account)) in sorted.into_iter().enumerate() {
2103 let idx = base_idx + pos as u16;
2104 indices[orig_idx] = idx;
2105 tx = tx.add_r_account(account);
2106 }
2107
2108 (tx, indices)
2109}
2110
2111impl TransactionBuilder {
2112 pub fn build_token_initialize_mint(
2114 fee_payer: TnPubkey,
2115 token_program: TnPubkey,
2116 mint_account: TnPubkey,
2117 creator: TnPubkey,
2118 mint_authority: TnPubkey,
2119 freeze_authority: Option<TnPubkey>,
2120 decimals: u8,
2121 ticker: &str,
2122 seed: [u8; 32],
2123 state_proof: Vec<u8>,
2124 fee: u64,
2125 nonce: u64,
2126 start_slot: u64,
2127 ) -> Result<Transaction> {
2128 let base_tx =
2129 Transaction::new(fee_payer, token_program, fee, nonce).with_start_slot(start_slot);
2130 let (tx, indices) = add_sorted_rw_accounts(base_tx, &[mint_account]);
2131 let mint_account_idx = indices[0];
2132
2133 let instruction_data = build_token_initialize_mint_instruction(
2134 mint_account_idx,
2135 decimals,
2136 creator,
2137 mint_authority,
2138 freeze_authority,
2139 ticker,
2140 seed,
2141 state_proof,
2142 )?;
2143
2144 let tx = tx
2145 .with_instructions(instruction_data)
2146 .with_expiry_after(100)
2147 .with_compute_units(300_000)
2148 .with_state_units(10_000)
2149 .with_memory_units(10_000);
2150
2151 Ok(tx)
2152 }
2153
2154 pub fn build_token_initialize_account(
2156 fee_payer: TnPubkey,
2157 token_program: TnPubkey,
2158 token_account: TnPubkey,
2159 mint_account: TnPubkey,
2160 owner: TnPubkey,
2161 seed: [u8; 32],
2162 state_proof: Vec<u8>,
2163 fee: u64,
2164 nonce: u64,
2165 start_slot: u64,
2166 ) -> Result<Transaction> {
2167 let owner_is_fee_payer = owner == fee_payer;
2168
2169 let mut rw_accounts = vec![token_account];
2170 rw_accounts.sort();
2171
2172 let mut ro_accounts = vec![mint_account];
2173 if !owner_is_fee_payer {
2174 ro_accounts.push(owner);
2175 ro_accounts.sort();
2176 }
2177
2178 let mut tx =
2179 Transaction::new(fee_payer, token_program, fee, nonce).with_start_slot(start_slot);
2180
2181 let mut token_account_idx = 0u16;
2182 for (i, account) in rw_accounts.iter().enumerate() {
2183 let idx = (2 + i) as u16;
2184 if *account == token_account {
2185 token_account_idx = idx;
2186 }
2187 tx = tx.add_rw_account(*account);
2188 }
2189
2190 let base_ro_idx = 2 + rw_accounts.len() as u16;
2191 let mut mint_account_idx = 0u16;
2192 let mut owner_account_idx = if owner_is_fee_payer { 0u16 } else { 0u16 };
2193 for (i, account) in ro_accounts.iter().enumerate() {
2194 let idx = base_ro_idx + i as u16;
2195 if *account == mint_account {
2196 mint_account_idx = idx;
2197 } else if !owner_is_fee_payer && *account == owner {
2198 owner_account_idx = idx;
2199 }
2200 tx = tx.add_r_account(*account);
2201 }
2202
2203 let instruction_data = build_token_initialize_account_instruction(
2204 token_account_idx,
2205 mint_account_idx,
2206 owner_account_idx,
2207 seed,
2208 state_proof,
2209 )?;
2210
2211 let tx = tx
2212 .with_instructions(instruction_data)
2213 .with_expiry_after(100)
2214 .with_compute_units(300_000)
2215 .with_state_units(10_000)
2216 .with_memory_units(10_000);
2217
2218 Ok(tx)
2219 }
2220
2221 pub fn build_token_transfer(
2223 fee_payer: TnPubkey,
2224 token_program: TnPubkey,
2225 source_account: TnPubkey,
2226 dest_account: TnPubkey,
2227 _authority: TnPubkey,
2228 amount: u64,
2229 fee: u64,
2230 nonce: u64,
2231 start_slot: u64,
2232 ) -> Result<Transaction> {
2233 let mut tx = Transaction::new(fee_payer, token_program, fee, nonce)
2234 .with_start_slot(start_slot)
2235 .with_expiry_after(100)
2236 .with_compute_units(300_000)
2237 .with_state_units(10_000)
2238 .with_memory_units(10_000);
2239
2240 let is_self_transfer = source_account == dest_account;
2241 let (source_account_idx, dest_account_idx) = if is_self_transfer {
2242 tx = tx.add_rw_account(source_account);
2244 (2u16, 2u16)
2245 } else {
2246 let accounts = &[(source_account, true), (dest_account, true)];
2248 let (updated_tx, indices) = add_sorted_accounts(tx, accounts);
2249 tx = updated_tx;
2250 (indices[0], indices[1])
2251 };
2252
2253 let instruction_data =
2257 build_token_transfer_instruction(source_account_idx, dest_account_idx, amount)?;
2258
2259 Ok(tx.with_instructions(instruction_data))
2260 }
2261
2262 pub fn build_token_mint_to(
2264 fee_payer: TnPubkey,
2265 token_program: TnPubkey,
2266 mint_account: TnPubkey,
2267 dest_account: TnPubkey,
2268 authority: TnPubkey,
2269 amount: u64,
2270 fee: u64,
2271 nonce: u64,
2272 start_slot: u64,
2273 ) -> Result<Transaction> {
2274 let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
2275 .with_start_slot(start_slot)
2276 .with_expiry_after(100)
2277 .with_compute_units(300_000)
2278 .with_state_units(10_000)
2279 .with_memory_units(10_000);
2280
2281 let (tx_after_rw, rw_indices) =
2282 add_sorted_rw_accounts(base_tx, &[mint_account, dest_account]);
2283 let mint_account_idx = rw_indices[0];
2284 let dest_account_idx = rw_indices[1];
2285
2286 let mut tx = tx_after_rw;
2287 let authority_account_idx = if authority == fee_payer {
2288 0u16
2289 } else {
2290 let base_ro_idx = 2 + rw_indices.len() as u16;
2291 let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
2292 tx = tx_after_ro;
2293 ro_indices[0]
2294 };
2295
2296 let instruction_data = build_token_mint_to_instruction(
2297 mint_account_idx,
2298 dest_account_idx,
2299 authority_account_idx,
2300 amount,
2301 )?;
2302
2303 Ok(tx.with_instructions(instruction_data))
2304 }
2305
2306 pub fn build_token_burn(
2308 fee_payer: TnPubkey,
2309 token_program: TnPubkey,
2310 token_account: TnPubkey,
2311 mint_account: TnPubkey,
2312 authority: TnPubkey,
2313 amount: u64,
2314 fee: u64,
2315 nonce: u64,
2316 start_slot: u64,
2317 ) -> Result<Transaction> {
2318 let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
2319 .with_start_slot(start_slot)
2320 .with_expiry_after(100)
2321 .with_compute_units(300_000)
2322 .with_state_units(10_000)
2323 .with_memory_units(10_000);
2324
2325 let (tx_after_rw, rw_indices) =
2326 add_sorted_rw_accounts(base_tx, &[token_account, mint_account]);
2327 let token_account_idx = rw_indices[0];
2328 let mint_account_idx = rw_indices[1];
2329
2330 let mut tx = tx_after_rw;
2331 let authority_account_idx = if authority == fee_payer {
2332 0u16
2333 } else {
2334 let base_ro_idx = 2 + rw_indices.len() as u16;
2335 let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
2336 tx = tx_after_ro;
2337 ro_indices[0]
2338 };
2339
2340 let instruction_data = build_token_burn_instruction(
2341 token_account_idx,
2342 mint_account_idx,
2343 authority_account_idx,
2344 amount,
2345 )?;
2346
2347 Ok(tx.with_instructions(instruction_data))
2348 }
2349
2350 pub fn build_token_freeze_account(
2352 fee_payer: TnPubkey,
2353 token_program: TnPubkey,
2354 token_account: TnPubkey,
2355 mint_account: TnPubkey,
2356 authority: TnPubkey,
2357 fee: u64,
2358 nonce: u64,
2359 start_slot: u64,
2360 ) -> Result<Transaction> {
2361 let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
2362 .with_start_slot(start_slot)
2363 .with_expiry_after(100)
2364 .with_compute_units(300_000)
2365 .with_state_units(10_000)
2366 .with_memory_units(10_000);
2367
2368 let (tx_after_rw, rw_indices) =
2369 add_sorted_rw_accounts(base_tx, &[token_account, mint_account]);
2370 let token_account_idx = rw_indices[0];
2371 let mint_account_idx = rw_indices[1];
2372
2373 let mut tx = tx_after_rw;
2374 let authority_account_idx = if authority == fee_payer {
2375 0u16
2376 } else {
2377 let base_ro_idx = 2 + rw_indices.len() as u16;
2378 let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
2379 tx = tx_after_ro;
2380 ro_indices[0]
2381 };
2382
2383 let instruction_data = build_token_freeze_account_instruction(
2384 token_account_idx,
2385 mint_account_idx,
2386 authority_account_idx,
2387 )?;
2388
2389 Ok(tx.with_instructions(instruction_data))
2390 }
2391
2392 pub fn build_token_thaw_account(
2394 fee_payer: TnPubkey,
2395 token_program: TnPubkey,
2396 token_account: TnPubkey,
2397 mint_account: TnPubkey,
2398 authority: TnPubkey,
2399 fee: u64,
2400 nonce: u64,
2401 start_slot: u64,
2402 ) -> Result<Transaction> {
2403 let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
2404 .with_start_slot(start_slot)
2405 .with_expiry_after(100)
2406 .with_compute_units(300_000)
2407 .with_state_units(10_000)
2408 .with_memory_units(10_000);
2409
2410 let (tx_after_rw, rw_indices) =
2411 add_sorted_rw_accounts(base_tx, &[token_account, mint_account]);
2412 let token_account_idx = rw_indices[0];
2413 let mint_account_idx = rw_indices[1];
2414
2415 let mut tx = tx_after_rw;
2416 let authority_account_idx = if authority == fee_payer {
2417 0u16
2418 } else {
2419 let base_ro_idx = 2 + rw_indices.len() as u16;
2420 let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
2421 tx = tx_after_ro;
2422 ro_indices[0]
2423 };
2424
2425 let instruction_data = build_token_thaw_account_instruction(
2426 token_account_idx,
2427 mint_account_idx,
2428 authority_account_idx,
2429 )?;
2430
2431 Ok(tx.with_instructions(instruction_data))
2432 }
2433
2434 pub fn build_token_close_account(
2436 fee_payer: TnPubkey,
2437 token_program: TnPubkey,
2438 token_account: TnPubkey,
2439 destination: TnPubkey,
2440 authority: TnPubkey,
2441 fee: u64,
2442 nonce: u64,
2443 start_slot: u64,
2444 ) -> Result<Transaction> {
2445 let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
2446 .with_start_slot(start_slot)
2447 .with_expiry_after(100)
2448 .with_compute_units(300_000)
2449 .with_state_units(10_000)
2450 .with_memory_units(10_000);
2451
2452 let mut rw_accounts = vec![token_account];
2453 let destination_in_accounts = destination != fee_payer;
2454 if destination_in_accounts {
2455 rw_accounts.push(destination);
2456 }
2457
2458 let (tx_after_rw, rw_indices) = add_sorted_rw_accounts(base_tx, &rw_accounts);
2459 let token_account_idx = rw_indices[0];
2460 let destination_idx = if destination_in_accounts {
2461 rw_indices[1]
2462 } else {
2463 0u16
2464 };
2465
2466 let mut tx = tx_after_rw;
2467 let authority_account_idx = if authority == fee_payer {
2468 0u16
2469 } else {
2470 let base_ro_idx = 2 + rw_indices.len() as u16;
2471 let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
2472 tx = tx_after_ro;
2473 ro_indices[0]
2474 };
2475
2476 let instruction_data = build_token_close_account_instruction(
2477 token_account_idx,
2478 destination_idx,
2479 authority_account_idx,
2480 )?;
2481
2482 Ok(tx.with_instructions(instruction_data))
2483 }
2484
2485 pub fn build_wthru_initialize_mint(
2487 fee_payer: TnPubkey,
2488 wthru_program: TnPubkey,
2489 token_program: TnPubkey,
2490 mint_account: TnPubkey,
2491 vault_account: TnPubkey,
2492 decimals: u8,
2493 mint_seed: [u8; 32],
2494 mint_proof: Vec<u8>,
2495 vault_proof: Vec<u8>,
2496 fee: u64,
2497 nonce: u64,
2498 start_slot: u64,
2499 ) -> Result<Transaction> {
2500 let mut tx = Transaction::new(fee_payer, wthru_program, fee, nonce)
2501 .with_start_slot(start_slot)
2502 .with_expiry_after(100)
2503 .with_compute_units(500_000)
2504 .with_state_units(10_000)
2505 .with_memory_units(10_000);
2506
2507 let accounts = [
2508 (mint_account, true),
2509 (vault_account, true),
2510 (token_program, false),
2511 ];
2512
2513 let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
2514 tx = tx_with_accounts;
2515
2516 let mint_account_idx = indices[0];
2517 let vault_account_idx = indices[1];
2518 let token_program_idx = indices[2];
2519
2520 let instruction_data = build_wthru_initialize_mint_instruction(
2521 token_program_idx,
2522 mint_account_idx,
2523 vault_account_idx,
2524 decimals,
2525 mint_seed,
2526 mint_proof,
2527 vault_proof,
2528 )?;
2529
2530 Ok(tx.with_instructions(instruction_data))
2531 }
2532
2533 pub fn build_wthru_deposit(
2535 fee_payer: TnPubkey,
2536 wthru_program: TnPubkey,
2537 token_program: TnPubkey,
2538 mint_account: TnPubkey,
2539 vault_account: TnPubkey,
2540 dest_token_account: TnPubkey,
2541 fee: u64,
2542 nonce: u64,
2543 start_slot: u64,
2544 ) -> Result<Transaction> {
2545 let mut tx = Transaction::new(fee_payer, wthru_program, fee, nonce)
2546 .with_start_slot(start_slot)
2547 .with_expiry_after(100)
2548 .with_compute_units(400_000)
2549 .with_state_units(10_000)
2550 .with_memory_units(10_000);
2551
2552 let accounts = [
2553 (mint_account, true),
2554 (vault_account, true),
2555 (dest_token_account, true),
2556 (token_program, false),
2557 ];
2558
2559 let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
2560 tx = tx_with_accounts;
2561
2562 let mint_account_idx = indices[0];
2563 let vault_account_idx = indices[1];
2564 let dest_account_idx = indices[2];
2565 let token_program_idx = indices[3];
2566
2567 let instruction_data = build_wthru_deposit_instruction(
2568 token_program_idx,
2569 vault_account_idx,
2570 mint_account_idx,
2571 dest_account_idx,
2572 )?;
2573
2574 Ok(tx.with_instructions(instruction_data))
2575 }
2576
2577 pub fn build_wthru_withdraw(
2579 fee_payer: TnPubkey,
2580 wthru_program: TnPubkey,
2581 token_program: TnPubkey,
2582 mint_account: TnPubkey,
2583 vault_account: TnPubkey,
2584 wthru_token_account: TnPubkey,
2585 recipient_account: TnPubkey,
2586 amount: u64,
2587 fee: u64,
2588 nonce: u64,
2589 start_slot: u64,
2590 ) -> Result<Transaction> {
2591 let mut tx = Transaction::new(fee_payer, wthru_program, fee, nonce)
2592 .with_start_slot(start_slot)
2593 .with_expiry_after(100)
2594 .with_compute_units(400_000)
2595 .with_state_units(10_000)
2596 .with_memory_units(10_000);
2597
2598 let accounts = [
2599 (mint_account, true),
2600 (vault_account, true),
2601 (wthru_token_account, true),
2602 (recipient_account, true),
2603 (token_program, false),
2604 ];
2605
2606 let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
2607 tx = tx_with_accounts;
2608
2609 let mint_account_idx = indices[0];
2610 let vault_account_idx = indices[1];
2611 let token_account_idx = indices[2];
2612 let recipient_account_idx = indices[3];
2613 let token_program_idx = indices[4];
2614
2615 let owner_account_idx = 0u16; let instruction_data = build_wthru_withdraw_instruction(
2618 token_program_idx,
2619 vault_account_idx,
2620 mint_account_idx,
2621 token_account_idx,
2622 owner_account_idx,
2623 recipient_account_idx,
2624 amount,
2625 )?;
2626
2627 Ok(tx.with_instructions(instruction_data))
2628 }
2629
2630 pub fn build_faucet_deposit(
2633 fee_payer: TnPubkey,
2634 faucet_program: TnPubkey,
2635 faucet_account: TnPubkey,
2636 depositor_account: TnPubkey,
2637 eoa_program: TnPubkey,
2638 amount: u64,
2639 fee: u64,
2640 nonce: u64,
2641 start_slot: u64,
2642 ) -> Result<Transaction> {
2643 let tx = Transaction::new(fee_payer, faucet_program, fee, nonce)
2644 .with_start_slot(start_slot)
2645 .with_expiry_after(100)
2646 .with_compute_units(300_000)
2647 .with_state_units(10_000)
2648 .with_memory_units(10_000);
2649
2650 let (tx, depositor_account_idx) = Self::ensure_rw_account(tx, depositor_account);
2651 let (tx, faucet_account_idx) = Self::ensure_rw_account(tx, faucet_account);
2652 let (tx, eoa_program_idx) = Self::ensure_ro_account(tx, eoa_program);
2653
2654 let instruction_data = build_faucet_deposit_instruction(
2655 faucet_account_idx,
2656 depositor_account_idx,
2657 eoa_program_idx,
2658 amount,
2659 )?;
2660
2661 Ok(tx.with_instructions(instruction_data))
2662 }
2663
2664 fn resolve_account_index(tx: &Transaction, target: &TnPubkey) -> Option<u16> {
2665 if *target == tx.fee_payer {
2666 return Some(0u16);
2667 }
2668
2669 if *target == tx.program {
2670 return Some(1u16);
2671 }
2672
2673 if let Some(ref rw) = tx.rw_accs {
2674 if let Some(pos) = rw.iter().position(|acc| acc == target) {
2675 return Some(2u16 + pos as u16);
2676 }
2677 }
2678
2679 if let Some(ref ro) = tx.r_accs {
2680 let base = 2u16 + tx.rw_accs.as_ref().map_or(0u16, |v| v.len() as u16);
2681 if let Some(pos) = ro.iter().position(|acc| acc == target) {
2682 return Some(base + pos as u16);
2683 }
2684 }
2685
2686 None
2687 }
2688
2689 fn ensure_rw_account(mut tx: Transaction, account: TnPubkey) -> (Transaction, u16) {
2690 if let Some(idx) = Self::resolve_account_index(&tx, &account) {
2691 return (tx, idx);
2692 }
2693
2694 tx = tx.add_rw_account(account);
2695 let idx = Self::resolve_account_index(&tx, &account)
2696 .expect("read-write account index should exist after insertion");
2697
2698 (tx, idx)
2699 }
2700
2701 fn ensure_ro_account(mut tx: Transaction, account: TnPubkey) -> (Transaction, u16) {
2702 if let Some(idx) = Self::resolve_account_index(&tx, &account) {
2703 return (tx, idx);
2704 }
2705
2706 tx = tx.add_r_account(account);
2707 let idx = Self::resolve_account_index(&tx, &account)
2708 .expect("read-only account index should exist after insertion");
2709
2710 (tx, idx)
2711 }
2712
2713 pub fn build_faucet_withdraw(
2715 fee_payer: TnPubkey,
2716 faucet_program: TnPubkey,
2717 faucet_account: TnPubkey,
2718 recipient_account: TnPubkey,
2719 amount: u64,
2720 fee: u64,
2721 nonce: u64,
2722 start_slot: u64,
2723 ) -> Result<Transaction> {
2724 let mut tx = Transaction::new(fee_payer, faucet_program, fee, nonce)
2725 .with_start_slot(start_slot)
2726 .with_expiry_after(100)
2727 .with_compute_units(300_000)
2728 .with_state_units(10_000)
2729 .with_memory_units(10_000);
2730
2731 let faucet_is_fee_payer = faucet_account == fee_payer;
2734 let recipient_is_fee_payer = recipient_account == fee_payer;
2735 let recipient_is_faucet = recipient_account == faucet_account;
2736
2737 let (faucet_account_idx, recipient_account_idx) = if faucet_is_fee_payer && recipient_is_fee_payer {
2738 (0u16, 0u16)
2740 } else if faucet_is_fee_payer {
2741 tx = tx.add_rw_account(recipient_account);
2743 (0u16, 2u16)
2744 } else if recipient_is_fee_payer {
2745 tx = tx.add_rw_account(faucet_account);
2747 (2u16, 0u16)
2748 } else if recipient_is_faucet {
2749 tx = tx.add_rw_account(faucet_account);
2751 (2u16, 2u16)
2752 } else {
2753 if faucet_account < recipient_account {
2755 tx = tx.add_rw_account(faucet_account);
2756 tx = tx.add_rw_account(recipient_account);
2757 (2u16, 3u16)
2758 } else {
2759 tx = tx.add_rw_account(recipient_account);
2760 tx = tx.add_rw_account(faucet_account);
2761 (3u16, 2u16)
2762 }
2763 };
2764
2765 let instruction_data = build_faucet_withdraw_instruction(
2766 faucet_account_idx,
2767 recipient_account_idx,
2768 amount,
2769 )?;
2770
2771 Ok(tx.with_instructions(instruction_data))
2772 }
2773
2774 pub fn build_name_service_initialize_root(
2776 fee_payer: TnPubkey,
2777 name_service_program: TnPubkey,
2778 registrar_account: TnPubkey,
2779 authority_account: TnPubkey,
2780 root_name: &str,
2781 state_proof: Vec<u8>,
2782 fee: u64,
2783 nonce: u64,
2784 start_slot: u64,
2785 ) -> Result<Transaction> {
2786 let mut tx = Transaction::new(fee_payer, name_service_program, fee, nonce)
2787 .with_start_slot(start_slot)
2788 .with_expiry_after(100)
2789 .with_compute_units(500_000)
2790 .with_state_units(10_000)
2791 .with_memory_units(10_000);
2792
2793 let accounts = [(registrar_account, true), (authority_account, false)];
2794 let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
2795 tx = tx_with_accounts;
2796
2797 let registrar_account_idx = indices[0];
2798 let authority_account_idx = indices[1];
2799
2800 let instruction_data = build_name_service_initialize_root_instruction(
2801 registrar_account_idx,
2802 authority_account_idx,
2803 root_name,
2804 state_proof,
2805 )?;
2806
2807 Ok(tx.with_instructions(instruction_data))
2808 }
2809
2810 pub fn build_name_service_register_subdomain(
2812 fee_payer: TnPubkey,
2813 name_service_program: TnPubkey,
2814 domain_account: TnPubkey,
2815 parent_account: TnPubkey,
2816 owner_account: TnPubkey,
2817 authority_account: TnPubkey,
2818 domain_name: &str,
2819 state_proof: Vec<u8>,
2820 fee: u64,
2821 nonce: u64,
2822 start_slot: u64,
2823 ) -> Result<Transaction> {
2824 let mut tx = Transaction::new(fee_payer, name_service_program, fee, nonce)
2825 .with_start_slot(start_slot)
2826 .with_expiry_after(100)
2827 .with_compute_units(500_000)
2828 .with_state_units(10_000)
2829 .with_memory_units(10_000);
2830
2831 let accounts = [
2832 (domain_account, true),
2833 (parent_account, true),
2834 (owner_account, false),
2835 (authority_account, false),
2836 ];
2837 let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
2838 tx = tx_with_accounts;
2839
2840 let domain_account_idx = indices[0];
2841 let parent_account_idx = indices[1];
2842 let owner_account_idx = indices[2];
2843 let authority_account_idx = indices[3];
2844
2845 let instruction_data = build_name_service_register_subdomain_instruction(
2846 domain_account_idx,
2847 parent_account_idx,
2848 owner_account_idx,
2849 authority_account_idx,
2850 domain_name,
2851 state_proof,
2852 )?;
2853
2854 Ok(tx.with_instructions(instruction_data))
2855 }
2856
2857 pub fn build_name_service_append_record(
2859 fee_payer: TnPubkey,
2860 name_service_program: TnPubkey,
2861 domain_account: TnPubkey,
2862 owner_account: TnPubkey,
2863 key: &[u8],
2864 value: &[u8],
2865 fee: u64,
2866 nonce: u64,
2867 start_slot: u64,
2868 ) -> Result<Transaction> {
2869 let mut tx = Transaction::new(fee_payer, name_service_program, fee, nonce)
2870 .with_start_slot(start_slot)
2871 .with_expiry_after(100)
2872 .with_compute_units(250_000)
2873 .with_state_units(10_000)
2874 .with_memory_units(10_000);
2875
2876 let accounts = [(domain_account, true), (owner_account, false)];
2877 let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
2878 tx = tx_with_accounts;
2879
2880 let domain_account_idx = indices[0];
2881 let owner_account_idx = indices[1];
2882
2883 let instruction_data = build_name_service_append_record_instruction(
2884 domain_account_idx,
2885 owner_account_idx,
2886 key,
2887 value,
2888 )?;
2889
2890 Ok(tx.with_instructions(instruction_data))
2891 }
2892
2893 pub fn build_name_service_delete_record(
2895 fee_payer: TnPubkey,
2896 name_service_program: TnPubkey,
2897 domain_account: TnPubkey,
2898 owner_account: TnPubkey,
2899 key: &[u8],
2900 fee: u64,
2901 nonce: u64,
2902 start_slot: u64,
2903 ) -> Result<Transaction> {
2904 let mut tx = Transaction::new(fee_payer, name_service_program, fee, nonce)
2905 .with_start_slot(start_slot)
2906 .with_expiry_after(100)
2907 .with_compute_units(200_000)
2908 .with_state_units(10_000)
2909 .with_memory_units(10_000);
2910
2911 let accounts = [(domain_account, true), (owner_account, false)];
2912 let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
2913 tx = tx_with_accounts;
2914
2915 let domain_account_idx = indices[0];
2916 let owner_account_idx = indices[1];
2917
2918 let instruction_data = build_name_service_delete_record_instruction(
2919 domain_account_idx,
2920 owner_account_idx,
2921 key,
2922 )?;
2923
2924 Ok(tx.with_instructions(instruction_data))
2925 }
2926
2927 pub fn build_name_service_unregister_subdomain(
2929 fee_payer: TnPubkey,
2930 name_service_program: TnPubkey,
2931 domain_account: TnPubkey,
2932 owner_account: TnPubkey,
2933 fee: u64,
2934 nonce: u64,
2935 start_slot: u64,
2936 ) -> Result<Transaction> {
2937 let mut tx = Transaction::new(fee_payer, name_service_program, fee, nonce)
2938 .with_start_slot(start_slot)
2939 .with_expiry_after(100)
2940 .with_compute_units(200_000)
2941 .with_state_units(10_000)
2942 .with_memory_units(10_000);
2943
2944 let accounts = [(domain_account, true), (owner_account, false)];
2945 let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
2946 tx = tx_with_accounts;
2947
2948 let domain_account_idx = indices[0];
2949 let owner_account_idx = indices[1];
2950
2951 let instruction_data = build_name_service_unregister_subdomain_instruction(
2952 domain_account_idx,
2953 owner_account_idx,
2954 )?;
2955
2956 Ok(tx.with_instructions(instruction_data))
2957 }
2958
2959 pub fn build_thru_registrar_initialize_registry(
2961 fee_payer: TnPubkey,
2962 thru_registrar_program: TnPubkey,
2963 config_account: TnPubkey,
2964 name_service_program: TnPubkey,
2965 root_registrar_account: TnPubkey,
2966 treasurer_account: TnPubkey,
2967 token_mint_account: TnPubkey,
2968 token_program: TnPubkey,
2969 root_domain_name: &str,
2970 price_per_year: u64,
2971 config_proof: Vec<u8>,
2972 registrar_proof: Vec<u8>,
2973 fee: u64,
2974 nonce: u64,
2975 start_slot: u64,
2976 ) -> Result<Transaction> {
2977 let mut tx = Transaction::new(fee_payer, thru_registrar_program, fee, nonce)
2978 .with_start_slot(start_slot)
2979 .with_expiry_after(100)
2980 .with_compute_units(500_000)
2981 .with_state_units(10_000)
2982 .with_memory_units(10_000);
2983
2984 let mut rw_accounts = vec![config_account, root_registrar_account];
2987 rw_accounts.sort();
2988
2989 let mut ro_accounts = vec![
2990 name_service_program,
2991 treasurer_account,
2992 token_mint_account,
2993 token_program,
2994 ];
2995 ro_accounts.sort();
2996
2997 let mut config_account_idx = 0u16;
2999 let mut root_registrar_account_idx = 0u16;
3000 for (i, account) in rw_accounts.iter().enumerate() {
3001 let idx = (2 + i) as u16;
3002 if *account == config_account {
3003 config_account_idx = idx;
3004 } else if *account == root_registrar_account {
3005 root_registrar_account_idx = idx;
3006 }
3007 tx = tx.add_rw_account(*account);
3008 }
3009
3010 let base_ro_idx = 2 + rw_accounts.len() as u16;
3012 let mut name_service_program_idx = 0u16;
3013 let mut treasurer_account_idx = 0u16;
3014 let mut token_mint_account_idx = 0u16;
3015 let mut token_program_idx = 0u16;
3016
3017 for (i, account) in ro_accounts.iter().enumerate() {
3018 let idx = base_ro_idx + i as u16;
3019 if *account == name_service_program {
3020 name_service_program_idx = idx;
3021 } else if *account == root_registrar_account {
3022 root_registrar_account_idx = idx;
3023 } else if *account == treasurer_account {
3024 treasurer_account_idx = idx;
3025 } else if *account == token_mint_account {
3026 token_mint_account_idx = idx;
3027 } else if *account == token_program {
3028 token_program_idx = idx;
3029 }
3030 tx = tx.add_r_account(*account);
3031 }
3032
3033 let instruction_data = build_thru_registrar_initialize_registry_instruction(
3034 config_account_idx,
3035 name_service_program_idx,
3036 root_registrar_account_idx,
3037 treasurer_account_idx,
3038 token_mint_account_idx,
3039 token_program_idx,
3040 root_domain_name,
3041 price_per_year,
3042 config_proof,
3043 registrar_proof,
3044 )?;
3045
3046 Ok(tx.with_instructions(instruction_data))
3047 }
3048
3049 pub fn build_thru_registrar_purchase_domain(
3051 fee_payer: TnPubkey,
3052 thru_registrar_program: TnPubkey,
3053 config_account: TnPubkey,
3054 lease_account: TnPubkey,
3055 domain_account: TnPubkey,
3056 name_service_program: TnPubkey,
3057 root_registrar_account: TnPubkey,
3058 treasurer_account: TnPubkey,
3059 payer_token_account: TnPubkey,
3060 token_mint_account: TnPubkey,
3061 token_program: TnPubkey,
3062 domain_name: &str,
3063 years: u8,
3064 lease_proof: Vec<u8>,
3065 domain_proof: Vec<u8>,
3066 fee: u64,
3067 nonce: u64,
3068 start_slot: u64,
3069 ) -> Result<Transaction> {
3070 let mut tx = Transaction::new(fee_payer, thru_registrar_program, fee, nonce)
3071 .with_start_slot(start_slot)
3072 .with_expiry_after(100)
3073 .with_compute_units(500_000)
3074 .with_state_units(10_000)
3075 .with_memory_units(10_000);
3076
3077 let mut rw_accounts = vec![
3080 config_account,
3081 lease_account,
3082 domain_account,
3083 treasurer_account,
3084 payer_token_account,
3085 root_registrar_account,
3086 ];
3087 rw_accounts.sort();
3088
3089 let mut ro_accounts = vec![
3090 name_service_program,
3091 token_mint_account,
3092 token_program,
3093 ];
3094 ro_accounts.sort();
3095
3096 let mut config_account_idx = 0u16;
3098 let mut lease_account_idx = 0u16;
3099 let mut domain_account_idx = 0u16;
3100 let mut treasurer_account_idx = 0u16;
3101 let mut payer_token_account_idx = 0u16;
3102 let mut root_registrar_account_idx = 0u16;
3103 for (i, account) in rw_accounts.iter().enumerate() {
3104 let idx = (2 + i) as u16;
3105 if *account == config_account {
3106 config_account_idx = idx;
3107 } else if *account == lease_account {
3108 lease_account_idx = idx;
3109 } else if *account == domain_account {
3110 domain_account_idx = idx;
3111 } else if *account == treasurer_account {
3112 treasurer_account_idx = idx;
3113 } else if *account == payer_token_account {
3114 payer_token_account_idx = idx;
3115 } else if *account == root_registrar_account {
3116 root_registrar_account_idx = idx;
3117 }
3118 tx = tx.add_rw_account(*account);
3119 }
3120
3121 let base_ro_idx = 2 + rw_accounts.len() as u16;
3123 let mut name_service_program_idx = 0u16;
3124 let mut token_mint_account_idx = 0u16;
3125 let mut token_program_idx = 0u16;
3126
3127 for (i, account) in ro_accounts.iter().enumerate() {
3128 let idx = base_ro_idx + i as u16;
3129 if *account == config_account {
3130 config_account_idx = idx; } else if *account == name_service_program {
3132 name_service_program_idx = idx;
3133 } else if *account == root_registrar_account {
3134 root_registrar_account_idx = idx;
3135 } else if *account == treasurer_account {
3136 treasurer_account_idx = idx;
3137 } else if *account == payer_token_account {
3138 payer_token_account_idx = idx;
3139 } else if *account == token_mint_account {
3140 token_mint_account_idx = idx;
3141 } else if *account == token_program {
3142 token_program_idx = idx;
3143 }
3144 tx = tx.add_r_account(*account);
3145 }
3146
3147 let instruction_data = build_thru_registrar_purchase_domain_instruction(
3148 config_account_idx,
3149 lease_account_idx,
3150 domain_account_idx,
3151 name_service_program_idx,
3152 root_registrar_account_idx,
3153 treasurer_account_idx,
3154 payer_token_account_idx,
3155 token_mint_account_idx,
3156 token_program_idx,
3157 domain_name,
3158 years,
3159 lease_proof,
3160 domain_proof,
3161 )?;
3162
3163 Ok(tx.with_instructions(instruction_data))
3164 }
3165
3166 pub fn build_thru_registrar_renew_lease(
3168 fee_payer: TnPubkey,
3169 thru_registrar_program: TnPubkey,
3170 config_account: TnPubkey,
3171 lease_account: TnPubkey,
3172 treasurer_account: TnPubkey,
3173 payer_token_account: TnPubkey,
3174 token_mint_account: TnPubkey,
3175 token_program: TnPubkey,
3176 years: u8,
3177 fee: u64,
3178 nonce: u64,
3179 start_slot: u64,
3180 ) -> Result<Transaction> {
3181 let mut tx = Transaction::new(fee_payer, thru_registrar_program, fee, nonce)
3182 .with_start_slot(start_slot)
3183 .with_expiry_after(100)
3184 .with_compute_units(300_000)
3185 .with_state_units(10_000)
3186 .with_memory_units(10_000);
3187
3188 let mut rw_accounts = vec![lease_account, treasurer_account, payer_token_account];
3191 rw_accounts.sort();
3192
3193 let mut ro_accounts = vec![
3194 config_account,
3195 token_mint_account,
3196 token_program,
3197 ];
3198 ro_accounts.sort();
3199
3200 let mut lease_account_idx = 0u16;
3202 let mut treasurer_account_idx = 0u16;
3203 let mut payer_token_account_idx = 0u16;
3204 for (i, account) in rw_accounts.iter().enumerate() {
3205 let idx = (2 + i) as u16;
3206 if *account == lease_account {
3207 lease_account_idx = idx;
3208 } else if *account == treasurer_account {
3209 treasurer_account_idx = idx;
3210 } else if *account == payer_token_account {
3211 payer_token_account_idx = idx;
3212 }
3213 tx = tx.add_rw_account(*account);
3214 }
3215
3216 let base_ro_idx = 2 + rw_accounts.len() as u16;
3218 let mut config_account_idx = 0u16;
3219 let mut token_mint_account_idx = 0u16;
3220 let mut token_program_idx = 0u16;
3221
3222 for (i, account) in ro_accounts.iter().enumerate() {
3223 let idx = base_ro_idx + i as u16;
3224 if *account == config_account {
3225 config_account_idx = idx;
3226 } else if *account == token_mint_account {
3227 token_mint_account_idx = idx;
3228 } else if *account == token_program {
3229 token_program_idx = idx;
3230 }
3231 tx = tx.add_r_account(*account);
3232 }
3233
3234 let instruction_data = build_thru_registrar_renew_lease_instruction(
3235 config_account_idx,
3236 lease_account_idx,
3237 treasurer_account_idx,
3238 payer_token_account_idx,
3239 token_mint_account_idx,
3240 token_program_idx,
3241 years,
3242 )?;
3243
3244 Ok(tx.with_instructions(instruction_data))
3245 }
3246
3247 pub fn build_thru_registrar_claim_expired_domain(
3249 fee_payer: TnPubkey,
3250 thru_registrar_program: TnPubkey,
3251 config_account: TnPubkey,
3252 lease_account: TnPubkey,
3253 treasurer_account: TnPubkey,
3254 payer_token_account: TnPubkey,
3255 token_mint_account: TnPubkey,
3256 token_program: TnPubkey,
3257 years: u8,
3258 fee: u64,
3259 nonce: u64,
3260 start_slot: u64,
3261 ) -> Result<Transaction> {
3262 let mut tx = Transaction::new(fee_payer, thru_registrar_program, fee, nonce)
3263 .with_start_slot(start_slot)
3264 .with_expiry_after(100)
3265 .with_compute_units(300_000)
3266 .with_state_units(10_000)
3267 .with_memory_units(10_000);
3268
3269 let mut rw_accounts = vec![lease_account, treasurer_account, payer_token_account];
3272 rw_accounts.sort();
3273
3274 let mut ro_accounts = vec![
3275 config_account,
3276 token_mint_account,
3277 token_program,
3278 ];
3279 ro_accounts.sort();
3280
3281 let mut lease_account_idx = 0u16;
3283 let mut treasurer_account_idx = 0u16;
3284 let mut payer_token_account_idx = 0u16;
3285 for (i, account) in rw_accounts.iter().enumerate() {
3286 let idx = (2 + i) as u16;
3287 if *account == lease_account {
3288 lease_account_idx = idx;
3289 } else if *account == treasurer_account {
3290 treasurer_account_idx = idx;
3291 } else if *account == payer_token_account {
3292 payer_token_account_idx = idx;
3293 }
3294 tx = tx.add_rw_account(*account);
3295 }
3296
3297 let base_ro_idx = 2 + rw_accounts.len() as u16;
3299 let mut config_account_idx = 0u16;
3300 let mut token_mint_account_idx = 0u16;
3301 let mut token_program_idx = 0u16;
3302
3303 for (i, account) in ro_accounts.iter().enumerate() {
3304 let idx = base_ro_idx + i as u16;
3305 if *account == config_account {
3306 config_account_idx = idx;
3307 } else if *account == token_mint_account {
3308 token_mint_account_idx = idx;
3309 } else if *account == token_program {
3310 token_program_idx = idx;
3311 }
3312 tx = tx.add_r_account(*account);
3313 }
3314
3315 let instruction_data = build_thru_registrar_claim_expired_domain_instruction(
3316 config_account_idx,
3317 lease_account_idx,
3318 treasurer_account_idx,
3319 payer_token_account_idx,
3320 token_mint_account_idx,
3321 token_program_idx,
3322 years,
3323 )?;
3324
3325 Ok(tx.with_instructions(instruction_data))
3326 }
3327}
3328
3329fn build_token_initialize_mint_instruction(
3331 mint_account_idx: u16,
3332 decimals: u8,
3333 creator: TnPubkey,
3334 mint_authority: TnPubkey,
3335 freeze_authority: Option<TnPubkey>,
3336 ticker: &str,
3337 seed: [u8; 32],
3338 state_proof: Vec<u8>,
3339) -> Result<Vec<u8>> {
3340 let mut instruction_data = Vec::new();
3341
3342 instruction_data.push(TOKEN_INSTRUCTION_INITIALIZE_MINT);
3344
3345 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
3347
3348 instruction_data.push(decimals);
3350
3351 instruction_data.extend_from_slice(&creator);
3353
3354 instruction_data.extend_from_slice(&mint_authority);
3356
3357 let (freeze_auth, has_freeze_auth) = match freeze_authority {
3359 Some(auth) => (auth, 1u8),
3360 None => ([0u8; 32], 0u8),
3361 };
3362 instruction_data.extend_from_slice(&freeze_auth);
3363 instruction_data.push(has_freeze_auth);
3364
3365 let ticker_bytes = ticker.as_bytes();
3367 if ticker_bytes.len() > 8 {
3368 return Err(anyhow::anyhow!("Ticker must be 8 characters or less"));
3369 }
3370
3371 instruction_data.push(ticker_bytes.len() as u8);
3372 let mut ticker_padded = [0u8; 8];
3373 ticker_padded[..ticker_bytes.len()].copy_from_slice(ticker_bytes);
3374 instruction_data.extend_from_slice(&ticker_padded);
3375
3376 instruction_data.extend_from_slice(&seed);
3378
3379 instruction_data.extend_from_slice(&state_proof);
3381
3382 Ok(instruction_data)
3383}
3384
3385fn build_token_initialize_account_instruction(
3387 token_account_idx: u16,
3388 mint_account_idx: u16,
3389 owner_account_idx: u16,
3390 seed: [u8; 32],
3391 state_proof: Vec<u8>,
3392) -> Result<Vec<u8>> {
3393 let mut instruction_data = Vec::new();
3394
3395 instruction_data.push(TOKEN_INSTRUCTION_INITIALIZE_ACCOUNT);
3397
3398 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
3400
3401 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
3403
3404 instruction_data.extend_from_slice(&owner_account_idx.to_le_bytes());
3406
3407 instruction_data.extend_from_slice(&seed);
3409
3410 instruction_data.extend_from_slice(&state_proof);
3412
3413 Ok(instruction_data)
3414}
3415
3416fn build_token_transfer_instruction(
3418 source_account_idx: u16,
3419 dest_account_idx: u16,
3420 amount: u64,
3421) -> Result<Vec<u8>> {
3422 let mut instruction_data = Vec::new();
3423
3424 instruction_data.push(TOKEN_INSTRUCTION_TRANSFER);
3426
3427 instruction_data.extend_from_slice(&source_account_idx.to_le_bytes());
3429
3430 instruction_data.extend_from_slice(&dest_account_idx.to_le_bytes());
3432
3433 instruction_data.extend_from_slice(&amount.to_le_bytes());
3435
3436 Ok(instruction_data)
3437}
3438
3439fn build_token_mint_to_instruction(
3441 mint_account_idx: u16,
3442 dest_account_idx: u16,
3443 authority_idx: u16,
3444 amount: u64,
3445) -> Result<Vec<u8>> {
3446 let mut instruction_data = Vec::new();
3447
3448 instruction_data.push(TOKEN_INSTRUCTION_MINT_TO);
3450
3451 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
3453
3454 instruction_data.extend_from_slice(&dest_account_idx.to_le_bytes());
3456
3457 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
3459
3460 instruction_data.extend_from_slice(&amount.to_le_bytes());
3462
3463 Ok(instruction_data)
3464}
3465
3466fn build_token_burn_instruction(
3468 token_account_idx: u16,
3469 mint_account_idx: u16,
3470 authority_idx: u16,
3471 amount: u64,
3472) -> Result<Vec<u8>> {
3473 let mut instruction_data = Vec::new();
3474
3475 instruction_data.push(TOKEN_INSTRUCTION_BURN);
3477
3478 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
3480
3481 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
3483
3484 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
3486
3487 instruction_data.extend_from_slice(&amount.to_le_bytes());
3489
3490 Ok(instruction_data)
3491}
3492
3493fn build_token_freeze_account_instruction(
3495 token_account_idx: u16,
3496 mint_account_idx: u16,
3497 authority_idx: u16,
3498) -> Result<Vec<u8>> {
3499 let mut instruction_data = Vec::new();
3500
3501 instruction_data.push(TOKEN_INSTRUCTION_FREEZE_ACCOUNT);
3503
3504 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
3506
3507 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
3509
3510 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
3512
3513 Ok(instruction_data)
3514}
3515
3516fn build_token_thaw_account_instruction(
3518 token_account_idx: u16,
3519 mint_account_idx: u16,
3520 authority_idx: u16,
3521) -> Result<Vec<u8>> {
3522 let mut instruction_data = Vec::new();
3523
3524 instruction_data.push(TOKEN_INSTRUCTION_THAW_ACCOUNT);
3526
3527 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
3529
3530 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
3532
3533 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
3535
3536 Ok(instruction_data)
3537}
3538
3539fn build_token_close_account_instruction(
3541 token_account_idx: u16,
3542 destination_idx: u16,
3543 authority_idx: u16,
3544) -> Result<Vec<u8>> {
3545 let mut instruction_data = Vec::new();
3546
3547 instruction_data.push(TOKEN_INSTRUCTION_CLOSE_ACCOUNT);
3549
3550 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
3552
3553 instruction_data.extend_from_slice(&destination_idx.to_le_bytes());
3555
3556 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
3558
3559 Ok(instruction_data)
3560}
3561
3562fn build_wthru_initialize_mint_instruction(
3563 token_program_idx: u16,
3564 mint_account_idx: u16,
3565 vault_account_idx: u16,
3566 decimals: u8,
3567 mint_seed: [u8; 32],
3568 mint_proof: Vec<u8>,
3569 vault_proof: Vec<u8>,
3570) -> Result<Vec<u8>> {
3571 let mint_proof_len =
3572 u64::try_from(mint_proof.len()).map_err(|_| anyhow::anyhow!("mint proof too large"))?;
3573 let vault_proof_len =
3574 u64::try_from(vault_proof.len()).map_err(|_| anyhow::anyhow!("vault proof too large"))?;
3575
3576 let mut instruction_data = Vec::new();
3577 instruction_data.extend_from_slice(&TN_WTHRU_INSTRUCTION_INITIALIZE_MINT.to_le_bytes());
3578 instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
3579 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
3580 instruction_data.extend_from_slice(&vault_account_idx.to_le_bytes());
3581 instruction_data.push(decimals);
3582 instruction_data.extend_from_slice(&mint_seed);
3583 instruction_data.extend_from_slice(&mint_proof_len.to_le_bytes());
3584 instruction_data.extend_from_slice(&vault_proof_len.to_le_bytes());
3585 instruction_data.extend_from_slice(&mint_proof);
3586 instruction_data.extend_from_slice(&vault_proof);
3587
3588 Ok(instruction_data)
3589}
3590
3591fn build_wthru_deposit_instruction(
3592 token_program_idx: u16,
3593 vault_account_idx: u16,
3594 mint_account_idx: u16,
3595 dest_account_idx: u16,
3596) -> Result<Vec<u8>> {
3597 let mut instruction_data = Vec::new();
3598 instruction_data.extend_from_slice(&TN_WTHRU_INSTRUCTION_DEPOSIT.to_le_bytes());
3599 instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
3600 instruction_data.extend_from_slice(&vault_account_idx.to_le_bytes());
3601 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
3602 instruction_data.extend_from_slice(&dest_account_idx.to_le_bytes());
3603
3604 Ok(instruction_data)
3605}
3606
3607fn build_wthru_withdraw_instruction(
3608 token_program_idx: u16,
3609 vault_account_idx: u16,
3610 mint_account_idx: u16,
3611 wthru_token_account_idx: u16,
3612 owner_account_idx: u16,
3613 recipient_account_idx: u16,
3614 amount: u64,
3615) -> Result<Vec<u8>> {
3616 let mut instruction_data = Vec::new();
3617 instruction_data.extend_from_slice(&TN_WTHRU_INSTRUCTION_WITHDRAW.to_le_bytes());
3618 instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
3619 instruction_data.extend_from_slice(&vault_account_idx.to_le_bytes());
3620 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
3621 instruction_data.extend_from_slice(&wthru_token_account_idx.to_le_bytes());
3622 instruction_data.extend_from_slice(&owner_account_idx.to_le_bytes());
3623 instruction_data.extend_from_slice(&recipient_account_idx.to_le_bytes());
3624 instruction_data.extend_from_slice(&amount.to_le_bytes());
3625
3626 Ok(instruction_data)
3627}
3628
3629fn build_faucet_deposit_instruction(
3631 faucet_account_idx: u16,
3632 depositor_account_idx: u16,
3633 eoa_program_idx: u16,
3634 amount: u64,
3635) -> Result<Vec<u8>> {
3636 let mut instruction_data = Vec::new();
3637
3638 instruction_data.extend_from_slice(&0u32.to_le_bytes());
3640
3641 instruction_data.extend_from_slice(&faucet_account_idx.to_le_bytes());
3644
3645 instruction_data.extend_from_slice(&depositor_account_idx.to_le_bytes());
3647
3648 instruction_data.extend_from_slice(&eoa_program_idx.to_le_bytes());
3650
3651 instruction_data.extend_from_slice(&amount.to_le_bytes());
3653
3654 Ok(instruction_data)
3655}
3656
3657fn build_faucet_withdraw_instruction(
3659 faucet_account_idx: u16,
3660 recipient_account_idx: u16,
3661 amount: u64,
3662) -> Result<Vec<u8>> {
3663 let mut instruction_data = Vec::new();
3664
3665 instruction_data.extend_from_slice(&1u32.to_le_bytes());
3667
3668 instruction_data.extend_from_slice(&faucet_account_idx.to_le_bytes());
3671
3672 instruction_data.extend_from_slice(&recipient_account_idx.to_le_bytes());
3674
3675 instruction_data.extend_from_slice(&amount.to_le_bytes());
3677
3678 Ok(instruction_data)
3679}
3680
3681#[repr(C, packed)]
3682struct NameServiceInitializeRootArgs {
3683 registrar_account_idx: u16,
3684 authority_account_idx: u16,
3685 root_name: [u8; TN_NAME_SERVICE_MAX_DOMAIN_LENGTH],
3686 root_name_length: u32,
3687}
3688
3689#[repr(C, packed)]
3690struct NameServiceRegisterSubdomainArgs {
3691 domain_account_idx: u16,
3692 parent_account_idx: u16,
3693 owner_account_idx: u16,
3694 authority_account_idx: u16,
3695 name: [u8; TN_NAME_SERVICE_MAX_DOMAIN_LENGTH],
3696 name_length: u32,
3697}
3698
3699#[repr(C, packed)]
3700struct NameServiceAppendRecordArgs {
3701 domain_account_idx: u16,
3702 owner_account_idx: u16,
3703 key_length: u32,
3704 key: [u8; TN_NAME_SERVICE_MAX_KEY_LENGTH],
3705 value_length: u32,
3706 value: [u8; TN_NAME_SERVICE_MAX_VALUE_LENGTH],
3707}
3708
3709#[repr(C, packed)]
3710struct NameServiceDeleteRecordArgs {
3711 domain_account_idx: u16,
3712 owner_account_idx: u16,
3713 key_length: u32,
3714 key: [u8; TN_NAME_SERVICE_MAX_KEY_LENGTH],
3715}
3716
3717#[repr(C, packed)]
3718struct NameServiceUnregisterSubdomainArgs {
3719 domain_account_idx: u16,
3720 owner_account_idx: u16,
3721}
3722
3723fn build_name_service_initialize_root_instruction(
3725 registrar_account_idx: u16,
3726 authority_account_idx: u16,
3727 root_name: &str,
3728 state_proof: Vec<u8>,
3729) -> Result<Vec<u8>> {
3730 let root_name_bytes = root_name.as_bytes();
3731 if root_name_bytes.is_empty()
3732 || root_name_bytes.len() > TN_NAME_SERVICE_MAX_DOMAIN_LENGTH
3733 {
3734 return Err(anyhow::anyhow!(
3735 "Root name length must be between 1 and {}",
3736 TN_NAME_SERVICE_MAX_DOMAIN_LENGTH
3737 ));
3738 }
3739
3740 let mut args = NameServiceInitializeRootArgs {
3741 registrar_account_idx,
3742 authority_account_idx,
3743 root_name: [0u8; TN_NAME_SERVICE_MAX_DOMAIN_LENGTH],
3744 root_name_length: root_name_bytes.len() as u32,
3745 };
3746 args.root_name[..root_name_bytes.len()].copy_from_slice(root_name_bytes);
3747
3748 let mut instruction_data = Vec::new();
3749 instruction_data.extend_from_slice(&TN_NAME_SERVICE_INSTRUCTION_INITIALIZE_ROOT.to_le_bytes());
3750
3751 let args_bytes = unsafe {
3752 std::slice::from_raw_parts(
3753 &args as *const _ as *const u8,
3754 std::mem::size_of::<NameServiceInitializeRootArgs>(),
3755 )
3756 };
3757 instruction_data.extend_from_slice(args_bytes);
3758
3759 instruction_data.extend_from_slice(&TN_NAME_SERVICE_PROOF_INLINE.to_le_bytes());
3760 instruction_data.extend_from_slice(&state_proof);
3761
3762 Ok(instruction_data)
3763}
3764
3765fn build_name_service_register_subdomain_instruction(
3767 domain_account_idx: u16,
3768 parent_account_idx: u16,
3769 owner_account_idx: u16,
3770 authority_account_idx: u16,
3771 domain_name: &str,
3772 state_proof: Vec<u8>,
3773) -> Result<Vec<u8>> {
3774 let domain_bytes = domain_name.as_bytes();
3775 if domain_bytes.is_empty()
3776 || domain_bytes.len() > TN_NAME_SERVICE_MAX_DOMAIN_LENGTH
3777 {
3778 return Err(anyhow::anyhow!(
3779 "Domain name length must be between 1 and {}",
3780 TN_NAME_SERVICE_MAX_DOMAIN_LENGTH
3781 ));
3782 }
3783
3784 let mut args = NameServiceRegisterSubdomainArgs {
3785 domain_account_idx,
3786 parent_account_idx,
3787 owner_account_idx,
3788 authority_account_idx,
3789 name: [0u8; TN_NAME_SERVICE_MAX_DOMAIN_LENGTH],
3790 name_length: domain_bytes.len() as u32,
3791 };
3792 args.name[..domain_bytes.len()].copy_from_slice(domain_bytes);
3793
3794 let mut instruction_data = Vec::new();
3795 instruction_data.extend_from_slice(&TN_NAME_SERVICE_INSTRUCTION_REGISTER_SUBDOMAIN.to_le_bytes());
3796
3797 let args_bytes = unsafe {
3798 std::slice::from_raw_parts(
3799 &args as *const _ as *const u8,
3800 std::mem::size_of::<NameServiceRegisterSubdomainArgs>(),
3801 )
3802 };
3803 instruction_data.extend_from_slice(args_bytes);
3804
3805 instruction_data.extend_from_slice(&TN_NAME_SERVICE_PROOF_INLINE.to_le_bytes());
3806 instruction_data.extend_from_slice(&state_proof);
3807
3808 Ok(instruction_data)
3809}
3810
3811fn build_name_service_append_record_instruction(
3813 domain_account_idx: u16,
3814 owner_account_idx: u16,
3815 key: &[u8],
3816 value: &[u8],
3817) -> Result<Vec<u8>> {
3818 if key.is_empty() || key.len() > TN_NAME_SERVICE_MAX_KEY_LENGTH {
3819 return Err(anyhow::anyhow!(
3820 "Key length must be between 1 and {} bytes",
3821 TN_NAME_SERVICE_MAX_KEY_LENGTH
3822 ));
3823 }
3824 if value.len() > TN_NAME_SERVICE_MAX_VALUE_LENGTH {
3825 return Err(anyhow::anyhow!(
3826 "Value length must be <= {} bytes",
3827 TN_NAME_SERVICE_MAX_VALUE_LENGTH
3828 ));
3829 }
3830
3831 let mut args = NameServiceAppendRecordArgs {
3832 domain_account_idx,
3833 owner_account_idx,
3834 key_length: key.len() as u32,
3835 key: [0u8; TN_NAME_SERVICE_MAX_KEY_LENGTH],
3836 value_length: value.len() as u32,
3837 value: [0u8; TN_NAME_SERVICE_MAX_VALUE_LENGTH],
3838 };
3839 args.key[..key.len()].copy_from_slice(key);
3840 args.value[..value.len()].copy_from_slice(value);
3841
3842 let mut instruction_data = Vec::new();
3843 instruction_data.extend_from_slice(&TN_NAME_SERVICE_INSTRUCTION_APPEND_RECORD.to_le_bytes());
3844
3845 let args_bytes = unsafe {
3846 std::slice::from_raw_parts(
3847 &args as *const _ as *const u8,
3848 std::mem::size_of::<NameServiceAppendRecordArgs>(),
3849 )
3850 };
3851 instruction_data.extend_from_slice(args_bytes);
3852
3853 Ok(instruction_data)
3854}
3855
3856fn build_name_service_delete_record_instruction(
3858 domain_account_idx: u16,
3859 owner_account_idx: u16,
3860 key: &[u8],
3861) -> Result<Vec<u8>> {
3862 if key.is_empty() || key.len() > TN_NAME_SERVICE_MAX_KEY_LENGTH {
3863 return Err(anyhow::anyhow!(
3864 "Key length must be between 1 and {} bytes",
3865 TN_NAME_SERVICE_MAX_KEY_LENGTH
3866 ));
3867 }
3868
3869 let mut args = NameServiceDeleteRecordArgs {
3870 domain_account_idx,
3871 owner_account_idx,
3872 key_length: key.len() as u32,
3873 key: [0u8; TN_NAME_SERVICE_MAX_KEY_LENGTH],
3874 };
3875 args.key[..key.len()].copy_from_slice(key);
3876
3877 let mut instruction_data = Vec::new();
3878 instruction_data.extend_from_slice(&TN_NAME_SERVICE_INSTRUCTION_DELETE_RECORD.to_le_bytes());
3879
3880 let args_bytes = unsafe {
3881 std::slice::from_raw_parts(
3882 &args as *const _ as *const u8,
3883 std::mem::size_of::<NameServiceDeleteRecordArgs>(),
3884 )
3885 };
3886 instruction_data.extend_from_slice(args_bytes);
3887
3888 Ok(instruction_data)
3889}
3890
3891fn build_name_service_unregister_subdomain_instruction(
3893 domain_account_idx: u16,
3894 owner_account_idx: u16,
3895) -> Result<Vec<u8>> {
3896 let args = NameServiceUnregisterSubdomainArgs {
3897 domain_account_idx,
3898 owner_account_idx,
3899 };
3900
3901 let mut instruction_data = Vec::new();
3902 instruction_data.extend_from_slice(&TN_NAME_SERVICE_INSTRUCTION_UNREGISTER.to_le_bytes());
3903
3904 let args_bytes = unsafe {
3905 std::slice::from_raw_parts(
3906 &args as *const _ as *const u8,
3907 std::mem::size_of::<NameServiceUnregisterSubdomainArgs>(),
3908 )
3909 };
3910 instruction_data.extend_from_slice(args_bytes);
3911
3912 Ok(instruction_data)
3913}
3914
3915fn build_thru_registrar_initialize_registry_instruction(
3917 config_account_idx: u16,
3918 name_service_program_idx: u16,
3919 root_registrar_account_idx: u16,
3920 treasurer_account_idx: u16,
3921 token_mint_account_idx: u16,
3922 token_program_idx: u16,
3923 root_domain_name: &str,
3924 price_per_year: u64,
3925 config_proof: Vec<u8>,
3926 registrar_proof: Vec<u8>,
3927) -> Result<Vec<u8>> {
3928 let mut instruction_data = Vec::new();
3929
3930 instruction_data.extend_from_slice(&TN_THRU_REGISTRAR_INSTRUCTION_INITIALIZE_REGISTRY.to_le_bytes());
3932
3933 instruction_data.extend_from_slice(&config_account_idx.to_le_bytes());
3936 instruction_data.extend_from_slice(&name_service_program_idx.to_le_bytes());
3938 instruction_data.extend_from_slice(&root_registrar_account_idx.to_le_bytes());
3940 instruction_data.extend_from_slice(&treasurer_account_idx.to_le_bytes());
3942 instruction_data.extend_from_slice(&token_mint_account_idx.to_le_bytes());
3944 instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
3946 let domain_bytes = root_domain_name.as_bytes();
3948 if domain_bytes.len() > 64 {
3949 return Err(anyhow::anyhow!("Root domain name must be 64 characters or less"));
3950 }
3951 let mut domain_padded = [0u8; 64];
3952 domain_padded[..domain_bytes.len()].copy_from_slice(domain_bytes);
3953 instruction_data.extend_from_slice(&domain_padded);
3954 instruction_data.extend_from_slice(&(domain_bytes.len() as u32).to_le_bytes());
3956 instruction_data.extend_from_slice(&price_per_year.to_le_bytes());
3958
3959 instruction_data.extend_from_slice(&config_proof);
3962 instruction_data.extend_from_slice(®istrar_proof);
3964
3965 Ok(instruction_data)
3966}
3967
3968fn build_thru_registrar_purchase_domain_instruction(
3970 config_account_idx: u16,
3971 lease_account_idx: u16,
3972 domain_account_idx: u16,
3973 name_service_program_idx: u16,
3974 root_registrar_account_idx: u16,
3975 treasurer_account_idx: u16,
3976 payer_token_account_idx: u16,
3977 token_mint_account_idx: u16,
3978 token_program_idx: u16,
3979 domain_name: &str,
3980 years: u8,
3981 lease_proof: Vec<u8>,
3982 domain_proof: Vec<u8>,
3983) -> Result<Vec<u8>> {
3984 let mut instruction_data = Vec::new();
3985
3986 instruction_data.extend_from_slice(&TN_THRU_REGISTRAR_INSTRUCTION_PURCHASE_DOMAIN.to_le_bytes());
3988
3989 instruction_data.extend_from_slice(&config_account_idx.to_le_bytes());
3992 instruction_data.extend_from_slice(&lease_account_idx.to_le_bytes());
3994 instruction_data.extend_from_slice(&domain_account_idx.to_le_bytes());
3996 instruction_data.extend_from_slice(&name_service_program_idx.to_le_bytes());
3998 instruction_data.extend_from_slice(&root_registrar_account_idx.to_le_bytes());
4000 instruction_data.extend_from_slice(&treasurer_account_idx.to_le_bytes());
4002 instruction_data.extend_from_slice(&payer_token_account_idx.to_le_bytes());
4004 instruction_data.extend_from_slice(&token_mint_account_idx.to_le_bytes());
4006 instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
4008 let domain_bytes = domain_name.as_bytes();
4010 if domain_bytes.len() > 64 {
4011 return Err(anyhow::anyhow!("Domain name must be 64 characters or less"));
4012 }
4013 let mut domain_padded = [0u8; 64];
4014 domain_padded[..domain_bytes.len()].copy_from_slice(domain_bytes);
4015 instruction_data.extend_from_slice(&domain_padded);
4016 instruction_data.extend_from_slice(&(domain_bytes.len() as u32).to_le_bytes());
4018 instruction_data.push(years);
4020
4021 instruction_data.extend_from_slice(&lease_proof);
4024 instruction_data.extend_from_slice(&domain_proof);
4026
4027 Ok(instruction_data)
4028}
4029
4030fn build_thru_registrar_renew_lease_instruction(
4032 config_account_idx: u16,
4033 lease_account_idx: u16,
4034 treasurer_account_idx: u16,
4035 payer_token_account_idx: u16,
4036 token_mint_account_idx: u16,
4037 token_program_idx: u16,
4038 years: u8,
4039) -> Result<Vec<u8>> {
4040 let mut instruction_data = Vec::new();
4041
4042 instruction_data.extend_from_slice(&TN_THRU_REGISTRAR_INSTRUCTION_RENEW_LEASE.to_le_bytes());
4044
4045 instruction_data.extend_from_slice(&config_account_idx.to_le_bytes());
4048 instruction_data.extend_from_slice(&lease_account_idx.to_le_bytes());
4050 instruction_data.extend_from_slice(&treasurer_account_idx.to_le_bytes());
4052 instruction_data.extend_from_slice(&payer_token_account_idx.to_le_bytes());
4054 instruction_data.extend_from_slice(&token_mint_account_idx.to_le_bytes());
4056 instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
4058 instruction_data.push(years);
4060
4061 Ok(instruction_data)
4062}
4063
4064fn build_thru_registrar_claim_expired_domain_instruction(
4066 config_account_idx: u16,
4067 lease_account_idx: u16,
4068 treasurer_account_idx: u16,
4069 payer_token_account_idx: u16,
4070 token_mint_account_idx: u16,
4071 token_program_idx: u16,
4072 years: u8,
4073) -> Result<Vec<u8>> {
4074 let mut instruction_data = Vec::new();
4075
4076 instruction_data.extend_from_slice(&TN_THRU_REGISTRAR_INSTRUCTION_CLAIM_EXPIRED_DOMAIN.to_le_bytes());
4078
4079 instruction_data.extend_from_slice(&config_account_idx.to_le_bytes());
4082 instruction_data.extend_from_slice(&lease_account_idx.to_le_bytes());
4084 instruction_data.extend_from_slice(&treasurer_account_idx.to_le_bytes());
4086 instruction_data.extend_from_slice(&payer_token_account_idx.to_le_bytes());
4088 instruction_data.extend_from_slice(&token_mint_account_idx.to_le_bytes());
4090 instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
4092 instruction_data.push(years);
4094
4095 Ok(instruction_data)
4096}