1use crate::{
2 StateProof,
3 tn_public_address::tn_pubkey_to_address_string,
4 txn_lib::{TnPubkey, Transaction},
5};
6use anyhow::Result;
7use hex;
8
9pub const NOOP_PROGRAM: [u8; 32] = {
11 let mut arr = [0u8; 32];
12 arr[31] = 0x03;
13 arr
14};
15pub const SYSTEM_PROGRAM: [u8; 32] = {
16 let mut arr = [0u8; 32];
17 arr[31] = 0x01;
18 arr
19};
20pub const EOA_PROGRAM: [u8; 32] = {
21 let arr = [0u8; 32];
22 arr
23};
24
25pub const UPLOADER_PROGRAM: [u8; 32] = {
26 let mut arr = [0u8; 32];
27 arr[31] = 0x02;
28 arr
29};
30
31#[derive(Debug, Clone)]
32pub struct TransactionBuilder {
33 }
38
39impl TransactionBuilder {
40 pub fn build_create_with_fee_payer_proof(
42 fee_payer: TnPubkey,
43 start_slot: u64,
44 fee_payer_state_proof: &StateProof,
45 ) -> Result<Transaction> {
46 let tx = Transaction::new(fee_payer, NOOP_PROGRAM, 0, 0)
47 .with_fee_payer_state_proof(fee_payer_state_proof)
48 .with_start_slot(start_slot)
49 .with_expiry_after(100)
50 .with_compute_units(10_000)
51 .with_memory_units(10_000)
52 .with_state_units(10_000);
53 Ok(tx)
54 }
55
56 pub fn build_transfer(
70 fee_payer: TnPubkey,
71 program: TnPubkey,
72 to_account: TnPubkey,
73 amount: u64,
74 fee: u64,
75 nonce: u64,
76 start_slot: u64,
77 ) -> Result<Transaction> {
78 let from_account_idx = 0u16; let to_account_idx = 2u16; let instruction_data =
83 build_transfer_instruction(from_account_idx, to_account_idx, amount)?;
84
85 let tx = Transaction::new(fee_payer, program, fee, nonce)
86 .with_start_slot(start_slot)
87 .add_rw_account(to_account) .with_instructions(instruction_data)
89 .with_expiry_after(100)
90 .with_compute_units(10000)
91 .with_memory_units(10000)
92 .with_state_units(10000);
93
94 Ok(tx)
95 }
96
97 pub fn build_create_account(
99 fee_payer: TnPubkey,
100 program: TnPubkey,
101 target_account: TnPubkey,
102 seed: &str,
103 state_proof: Option<&[u8]>,
104 fee: u64,
105 nonce: u64,
106 start_slot: u64,
107 ) -> Result<Transaction> {
108 let target_account_idx = 2u16; let instruction_data =
111 build_create_account_instruction(target_account_idx, seed, state_proof)?;
112
113 let tx = Transaction::new(fee_payer, program, fee, nonce)
114 .with_start_slot(start_slot)
115 .add_rw_account(target_account)
116 .with_instructions(instruction_data)
117 .with_expiry_after(100)
118 .with_compute_units(10_000)
119 .with_memory_units(10_000)
120 .with_state_units(10_000);
121
122 Ok(tx)
123 }
124
125 pub fn build_create_ephemeral_account(
127 fee_payer: TnPubkey,
128 program: TnPubkey,
129 target_account: TnPubkey,
130 seed: &[u8; 32],
131 fee: u64,
132 nonce: u64,
133 start_slot: u64,
134 ) -> Result<Transaction> {
135 let target_account_idx = 2u16; let instruction_data = build_ephemeral_account_instruction(target_account_idx, seed)?;
138
139 let tx = Transaction::new(fee_payer, program, fee, nonce)
140 .with_start_slot(start_slot)
141 .add_rw_account(target_account)
142 .with_instructions(instruction_data)
143 .with_expiry_after(100)
144 .with_compute_units(50_000)
145 .with_memory_units(10_000)
146 .with_state_units(10_000);
147 Ok(tx)
148 }
149
150 pub fn build_resize_account(
152 fee_payer: TnPubkey,
153 program: TnPubkey,
154 target_account: TnPubkey,
155 new_size: u64,
156 fee: u64,
157 nonce: u64,
158 start_slot: u64,
159 ) -> Result<Transaction> {
160 let target_account_idx = 2u16; let instruction_data = build_resize_instruction(target_account_idx, new_size)?;
163
164 let tx = Transaction::new(fee_payer, program, fee, nonce)
165 .with_start_slot(start_slot)
166 .with_expiry_after(100)
167 .with_compute_units(100032)
168 .with_state_units(1 + new_size.checked_div(4096).unwrap() as u16)
169 .with_memory_units(10000)
170 .add_rw_account(target_account)
171 .with_instructions(instruction_data)
172 .with_expiry_after(100)
173 .with_compute_units(10_000 + 2 * new_size as u32)
174 .with_memory_units(10_000)
175 .with_state_units(10_000);
176
177 Ok(tx)
178 }
179
180 pub fn build_compress_account(
182 fee_payer: TnPubkey,
183 program: TnPubkey,
184 target_account: TnPubkey,
185 state_proof: &[u8],
186 fee: u64,
187 nonce: u64,
188 start_slot: u64,
189 account_size: u32,
190 ) -> Result<Transaction> {
191 let target_account_idx = 2u16; let instruction_data = build_compress_instruction(target_account_idx, state_proof)?;
194
195 let tx = Transaction::new(fee_payer, program, fee, nonce)
196 .with_start_slot(start_slot)
197 .with_may_compress_account()
198 .add_rw_account(target_account)
199 .with_instructions(instruction_data)
200 .with_expiry_after(100)
201 .with_compute_units(100_300 + account_size * 2)
202 .with_memory_units(10000)
203 .with_state_units(10000);
204
205 Ok(tx)
206 }
207
208 pub fn build_decompress_account(
210 fee_payer: TnPubkey,
211 program: TnPubkey,
212 target_account: TnPubkey,
213 account_data: &[u8],
214 state_proof: &[u8],
215 fee: u64,
216 nonce: u64,
217 start_slot: u64,
218 ) -> Result<Transaction> {
219 let target_account_idx = 2u16; let instruction_data =
222 build_decompress_instruction(target_account_idx, account_data, state_proof)?;
223
224 let tx = Transaction::new(fee_payer, program, fee, nonce)
225 .with_start_slot(start_slot)
226 .add_rw_account(target_account)
227 .with_instructions(instruction_data)
228 .with_compute_units(100_300 + account_data.len() as u32 * 2)
229 .with_state_units(10_000)
230 .with_memory_units(10_000)
231 .with_expiry_after(100);
232 Ok(tx)
233 }
234
235 pub fn build_write_data(
237 fee_payer: TnPubkey,
238 program: TnPubkey,
239 target_account: TnPubkey,
240 offset: u16,
241 data: &[u8],
242 fee: u64,
243 nonce: u64,
244 start_slot: u64,
245 ) -> Result<Transaction> {
246 let target_account_idx = 2u16; let instruction_data = build_write_instruction(target_account_idx, offset, data)?;
249
250 let tx = Transaction::new(fee_payer, program, fee, nonce)
251 .with_start_slot(start_slot)
252 .with_expiry_after(100)
253 .with_compute_units(100045)
254 .with_state_units(10000)
255 .with_memory_units(10000)
256 .add_rw_account(target_account)
257 .with_instructions(instruction_data);
258
259 Ok(tx)
260 }
261}
262
263fn build_transfer_instruction(
272 from_account_idx: u16,
273 to_account_idx: u16,
274 amount: u64,
275) -> Result<Vec<u8>> {
276 let mut instruction = Vec::new();
277
278 instruction.extend_from_slice(&1u32.to_le_bytes());
280
281 instruction.extend_from_slice(&amount.to_le_bytes());
284
285 instruction.extend_from_slice(&from_account_idx.to_le_bytes());
287
288 instruction.extend_from_slice(&to_account_idx.to_le_bytes());
290
291 Ok(instruction)
292}
293
294fn build_create_account_instruction(
296 target_account_idx: u16,
297 seed: &str,
298 state_proof: Option<&[u8]>,
299) -> Result<Vec<u8>> {
300 let mut instruction = Vec::new();
301
302 instruction.push(0x00);
304
305 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
307
308 let seed_bytes =
310 hex::decode(seed).map_err(|e| anyhow::anyhow!("Failed to decode hex seed: {}", e))?;
311
312 instruction.extend_from_slice(&(seed_bytes.len() as u64).to_le_bytes());
314
315 let has_proof = state_proof.is_some();
317 instruction.push(if has_proof { 1u8 } else { 0u8 });
318
319 instruction.extend_from_slice(&seed_bytes);
321
322 if let Some(proof) = state_proof {
324 instruction.extend_from_slice(proof);
325 }
326
327 Ok(instruction)
328}
329
330fn build_ephemeral_account_instruction(
332 target_account_idx: u16,
333 seed: &[u8; 32],
334) -> Result<Vec<u8>> {
335 let mut instruction = Vec::new();
336
337 instruction.push(0x01);
339
340 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
342
343 instruction.extend_from_slice(&(seed.len() as u64).to_le_bytes());
345
346 instruction.extend_from_slice(seed);
348
349 Ok(instruction)
350}
351
352fn build_resize_instruction(target_account_idx: u16, new_size: u64) -> Result<Vec<u8>> {
354 let mut instruction = Vec::new();
355
356 instruction.push(0x04);
358
359 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
361
362 instruction.extend_from_slice(&new_size.to_le_bytes());
364
365 Ok(instruction)
366}
367
368fn build_write_instruction(target_account_idx: u16, offset: u16, data: &[u8]) -> Result<Vec<u8>> {
370 let mut instruction = Vec::new();
371
372 instruction.push(0xC8);
374
375 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
377
378 instruction.extend_from_slice(&offset.to_le_bytes());
380
381 instruction.extend_from_slice(&(data.len() as u16).to_le_bytes());
383
384 instruction.extend_from_slice(data);
386
387 Ok(instruction)
388}
389
390fn build_compress_instruction(target_account_idx: u16, state_proof: &[u8]) -> Result<Vec<u8>> {
392 let mut instruction = Vec::new();
393
394 instruction.push(0x05);
397
398 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
400
401 instruction.extend_from_slice(state_proof);
403
404 Ok(instruction)
405}
406
407fn build_decompress_instruction(
408 target_account_idx: u16,
409 account_data: &[u8],
410 state_proof: &[u8],
411) -> Result<Vec<u8>> {
412 let mut instruction = Vec::new();
413
414 instruction.push(0x06);
416
417 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
419 instruction.extend_from_slice(&(account_data.len() as u64).to_le_bytes());
420
421 instruction.extend_from_slice(account_data);
423
424 instruction.extend_from_slice(state_proof);
426
427 Ok(instruction)
428}
429
430pub fn generate_ephemeral_address(seed: &str) -> Result<String> {
435 let owner_pubkey = [0u8; 32];
437
438 let seed_bytes =
440 hex::decode(seed).map_err(|e| anyhow::anyhow!("Failed to decode hex seed: {}", e))?;
441
442 let mut seed_32 = [0u8; 32];
444 let copy_len = std::cmp::min(seed_bytes.len(), 32);
445 seed_32[..copy_len].copy_from_slice(&seed_bytes[..copy_len]);
446
447 Ok(
449 crate::tn_public_address::create_program_defined_account_address_string(
450 &owner_pubkey,
451 true, &seed_32,
453 ),
454 )
455}
456
457pub fn generate_system_derived_address(seed: &str, is_ephemeral: bool) -> Result<String> {
458 let seed_bytes =
460 hex::decode(seed).map_err(|e| anyhow::anyhow!("Failed to decode hex seed: {}", e))?;
461
462 let pubkey = generate_derived_address(&seed_bytes, &[0u8; 32], is_ephemeral)?;
463
464 Ok(tn_pubkey_to_address_string(&pubkey))
465}
466
467pub fn generate_derived_address(
468 seed: &[u8],
469 owner_pubkey: &[u8; 32],
470 is_ephemeral: bool,
471) -> Result<[u8; 32]> {
472 use sha2::{Digest, Sha256};
473
474 let mut hasher = Sha256::new();
476
477 hasher.update(&owner_pubkey);
479
480 hasher.update(&[is_ephemeral as u8]);
482
483 hasher.update(&seed);
485
486 Ok(hasher.finalize().into())
488}
489
490#[cfg(test)]
491mod tests {
492 use super::*;
493
494 #[test]
495 fn test_ephemeral_address_generation() {
496 let hex_seed1 = hex::encode("test_seed_123");
498 let hex_seed2 = hex::encode("test_seed_123");
499 let hex_seed3 = hex::encode("different_seed");
500
501 let addr1 = generate_ephemeral_address(&hex_seed1).unwrap();
502 let addr2 = generate_ephemeral_address(&hex_seed2).unwrap();
503 let addr3 = generate_ephemeral_address(&hex_seed3).unwrap();
504
505 assert_eq!(addr1, addr2);
507
508 assert_ne!(addr1, addr3);
510
511 assert!(addr1.starts_with("ta"));
513 assert!(addr2.starts_with("ta"));
514 assert!(addr3.starts_with("ta"));
515
516 assert_eq!(addr1.len(), 46);
518 assert_eq!(addr2.len(), 46);
519 assert_eq!(addr3.len(), 46);
520 }
521
522 #[test]
523 fn test_eoa_transfer_instruction_format() {
524 let from_idx = 0u16;
526 let to_idx = 2u16;
527 let amount = 1000u64;
528
529 let instruction = build_transfer_instruction(from_idx, to_idx, amount).unwrap();
530
531 assert_eq!(instruction.len(), 16, "Instruction should be 16 bytes");
539
540 let discriminant = u32::from_le_bytes([
542 instruction[0],
543 instruction[1],
544 instruction[2],
545 instruction[3],
546 ]);
547 assert_eq!(discriminant, 1, "Discriminant should be 1 for TRANSFER");
548
549 let parsed_amount = u64::from_le_bytes([
551 instruction[4],
552 instruction[5],
553 instruction[6],
554 instruction[7],
555 instruction[8],
556 instruction[9],
557 instruction[10],
558 instruction[11],
559 ]);
560 assert_eq!(parsed_amount, amount, "Amount should match input");
561
562 let parsed_from = u16::from_le_bytes([instruction[12], instruction[13]]);
564 assert_eq!(parsed_from, from_idx, "From index should match input");
565
566 let parsed_to = u16::from_le_bytes([instruction[14], instruction[15]]);
568 assert_eq!(parsed_to, to_idx, "To index should match input");
569 }
570}
571
572pub const TN_UPLOADER_PROGRAM_INSTRUCTION_CREATE: u32 = 0x00;
574pub const TN_UPLOADER_PROGRAM_INSTRUCTION_WRITE: u32 = 0x01;
575pub const TN_UPLOADER_PROGRAM_INSTRUCTION_DESTROY: u32 = 0x02;
576pub const TN_UPLOADER_PROGRAM_INSTRUCTION_FINALIZE: u32 = 0x03;
577
578#[repr(C, packed)]
580#[derive(Debug, Clone, Copy)]
581pub struct UploaderCreateArgs {
582 pub buffer_account_idx: u16,
583 pub meta_account_idx: u16,
584 pub authority_account_idx: u16,
585 pub buffer_account_sz: u32,
586 pub expected_account_hash: [u8; 32],
587 pub seed_len: u32,
588 }
590
591#[repr(C, packed)]
593#[derive(Debug, Clone, Copy)]
594pub struct UploaderWriteArgs {
595 pub buffer_account_idx: u16,
596 pub meta_account_idx: u16,
597 pub data_len: u32,
598 pub data_offset: u32,
599 }
601
602#[repr(C, packed)]
604#[derive(Debug, Clone, Copy)]
605pub struct UploaderFinalizeArgs {
606 pub buffer_account_idx: u16,
607 pub meta_account_idx: u16,
608 pub expected_account_hash: [u8; 32],
609}
610
611#[repr(C, packed)]
613#[derive(Debug, Clone, Copy)]
614pub struct UploaderDestroyArgs {
615 pub buffer_account_idx: u16,
616 pub meta_account_idx: u16,
617}
618
619pub const MANAGER_INSTRUCTION_CREATE_PERMANENT: u8 = 0x00;
621pub const MANAGER_INSTRUCTION_CREATE_EPHEMERAL: u8 = 0x01;
622pub const MANAGER_INSTRUCTION_UPGRADE: u8 = 0x02;
623pub const MANAGER_INSTRUCTION_SET_PAUSE: u8 = 0x03;
624pub const MANAGER_INSTRUCTION_DESTROY: u8 = 0x04;
625pub const MANAGER_INSTRUCTION_FINALIZE: u8 = 0x05;
626pub const MANAGER_INSTRUCTION_SET_AUTHORITY: u8 = 0x06;
627pub const MANAGER_INSTRUCTION_CLAIM_AUTHORITY: u8 = 0x07;
628
629#[repr(C, packed)]
631#[derive(Debug, Clone, Copy)]
632pub struct ManagerHeaderArgs {
633 pub discriminant: u8,
634 pub meta_account_idx: u16,
635 pub program_account_idx: u16,
636}
637
638#[repr(C, packed)]
640#[derive(Debug, Clone, Copy)]
641pub struct ManagerCreateArgs {
642 pub discriminant: u8,
643 pub meta_account_idx: u16,
644 pub program_account_idx: u16,
645 pub srcbuf_account_idx: u16,
646 pub srcbuf_offset: u32,
647 pub srcbuf_size: u32,
648 pub authority_account_idx: u16,
649 pub seed_len: u32,
650 }
652
653#[repr(C, packed)]
655#[derive(Debug, Clone, Copy)]
656pub struct ManagerUpgradeArgs {
657 pub discriminant: u8,
658 pub meta_account_idx: u16,
659 pub program_account_idx: u16,
660 pub srcbuf_account_idx: u16,
661 pub srcbuf_offset: u32,
662 pub srcbuf_size: u32,
663}
664
665#[repr(C, packed)]
667#[derive(Debug, Clone, Copy)]
668pub struct ManagerSetPauseArgs {
669 pub discriminant: u8,
670 pub meta_account_idx: u16,
671 pub program_account_idx: u16,
672 pub is_paused: u8,
673}
674
675#[repr(C, packed)]
677#[derive(Debug, Clone, Copy)]
678pub struct ManagerSetAuthorityArgs {
679 pub discriminant: u8,
680 pub meta_account_idx: u16,
681 pub program_account_idx: u16,
682 pub authority_candidate: [u8; 32],
683}
684
685pub const TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_CREATE: u8 = 0x00;
687pub const TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_WRITE: u8 = 0x01;
688
689#[repr(C, packed)]
691#[derive(Debug, Clone, Copy)]
692pub struct TestUploaderCreateArgs {
693 pub account_idx: u16,
694 pub is_ephemeral: u8,
695 pub account_sz: u32,
696 pub seed_len: u32,
697 }
699
700#[repr(C, packed)]
702#[derive(Debug, Clone, Copy)]
703pub struct TestUploaderWriteArgs {
704 pub target_account_idx: u16,
705 pub target_offset: u32,
706 pub data_len: u32,
707 }
709
710#[repr(C, packed)]
712#[derive(Debug, Clone, Copy)]
713pub struct SystemProgramDecompress2Args {
714 pub target_account_idx: u16,
715 pub meta_account_idx: u16,
716 pub data_account_idx: u16,
717 pub data_offset: u32,
718}
719
720impl TransactionBuilder {
721 pub fn build_uploader_create(
723 fee_payer: TnPubkey,
724 uploader_program: TnPubkey,
725 meta_account: TnPubkey,
726 buffer_account: TnPubkey,
727 buffer_size: u32,
728 expected_hash: [u8; 32],
729 seed: &[u8],
730 fee: u64,
731 nonce: u64,
732 start_slot: u64,
733 ) -> Result<Transaction> {
734 let authority_account_idx = 0u16;
736
737 let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
738 .with_start_slot(start_slot)
739 .with_expiry_after(10)
740 .with_compute_units(50_000 + 2 * buffer_size as u32)
741 .with_memory_units(10_000)
742 .with_state_units(10_000);
743
744 let mut meta_account_idx = 2u16;
745 let mut buffer_account_idx = 3u16;
746 if meta_account > buffer_account {
747 meta_account_idx = 3u16;
748 buffer_account_idx = 2u16;
749 tx = tx
750 .add_rw_account(buffer_account)
751 .add_rw_account(meta_account)
752 } else {
753 tx = tx
754 .add_rw_account(meta_account)
755 .add_rw_account(buffer_account)
756 }
757
758 let instruction_data = build_uploader_create_instruction(
759 buffer_account_idx,
760 meta_account_idx,
761 authority_account_idx,
762 buffer_size,
763 expected_hash,
764 seed,
765 )?;
766
767 tx = tx.with_instructions(instruction_data);
768
769 Ok(tx)
770 }
771
772 pub fn build_uploader_write(
774 fee_payer: TnPubkey,
775 uploader_program: TnPubkey,
776 meta_account: TnPubkey,
777 buffer_account: TnPubkey,
778 data: &[u8],
779 offset: u32,
780 fee: u64,
781 nonce: u64,
782 start_slot: u64,
783 ) -> Result<Transaction> {
784 let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
786 .with_start_slot(start_slot)
787 .with_expiry_after(10000)
788 .with_compute_units(500_000_000)
789 .with_memory_units(5000)
790 .with_state_units(5000);
791
792 let mut meta_account_idx = 2u16;
793 let mut buffer_account_idx = 3u16;
794 if meta_account > buffer_account {
795 meta_account_idx = 3u16;
796 buffer_account_idx = 2u16;
797 tx = tx
798 .add_rw_account(buffer_account)
799 .add_rw_account(meta_account)
800 } else {
801 tx = tx
802 .add_rw_account(meta_account)
803 .add_rw_account(buffer_account)
804 }
805
806 let instruction_data =
807 build_uploader_write_instruction(buffer_account_idx, meta_account_idx, data, offset)?;
808
809 tx = tx.with_instructions(instruction_data);
810
811 Ok(tx)
812 }
813
814 pub fn build_uploader_finalize(
816 fee_payer: TnPubkey,
817 uploader_program: TnPubkey,
818 meta_account: TnPubkey,
819 buffer_account: TnPubkey,
820 buffer_size: u32,
821 expected_hash: [u8; 32],
822 fee: u64,
823 nonce: u64,
824 start_slot: u64,
825 ) -> Result<Transaction> {
826 let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
827 .with_start_slot(start_slot)
828 .with_expiry_after(10000)
829 .with_compute_units(50_000 + 180 * buffer_size as u32)
830 .with_memory_units(5000)
831 .with_state_units(5000);
832
833 let mut meta_account_idx = 2u16;
835 let mut buffer_account_idx = 3u16;
836 if meta_account > buffer_account {
837 meta_account_idx = 3u16;
838 buffer_account_idx = 2u16;
839 tx = tx
840 .add_rw_account(buffer_account)
841 .add_rw_account(meta_account)
842 } else {
843 tx = tx
844 .add_rw_account(meta_account)
845 .add_rw_account(buffer_account)
846 }
847
848 let instruction_data = build_uploader_finalize_instruction(
849 buffer_account_idx,
850 meta_account_idx,
851 expected_hash,
852 )?;
853
854 tx = tx.with_instructions(instruction_data);
855
856 Ok(tx)
857 }
858
859 pub fn build_uploader_destroy(
861 fee_payer: TnPubkey,
862 uploader_program: TnPubkey,
863 meta_account: TnPubkey,
864 buffer_account: TnPubkey,
865 fee: u64,
866 nonce: u64,
867 start_slot: u64,
868 ) -> Result<Transaction> {
869 let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
870 .with_start_slot(start_slot)
871 .with_expiry_after(10000)
872 .with_compute_units(50000)
873 .with_memory_units(5000)
874 .with_state_units(5000);
875
876 let mut meta_account_idx = 2u16;
878 let mut buffer_account_idx = 3u16;
879 if meta_account > buffer_account {
880 meta_account_idx = 3u16;
881 buffer_account_idx = 2u16;
882 tx = tx
883 .add_rw_account(buffer_account)
884 .add_rw_account(meta_account)
885 } else {
886 tx = tx
887 .add_rw_account(meta_account)
888 .add_rw_account(buffer_account)
889 }
890
891 let instruction_data =
892 build_uploader_destroy_instruction(buffer_account_idx, meta_account_idx)?;
893
894 tx = tx.with_instructions(instruction_data);
895 Ok(tx)
896 }
897
898 pub fn build_manager_create(
900 fee_payer: TnPubkey,
901 manager_program: TnPubkey,
902 meta_account: TnPubkey,
903 program_account: TnPubkey,
904 srcbuf_account: TnPubkey,
905 authority_account: TnPubkey,
906 srcbuf_offset: u32,
907 srcbuf_size: u32,
908 seed: &[u8],
909 is_ephemeral: bool,
910 meta_proof: Option<&[u8]>,
911 program_proof: Option<&[u8]>,
912 fee: u64,
913 nonce: u64,
914 start_slot: u64,
915 ) -> Result<Transaction> {
916 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
917 .with_start_slot(start_slot)
918 .with_expiry_after(10000)
919 .with_compute_units(500_000_000)
920 .with_memory_units(5000)
921 .with_state_units(5000);
922
923 let authority_is_fee_payer = authority_account == fee_payer;
925
926 let mut rw_accounts = vec![(meta_account, "meta"), (program_account, "program")];
928
929 let mut r_accounts = vec![(srcbuf_account, "srcbuf")];
930
931 if !authority_is_fee_payer {
933 r_accounts.push((authority_account, "authority"));
934 }
935
936 rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
938
939 r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
941
942 let mut accounts = rw_accounts;
944 accounts.extend(r_accounts);
945
946 let mut meta_account_idx = 0u16;
947 let mut program_account_idx = 0u16;
948 let mut srcbuf_account_idx = 0u16;
949 let mut authority_account_idx = if authority_is_fee_payer {
950 0u16 } else {
952 0u16 };
954
955 for (i, (account, account_type)) in accounts.iter().enumerate() {
956 let idx = (i + 2) as u16; match *account_type {
958 "meta" => {
959 meta_account_idx = idx;
960 tx = tx.add_rw_account(*account);
961 }
962 "program" => {
963 program_account_idx = idx;
964 tx = tx.add_rw_account(*account);
965 }
966 "srcbuf" => {
967 srcbuf_account_idx = idx;
968 tx = tx.add_r_account(*account);
969 }
970 "authority" => {
971 authority_account_idx = idx;
972 tx = tx.add_r_account(*account);
973 }
974 _ => unreachable!(),
975 }
976 }
977
978 let discriminant = if is_ephemeral {
979 MANAGER_INSTRUCTION_CREATE_EPHEMERAL
980 } else {
981 MANAGER_INSTRUCTION_CREATE_PERMANENT
982 };
983
984 let combined_proof = if let (Some(meta), Some(program)) = (meta_proof, program_proof) {
986 let mut combined = Vec::with_capacity(meta.len() + program.len());
987 combined.extend_from_slice(meta);
988 combined.extend_from_slice(program);
989 Some(combined)
990 } else {
991 None
992 };
993
994 let instruction_data = build_manager_create_instruction(
995 discriminant,
996 meta_account_idx,
997 program_account_idx,
998 srcbuf_account_idx,
999 authority_account_idx,
1000 srcbuf_offset,
1001 srcbuf_size,
1002 seed,
1003 combined_proof.as_deref(),
1004 )?;
1005
1006 tx = tx.with_instructions(instruction_data);
1007 Ok(tx)
1008 }
1009
1010 pub fn build_manager_upgrade(
1012 fee_payer: TnPubkey,
1013 manager_program: TnPubkey,
1014 meta_account: TnPubkey,
1015 program_account: TnPubkey,
1016 srcbuf_account: TnPubkey,
1017 srcbuf_offset: u32,
1018 srcbuf_size: u32,
1019 fee: u64,
1020 nonce: u64,
1021 start_slot: u64,
1022 ) -> Result<Transaction> {
1023 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
1024 .with_start_slot(start_slot)
1025 .with_expiry_after(10000)
1026 .with_compute_units(500_000_000)
1027 .with_memory_units(5000)
1028 .with_state_units(5000);
1029
1030 let mut rw_accounts = vec![(meta_account, "meta"), (program_account, "program")];
1032
1033 let mut r_accounts = vec![(srcbuf_account, "srcbuf")];
1034
1035 rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
1037
1038 r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
1040
1041 let mut accounts = rw_accounts;
1043 accounts.extend(r_accounts);
1044
1045 let mut meta_account_idx = 0u16;
1046 let mut program_account_idx = 0u16;
1047 let mut srcbuf_account_idx = 0u16;
1048
1049 for (i, (account, account_type)) in accounts.iter().enumerate() {
1050 let idx = (i + 2) as u16; match *account_type {
1052 "meta" => {
1053 meta_account_idx = idx;
1054 tx = tx.add_rw_account(*account);
1055 }
1056 "program" => {
1057 program_account_idx = idx;
1058 tx = tx.add_rw_account(*account);
1059 }
1060 "srcbuf" => {
1061 srcbuf_account_idx = idx;
1062 tx = tx.add_r_account(*account);
1063 }
1064 _ => unreachable!(),
1065 }
1066 }
1067
1068 let instruction_data = build_manager_upgrade_instruction(
1069 meta_account_idx,
1070 program_account_idx,
1071 srcbuf_account_idx,
1072 srcbuf_offset,
1073 srcbuf_size,
1074 )?;
1075
1076 tx = tx.with_instructions(instruction_data);
1077 Ok(tx)
1078 }
1079
1080 pub fn build_manager_set_pause(
1082 fee_payer: TnPubkey,
1083 manager_program: TnPubkey,
1084 meta_account: TnPubkey,
1085 program_account: TnPubkey,
1086 is_paused: bool,
1087 fee: u64,
1088 nonce: u64,
1089 start_slot: u64,
1090 ) -> Result<Transaction> {
1091 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
1092 .with_start_slot(start_slot)
1093 .with_expiry_after(10000)
1094 .with_compute_units(100_000_000)
1095 .with_memory_units(5000)
1096 .with_state_units(5000);
1097
1098 let mut accounts = vec![(meta_account, "meta"), (program_account, "program")];
1100 accounts.sort_by(|a, b| a.0.cmp(&b.0));
1101
1102 let mut meta_account_idx = 0u16;
1103 let mut program_account_idx = 0u16;
1104
1105 for (i, (account, account_type)) in accounts.iter().enumerate() {
1106 let idx = (i + 2) as u16;
1107 match *account_type {
1108 "meta" => {
1109 meta_account_idx = idx;
1110 tx = tx.add_rw_account(*account);
1111 }
1112 "program" => {
1113 program_account_idx = idx;
1114 tx = tx.add_rw_account(*account);
1115 }
1116 _ => unreachable!(),
1117 }
1118 }
1119
1120 let instruction_data =
1121 build_manager_set_pause_instruction(meta_account_idx, program_account_idx, is_paused)?;
1122
1123 tx = tx.with_instructions(instruction_data);
1124 Ok(tx)
1125 }
1126
1127 pub fn build_manager_simple(
1129 fee_payer: TnPubkey,
1130 manager_program: TnPubkey,
1131 meta_account: TnPubkey,
1132 program_account: TnPubkey,
1133 instruction_type: u8,
1134 fee: u64,
1135 nonce: u64,
1136 start_slot: u64,
1137 ) -> Result<Transaction> {
1138 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
1139 .with_start_slot(start_slot)
1140 .with_expiry_after(10000)
1141 .with_compute_units(100_000_000)
1142 .with_memory_units(5000)
1143 .with_state_units(5000);
1144
1145 let mut accounts = vec![(meta_account, "meta"), (program_account, "program")];
1147 accounts.sort_by(|a, b| a.0.cmp(&b.0));
1148
1149 let mut meta_account_idx = 0u16;
1150 let mut program_account_idx = 0u16;
1151
1152 for (i, (account, account_type)) in accounts.iter().enumerate() {
1153 let idx = (i + 2) as u16;
1154 match *account_type {
1155 "meta" => {
1156 meta_account_idx = idx;
1157 tx = tx.add_rw_account(*account);
1158 }
1159 "program" => {
1160 program_account_idx = idx;
1161 tx = tx.add_rw_account(*account);
1162 }
1163 _ => unreachable!(),
1164 }
1165 }
1166
1167 let instruction_data = build_manager_header_instruction(
1168 instruction_type,
1169 meta_account_idx,
1170 program_account_idx,
1171 )?;
1172
1173 tx = tx.with_instructions(instruction_data);
1174 Ok(tx)
1175 }
1176
1177 pub fn build_manager_set_authority(
1179 fee_payer: TnPubkey,
1180 manager_program: TnPubkey,
1181 meta_account: TnPubkey,
1182 program_account: TnPubkey,
1183 authority_candidate: [u8; 32],
1184 fee: u64,
1185 nonce: u64,
1186 start_slot: u64,
1187 ) -> Result<Transaction> {
1188 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
1189 .with_start_slot(start_slot)
1190 .with_expiry_after(10000)
1191 .with_compute_units(100_000_000)
1192 .with_memory_units(5000)
1193 .with_state_units(5000);
1194
1195 let mut accounts = vec![(meta_account, "meta"), (program_account, "program")];
1197 accounts.sort_by(|a, b| a.0.cmp(&b.0));
1198
1199 let mut meta_account_idx = 0u16;
1200 let mut program_account_idx = 0u16;
1201
1202 for (i, (account, account_type)) in accounts.iter().enumerate() {
1203 let idx = (i + 2) as u16;
1204 match *account_type {
1205 "meta" => {
1206 meta_account_idx = idx;
1207 tx = tx.add_rw_account(*account);
1208 }
1209 "program" => {
1210 program_account_idx = idx;
1211 tx = tx.add_rw_account(*account);
1212 }
1213 _ => unreachable!(),
1214 }
1215 }
1216
1217 let instruction_data = build_manager_set_authority_instruction(
1218 meta_account_idx,
1219 program_account_idx,
1220 authority_candidate,
1221 )?;
1222
1223 tx = tx.with_instructions(instruction_data);
1224 Ok(tx)
1225 }
1226
1227 pub fn build_test_uploader_create(
1229 fee_payer: TnPubkey,
1230 test_uploader_program: TnPubkey,
1231 target_account: TnPubkey,
1232 account_sz: u32,
1233 seed: &[u8],
1234 is_ephemeral: bool,
1235 state_proof: Option<&[u8]>,
1236 fee: u64,
1237 nonce: u64,
1238 start_slot: u64,
1239 ) -> Result<Transaction> {
1240 let target_account_idx = 2u16;
1242
1243 let tx = Transaction::new(fee_payer, test_uploader_program, fee, nonce)
1244 .with_start_slot(start_slot)
1245 .with_expiry_after(100)
1246 .with_compute_units(100_000 + account_sz)
1247 .with_memory_units(10_000)
1248 .with_state_units(10_000)
1249 .add_rw_account(target_account);
1250
1251 let instruction_data = build_test_uploader_create_instruction(
1252 target_account_idx,
1253 account_sz,
1254 seed,
1255 is_ephemeral,
1256 state_proof,
1257 )?;
1258
1259 let tx = tx.with_instructions(instruction_data);
1260 Ok(tx)
1261 }
1262
1263 pub fn build_test_uploader_write(
1265 fee_payer: TnPubkey,
1266 test_uploader_program: TnPubkey,
1267 target_account: TnPubkey,
1268 offset: u32,
1269 data: &[u8],
1270 fee: u64,
1271 nonce: u64,
1272 start_slot: u64,
1273 ) -> Result<Transaction> {
1274 let target_account_idx = 2u16;
1276
1277 let tx = Transaction::new(fee_payer, test_uploader_program, fee, nonce)
1278 .with_start_slot(start_slot)
1279 .with_expiry_after(10_000)
1280 .with_compute_units(100_000 + 18 * data.len() as u32)
1281 .with_memory_units(10_000)
1282 .with_state_units(10_000)
1283 .add_rw_account(target_account);
1284
1285 let instruction_data =
1286 build_test_uploader_write_instruction(target_account_idx, offset, data)?;
1287
1288 let tx = tx.with_instructions(instruction_data);
1289 Ok(tx)
1290 }
1291
1292 pub fn build_decompress2(
1294 fee_payer: TnPubkey,
1295 program: TnPubkey,
1296 target_account: TnPubkey,
1297 meta_account: TnPubkey,
1298 data_account: TnPubkey,
1299 data_offset: u32,
1300 state_proof: &[u8],
1301 fee: u64,
1302 nonce: u64,
1303 start_slot: u64,
1304 data_sz: u32,
1305 ) -> Result<Transaction> {
1306 let mut tx = Transaction::new(fee_payer, program, fee, nonce)
1308 .with_start_slot(start_slot)
1309 .with_expiry_after(100)
1310 .with_compute_units(10_000 + 2 * data_sz)
1311 .with_memory_units(10_000)
1312 .with_state_units(10);
1313
1314 let target_account_idx = 2u16;
1316 tx = tx.add_rw_account(target_account);
1317
1318 let mut meta_account_idx = 0u16;
1319 let mut data_account_idx = 0u16;
1320
1321 if meta_account == data_account {
1323 let account_idx = 3u16;
1325 meta_account_idx = account_idx;
1326 data_account_idx = account_idx;
1327 tx = tx.add_r_account(meta_account);
1328 } else {
1329 let mut read_accounts = vec![(meta_account, "meta"), (data_account, "data")];
1331 read_accounts.sort_by(|a, b| a.0.cmp(&b.0));
1332
1333 for (i, (account, account_type)) in read_accounts.iter().enumerate() {
1334 let idx = (3 + i) as u16; match *account_type {
1336 "meta" => {
1337 meta_account_idx = idx;
1338 tx = tx.add_r_account(*account);
1339 }
1340 "data" => {
1341 data_account_idx = idx;
1342 tx = tx.add_r_account(*account);
1343 }
1344 _ => unreachable!(),
1345 }
1346 }
1347 }
1348
1349 let instruction_data = build_decompress2_instruction(
1350 target_account_idx,
1351 meta_account_idx,
1352 data_account_idx,
1353 data_offset,
1354 state_proof,
1355 )?;
1356
1357 tx = tx.with_instructions(instruction_data);
1358 Ok(tx)
1359 }
1360}
1361
1362fn build_uploader_create_instruction(
1364 buffer_account_idx: u16,
1365 meta_account_idx: u16,
1366 authority_account_idx: u16,
1367 buffer_size: u32,
1368 expected_hash: [u8; 32],
1369 seed: &[u8],
1370) -> Result<Vec<u8>> {
1371 let mut instruction = Vec::new();
1372
1373 instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_CREATE.to_le_bytes());
1375
1376 let args = UploaderCreateArgs {
1378 buffer_account_idx,
1379 meta_account_idx,
1380 authority_account_idx,
1381 buffer_account_sz: buffer_size,
1382 expected_account_hash: expected_hash,
1383 seed_len: seed.len() as u32,
1384 };
1385
1386 let args_bytes = unsafe {
1388 std::slice::from_raw_parts(
1389 &args as *const _ as *const u8,
1390 std::mem::size_of::<UploaderCreateArgs>(),
1391 )
1392 };
1393 instruction.extend_from_slice(args_bytes);
1394
1395 instruction.extend_from_slice(seed);
1397
1398 Ok(instruction)
1399}
1400
1401fn build_uploader_write_instruction(
1403 buffer_account_idx: u16,
1404 meta_account_idx: u16,
1405 data: &[u8],
1406 offset: u32,
1407) -> Result<Vec<u8>> {
1408 let mut instruction = Vec::new();
1409
1410 instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_WRITE.to_le_bytes());
1412
1413 let args = UploaderWriteArgs {
1415 buffer_account_idx,
1416 meta_account_idx,
1417 data_len: data.len() as u32,
1418 data_offset: offset,
1419 };
1420
1421 let args_bytes = unsafe {
1423 std::slice::from_raw_parts(
1424 &args as *const _ as *const u8,
1425 std::mem::size_of::<UploaderWriteArgs>(),
1426 )
1427 };
1428 instruction.extend_from_slice(args_bytes);
1429
1430 instruction.extend_from_slice(data);
1432
1433 Ok(instruction)
1434}
1435
1436fn build_uploader_finalize_instruction(
1438 buffer_account_idx: u16,
1439 meta_account_idx: u16,
1440 expected_hash: [u8; 32],
1441) -> Result<Vec<u8>> {
1442 let mut instruction = Vec::new();
1443
1444 instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_FINALIZE.to_le_bytes());
1446
1447 let args = UploaderFinalizeArgs {
1449 buffer_account_idx,
1450 meta_account_idx,
1451 expected_account_hash: expected_hash,
1452 };
1453
1454 let args_bytes = unsafe {
1456 std::slice::from_raw_parts(
1457 &args as *const _ as *const u8,
1458 std::mem::size_of::<UploaderFinalizeArgs>(),
1459 )
1460 };
1461 instruction.extend_from_slice(args_bytes);
1462
1463 Ok(instruction)
1464}
1465
1466fn build_uploader_destroy_instruction(
1468 buffer_account_idx: u16,
1469 meta_account_idx: u16,
1470) -> Result<Vec<u8>> {
1471 let mut instruction = Vec::new();
1472
1473 instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_DESTROY.to_le_bytes());
1475
1476 let args = UploaderDestroyArgs {
1478 buffer_account_idx,
1479 meta_account_idx,
1480 };
1481
1482 let args_bytes = unsafe {
1484 std::slice::from_raw_parts(
1485 &args as *const _ as *const u8,
1486 std::mem::size_of::<UploaderDestroyArgs>(),
1487 )
1488 };
1489 instruction.extend_from_slice(args_bytes);
1490
1491 Ok(instruction)
1492}
1493
1494fn build_manager_create_instruction(
1496 discriminant: u8,
1497 meta_account_idx: u16,
1498 program_account_idx: u16,
1499 srcbuf_account_idx: u16,
1500 authority_account_idx: u16,
1501 srcbuf_offset: u32,
1502 srcbuf_size: u32,
1503 seed: &[u8],
1504 proof: Option<&[u8]>,
1505) -> Result<Vec<u8>> {
1506 let mut instruction = Vec::new();
1507
1508 let args = ManagerCreateArgs {
1510 discriminant,
1511 meta_account_idx,
1512 program_account_idx,
1513 srcbuf_account_idx,
1514 srcbuf_offset,
1515 srcbuf_size,
1516 authority_account_idx,
1517 seed_len: seed.len() as u32,
1518 };
1519
1520 let args_bytes = unsafe {
1522 std::slice::from_raw_parts(
1523 &args as *const ManagerCreateArgs as *const u8,
1524 std::mem::size_of::<ManagerCreateArgs>(),
1525 )
1526 };
1527 instruction.extend_from_slice(args_bytes);
1528
1529 instruction.extend_from_slice(seed);
1531
1532 if let Some(proof_bytes) = proof {
1534 instruction.extend_from_slice(proof_bytes);
1535 }
1536
1537 Ok(instruction)
1538}
1539
1540fn build_manager_upgrade_instruction(
1542 meta_account_idx: u16,
1543 program_account_idx: u16,
1544 srcbuf_account_idx: u16,
1545 srcbuf_offset: u32,
1546 srcbuf_size: u32,
1547) -> Result<Vec<u8>> {
1548 let mut instruction = Vec::new();
1549
1550 let args = ManagerUpgradeArgs {
1551 discriminant: MANAGER_INSTRUCTION_UPGRADE,
1552 meta_account_idx,
1553 program_account_idx,
1554 srcbuf_account_idx,
1555 srcbuf_offset,
1556 srcbuf_size,
1557 };
1558
1559 let args_bytes = unsafe {
1560 std::slice::from_raw_parts(
1561 &args as *const ManagerUpgradeArgs as *const u8,
1562 std::mem::size_of::<ManagerUpgradeArgs>(),
1563 )
1564 };
1565 instruction.extend_from_slice(args_bytes);
1566
1567 Ok(instruction)
1568}
1569
1570fn build_manager_set_pause_instruction(
1572 meta_account_idx: u16,
1573 program_account_idx: u16,
1574 is_paused: bool,
1575) -> Result<Vec<u8>> {
1576 let mut instruction = Vec::new();
1577
1578 let args = ManagerSetPauseArgs {
1579 discriminant: MANAGER_INSTRUCTION_SET_PAUSE,
1580 meta_account_idx,
1581 program_account_idx,
1582 is_paused: if is_paused { 1 } else { 0 },
1583 };
1584
1585 let args_bytes = unsafe {
1586 std::slice::from_raw_parts(
1587 &args as *const ManagerSetPauseArgs as *const u8,
1588 std::mem::size_of::<ManagerSetPauseArgs>(),
1589 )
1590 };
1591 instruction.extend_from_slice(args_bytes);
1592
1593 Ok(instruction)
1594}
1595
1596fn build_manager_header_instruction(
1598 discriminant: u8,
1599 meta_account_idx: u16,
1600 program_account_idx: u16,
1601) -> Result<Vec<u8>> {
1602 let mut instruction = Vec::new();
1603
1604 let args = ManagerHeaderArgs {
1605 discriminant,
1606 meta_account_idx,
1607 program_account_idx,
1608 };
1609
1610 let args_bytes = unsafe {
1611 std::slice::from_raw_parts(
1612 &args as *const ManagerHeaderArgs as *const u8,
1613 std::mem::size_of::<ManagerHeaderArgs>(),
1614 )
1615 };
1616 instruction.extend_from_slice(args_bytes);
1617
1618 Ok(instruction)
1619}
1620
1621fn build_manager_set_authority_instruction(
1623 meta_account_idx: u16,
1624 program_account_idx: u16,
1625 authority_candidate: [u8; 32],
1626) -> Result<Vec<u8>> {
1627 let mut instruction = Vec::new();
1628
1629 let args = ManagerSetAuthorityArgs {
1630 discriminant: MANAGER_INSTRUCTION_SET_AUTHORITY,
1631 meta_account_idx,
1632 program_account_idx,
1633 authority_candidate,
1634 };
1635
1636 let args_bytes = unsafe {
1637 std::slice::from_raw_parts(
1638 &args as *const ManagerSetAuthorityArgs as *const u8,
1639 std::mem::size_of::<ManagerSetAuthorityArgs>(),
1640 )
1641 };
1642 instruction.extend_from_slice(args_bytes);
1643
1644 Ok(instruction)
1645}
1646
1647fn build_test_uploader_create_instruction(
1649 account_idx: u16,
1650 account_sz: u32,
1651 seed: &[u8],
1652 is_ephemeral: bool,
1653 state_proof: Option<&[u8]>,
1654) -> Result<Vec<u8>> {
1655 let mut instruction = Vec::new();
1656
1657 instruction.push(TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_CREATE);
1659
1660 let args = TestUploaderCreateArgs {
1662 account_idx,
1663 is_ephemeral: if is_ephemeral { 1u8 } else { 0u8 },
1664 account_sz,
1665 seed_len: seed.len() as u32,
1666 };
1667
1668 let args_bytes = unsafe {
1670 std::slice::from_raw_parts(
1671 &args as *const _ as *const u8,
1672 std::mem::size_of::<TestUploaderCreateArgs>(),
1673 )
1674 };
1675 instruction.extend_from_slice(args_bytes);
1676
1677 instruction.extend_from_slice(seed);
1679
1680 if let Some(proof) = state_proof {
1682 instruction.extend_from_slice(proof);
1683 }
1684
1685 Ok(instruction)
1686}
1687
1688fn build_test_uploader_write_instruction(
1690 target_account_idx: u16,
1691 target_offset: u32,
1692 data: &[u8],
1693) -> Result<Vec<u8>> {
1694 let mut instruction = Vec::new();
1695
1696 instruction.push(TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_WRITE);
1698
1699 let args = TestUploaderWriteArgs {
1701 target_account_idx,
1702 target_offset,
1703 data_len: data.len() as u32,
1704 };
1705
1706 let args_bytes = unsafe {
1708 std::slice::from_raw_parts(
1709 &args as *const _ as *const u8,
1710 std::mem::size_of::<TestUploaderWriteArgs>(),
1711 )
1712 };
1713 instruction.extend_from_slice(args_bytes);
1714
1715 instruction.extend_from_slice(data);
1717
1718 Ok(instruction)
1719}
1720
1721pub fn build_decompress2_instruction(
1723 target_account_idx: u16,
1724 meta_account_idx: u16,
1725 data_account_idx: u16,
1726 data_offset: u32,
1727 state_proof: &[u8],
1728) -> Result<Vec<u8>> {
1729 let mut instruction = Vec::new();
1730
1731 instruction.push(0x08);
1733
1734 let args = SystemProgramDecompress2Args {
1736 target_account_idx,
1737 meta_account_idx,
1738 data_account_idx,
1739 data_offset,
1740 };
1741
1742 let args_bytes = unsafe {
1744 std::slice::from_raw_parts(
1745 &args as *const _ as *const u8,
1746 std::mem::size_of::<SystemProgramDecompress2Args>(),
1747 )
1748 };
1749 instruction.extend_from_slice(args_bytes);
1750
1751 instruction.extend_from_slice(state_proof);
1753
1754 Ok(instruction)
1755}
1756
1757pub const TOKEN_INSTRUCTION_INITIALIZE_MINT: u8 = 0x00;
1759pub const TOKEN_INSTRUCTION_INITIALIZE_ACCOUNT: u8 = 0x01;
1760pub const TOKEN_INSTRUCTION_TRANSFER: u8 = 0x02;
1761pub const TOKEN_INSTRUCTION_MINT_TO: u8 = 0x03;
1762pub const TOKEN_INSTRUCTION_BURN: u8 = 0x04;
1763pub const TOKEN_INSTRUCTION_CLOSE_ACCOUNT: u8 = 0x05;
1764pub const TOKEN_INSTRUCTION_FREEZE_ACCOUNT: u8 = 0x06;
1765pub const TOKEN_INSTRUCTION_THAW_ACCOUNT: u8 = 0x07;
1766
1767fn add_sorted_accounts(tx: Transaction, accounts: &[(TnPubkey, bool)]) -> (Transaction, Vec<u16>) {
1769 let mut sorted_accounts: Vec<_> = accounts.iter().enumerate().collect();
1770 sorted_accounts.sort_by(|a, b| a.1.0.cmp(&b.1.0));
1771
1772 let mut updated_tx = tx;
1773 let mut indices = vec![0u16; accounts.len()];
1774
1775 for (original_idx, (i, (account, writable))) in sorted_accounts.iter().enumerate() {
1776 let account_idx = (original_idx + 2) as u16; indices[*i] = account_idx;
1778
1779 if *writable {
1780 updated_tx = updated_tx.add_rw_account(*account);
1781 } else {
1782 updated_tx = updated_tx.add_r_account(*account);
1783 }
1784 }
1785
1786 (updated_tx, indices)
1787}
1788
1789fn add_sorted_rw_accounts(mut tx: Transaction, accounts: &[TnPubkey]) -> (Transaction, Vec<u16>) {
1791 if accounts.is_empty() {
1792 return (tx, Vec::new());
1793 }
1794
1795 let mut sorted: Vec<(usize, TnPubkey)> = accounts.iter().cloned().enumerate().collect();
1796 sorted.sort_by(|a, b| a.1.cmp(&b.1));
1797
1798 let mut indices = vec![0u16; accounts.len()];
1799 for (pos, (orig_idx, account)) in sorted.into_iter().enumerate() {
1800 let idx = (2 + pos) as u16;
1801 indices[orig_idx] = idx;
1802 tx = tx.add_rw_account(account);
1803 }
1804
1805 (tx, indices)
1806}
1807
1808fn add_sorted_ro_accounts(
1809 mut tx: Transaction,
1810 base_idx: u16,
1811 accounts: &[TnPubkey],
1812) -> (Transaction, Vec<u16>) {
1813 if accounts.is_empty() {
1814 return (tx, Vec::new());
1815 }
1816
1817 let mut sorted: Vec<(usize, TnPubkey)> = accounts.iter().cloned().enumerate().collect();
1818 sorted.sort_by(|a, b| a.1.cmp(&b.1));
1819
1820 let mut indices = vec![0u16; accounts.len()];
1821 for (pos, (orig_idx, account)) in sorted.into_iter().enumerate() {
1822 let idx = base_idx + pos as u16;
1823 indices[orig_idx] = idx;
1824 tx = tx.add_r_account(account);
1825 }
1826
1827 (tx, indices)
1828}
1829
1830impl TransactionBuilder {
1831 pub fn build_token_initialize_mint(
1833 fee_payer: TnPubkey,
1834 token_program: TnPubkey,
1835 mint_account: TnPubkey,
1836 mint_authority: TnPubkey,
1837 freeze_authority: Option<TnPubkey>,
1838 decimals: u8,
1839 ticker: &str,
1840 seed: [u8; 32],
1841 state_proof: Vec<u8>,
1842 fee: u64,
1843 nonce: u64,
1844 start_slot: u64,
1845 ) -> Result<Transaction> {
1846 let base_tx =
1847 Transaction::new(fee_payer, token_program, fee, nonce).with_start_slot(start_slot);
1848 let (tx, indices) = add_sorted_rw_accounts(base_tx, &[mint_account]);
1849 let mint_account_idx = indices[0];
1850
1851 let instruction_data = build_token_initialize_mint_instruction(
1852 mint_account_idx,
1853 decimals,
1854 mint_authority,
1855 freeze_authority,
1856 ticker,
1857 seed,
1858 state_proof,
1859 )?;
1860
1861 let tx = tx
1862 .with_instructions(instruction_data)
1863 .with_expiry_after(100)
1864 .with_compute_units(300_000)
1865 .with_state_units(10_000)
1866 .with_memory_units(10_000);
1867
1868 Ok(tx)
1869 }
1870
1871 pub fn build_token_initialize_account(
1873 fee_payer: TnPubkey,
1874 token_program: TnPubkey,
1875 token_account: TnPubkey,
1876 mint_account: TnPubkey,
1877 owner: TnPubkey,
1878 seed: [u8; 32],
1879 state_proof: Vec<u8>,
1880 fee: u64,
1881 nonce: u64,
1882 start_slot: u64,
1883 ) -> Result<Transaction> {
1884 let owner_is_fee_payer = owner == fee_payer;
1885
1886 let mut rw_accounts = vec![token_account];
1887 rw_accounts.sort();
1888
1889 let mut ro_accounts = vec![mint_account];
1890 if !owner_is_fee_payer {
1891 ro_accounts.push(owner);
1892 ro_accounts.sort();
1893 }
1894
1895 let mut tx =
1896 Transaction::new(fee_payer, token_program, fee, nonce).with_start_slot(start_slot);
1897
1898 let mut token_account_idx = 0u16;
1899 for (i, account) in rw_accounts.iter().enumerate() {
1900 let idx = (2 + i) as u16;
1901 if *account == token_account {
1902 token_account_idx = idx;
1903 }
1904 tx = tx.add_rw_account(*account);
1905 }
1906
1907 let base_ro_idx = 2 + rw_accounts.len() as u16;
1908 let mut mint_account_idx = 0u16;
1909 let mut owner_account_idx = if owner_is_fee_payer { 0u16 } else { 0u16 };
1910 for (i, account) in ro_accounts.iter().enumerate() {
1911 let idx = base_ro_idx + i as u16;
1912 if *account == mint_account {
1913 mint_account_idx = idx;
1914 } else if !owner_is_fee_payer && *account == owner {
1915 owner_account_idx = idx;
1916 }
1917 tx = tx.add_r_account(*account);
1918 }
1919
1920 let instruction_data = build_token_initialize_account_instruction(
1921 token_account_idx,
1922 mint_account_idx,
1923 owner_account_idx,
1924 seed,
1925 state_proof,
1926 )?;
1927
1928 let tx = tx
1929 .with_instructions(instruction_data)
1930 .with_expiry_after(100)
1931 .with_compute_units(300_000)
1932 .with_state_units(10_000)
1933 .with_memory_units(10_000);
1934
1935 Ok(tx)
1936 }
1937
1938 pub fn build_token_transfer(
1940 fee_payer: TnPubkey,
1941 token_program: TnPubkey,
1942 source_account: TnPubkey,
1943 dest_account: TnPubkey,
1944 _authority: TnPubkey,
1945 amount: u64,
1946 fee: u64,
1947 nonce: u64,
1948 start_slot: u64,
1949 ) -> Result<Transaction> {
1950 let mut tx = Transaction::new(fee_payer, token_program, fee, nonce)
1951 .with_start_slot(start_slot)
1952 .with_expiry_after(100)
1953 .with_compute_units(300_000)
1954 .with_state_units(10_000)
1955 .with_memory_units(10_000);
1956
1957 let is_self_transfer = source_account == dest_account;
1958 let (source_account_idx, dest_account_idx) = if is_self_transfer {
1959 tx = tx.add_rw_account(source_account);
1961 (2u16, 2u16)
1962 } else {
1963 let accounts = &[(source_account, true), (dest_account, true)];
1965 let (updated_tx, indices) = add_sorted_accounts(tx, accounts);
1966 tx = updated_tx;
1967 (indices[0], indices[1])
1968 };
1969
1970 let instruction_data =
1974 build_token_transfer_instruction(source_account_idx, dest_account_idx, amount)?;
1975
1976 Ok(tx.with_instructions(instruction_data))
1977 }
1978
1979 pub fn build_token_mint_to(
1981 fee_payer: TnPubkey,
1982 token_program: TnPubkey,
1983 mint_account: TnPubkey,
1984 dest_account: TnPubkey,
1985 authority: TnPubkey,
1986 amount: u64,
1987 fee: u64,
1988 nonce: u64,
1989 start_slot: u64,
1990 ) -> Result<Transaction> {
1991 let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
1992 .with_start_slot(start_slot)
1993 .with_expiry_after(100)
1994 .with_compute_units(300_000)
1995 .with_state_units(10_000)
1996 .with_memory_units(10_000);
1997
1998 let (tx_after_rw, rw_indices) =
1999 add_sorted_rw_accounts(base_tx, &[mint_account, dest_account]);
2000 let mint_account_idx = rw_indices[0];
2001 let dest_account_idx = rw_indices[1];
2002
2003 let mut tx = tx_after_rw;
2004 let authority_account_idx = if authority == fee_payer {
2005 0u16
2006 } else {
2007 let base_ro_idx = 2 + rw_indices.len() as u16;
2008 let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
2009 tx = tx_after_ro;
2010 ro_indices[0]
2011 };
2012
2013 let instruction_data = build_token_mint_to_instruction(
2014 mint_account_idx,
2015 dest_account_idx,
2016 authority_account_idx,
2017 amount,
2018 )?;
2019
2020 Ok(tx.with_instructions(instruction_data))
2021 }
2022
2023 pub fn build_token_burn(
2025 fee_payer: TnPubkey,
2026 token_program: TnPubkey,
2027 token_account: TnPubkey,
2028 mint_account: TnPubkey,
2029 authority: TnPubkey,
2030 amount: u64,
2031 fee: u64,
2032 nonce: u64,
2033 start_slot: u64,
2034 ) -> Result<Transaction> {
2035 let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
2036 .with_start_slot(start_slot)
2037 .with_expiry_after(100)
2038 .with_compute_units(300_000)
2039 .with_state_units(10_000)
2040 .with_memory_units(10_000);
2041
2042 let (tx_after_rw, rw_indices) =
2043 add_sorted_rw_accounts(base_tx, &[token_account, mint_account]);
2044 let token_account_idx = rw_indices[0];
2045 let mint_account_idx = rw_indices[1];
2046
2047 let mut tx = tx_after_rw;
2048 let authority_account_idx = if authority == fee_payer {
2049 0u16
2050 } else {
2051 let base_ro_idx = 2 + rw_indices.len() as u16;
2052 let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
2053 tx = tx_after_ro;
2054 ro_indices[0]
2055 };
2056
2057 let instruction_data = build_token_burn_instruction(
2058 token_account_idx,
2059 mint_account_idx,
2060 authority_account_idx,
2061 amount,
2062 )?;
2063
2064 Ok(tx.with_instructions(instruction_data))
2065 }
2066
2067 pub fn build_token_freeze_account(
2069 fee_payer: TnPubkey,
2070 token_program: TnPubkey,
2071 token_account: TnPubkey,
2072 mint_account: TnPubkey,
2073 authority: TnPubkey,
2074 fee: u64,
2075 nonce: u64,
2076 start_slot: u64,
2077 ) -> Result<Transaction> {
2078 let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
2079 .with_start_slot(start_slot)
2080 .with_expiry_after(100)
2081 .with_compute_units(300_000)
2082 .with_state_units(10_000)
2083 .with_memory_units(10_000);
2084
2085 let (tx_after_rw, rw_indices) =
2086 add_sorted_rw_accounts(base_tx, &[token_account, mint_account]);
2087 let token_account_idx = rw_indices[0];
2088 let mint_account_idx = rw_indices[1];
2089
2090 let mut tx = tx_after_rw;
2091 let authority_account_idx = if authority == fee_payer {
2092 0u16
2093 } else {
2094 let base_ro_idx = 2 + rw_indices.len() as u16;
2095 let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
2096 tx = tx_after_ro;
2097 ro_indices[0]
2098 };
2099
2100 let instruction_data = build_token_freeze_account_instruction(
2101 token_account_idx,
2102 mint_account_idx,
2103 authority_account_idx,
2104 )?;
2105
2106 Ok(tx.with_instructions(instruction_data))
2107 }
2108
2109 pub fn build_token_thaw_account(
2111 fee_payer: TnPubkey,
2112 token_program: TnPubkey,
2113 token_account: TnPubkey,
2114 mint_account: TnPubkey,
2115 authority: TnPubkey,
2116 fee: u64,
2117 nonce: u64,
2118 start_slot: u64,
2119 ) -> Result<Transaction> {
2120 let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
2121 .with_start_slot(start_slot)
2122 .with_expiry_after(100)
2123 .with_compute_units(300_000)
2124 .with_state_units(10_000)
2125 .with_memory_units(10_000);
2126
2127 let (tx_after_rw, rw_indices) =
2128 add_sorted_rw_accounts(base_tx, &[token_account, mint_account]);
2129 let token_account_idx = rw_indices[0];
2130 let mint_account_idx = rw_indices[1];
2131
2132 let mut tx = tx_after_rw;
2133 let authority_account_idx = if authority == fee_payer {
2134 0u16
2135 } else {
2136 let base_ro_idx = 2 + rw_indices.len() as u16;
2137 let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
2138 tx = tx_after_ro;
2139 ro_indices[0]
2140 };
2141
2142 let instruction_data = build_token_thaw_account_instruction(
2143 token_account_idx,
2144 mint_account_idx,
2145 authority_account_idx,
2146 )?;
2147
2148 Ok(tx.with_instructions(instruction_data))
2149 }
2150
2151 pub fn build_token_close_account(
2153 fee_payer: TnPubkey,
2154 token_program: TnPubkey,
2155 token_account: TnPubkey,
2156 destination: TnPubkey,
2157 authority: TnPubkey,
2158 fee: u64,
2159 nonce: u64,
2160 start_slot: u64,
2161 ) -> Result<Transaction> {
2162 let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
2163 .with_start_slot(start_slot)
2164 .with_expiry_after(100)
2165 .with_compute_units(300_000)
2166 .with_state_units(10_000)
2167 .with_memory_units(10_000);
2168
2169 let mut rw_accounts = vec![token_account];
2170 let destination_in_accounts = destination != fee_payer;
2171 if destination_in_accounts {
2172 rw_accounts.push(destination);
2173 }
2174
2175 let (tx_after_rw, rw_indices) = add_sorted_rw_accounts(base_tx, &rw_accounts);
2176 let token_account_idx = rw_indices[0];
2177 let destination_idx = if destination_in_accounts {
2178 rw_indices[1]
2179 } else {
2180 0u16
2181 };
2182
2183 let mut tx = tx_after_rw;
2184 let authority_account_idx = if authority == fee_payer {
2185 0u16
2186 } else {
2187 let base_ro_idx = 2 + rw_indices.len() as u16;
2188 let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
2189 tx = tx_after_ro;
2190 ro_indices[0]
2191 };
2192
2193 let instruction_data = build_token_close_account_instruction(
2194 token_account_idx,
2195 destination_idx,
2196 authority_account_idx,
2197 )?;
2198
2199 Ok(tx.with_instructions(instruction_data))
2200 }
2201}
2202
2203fn build_token_initialize_mint_instruction(
2205 mint_account_idx: u16,
2206 decimals: u8,
2207 mint_authority: TnPubkey,
2208 freeze_authority: Option<TnPubkey>,
2209 ticker: &str,
2210 seed: [u8; 32],
2211 state_proof: Vec<u8>,
2212) -> Result<Vec<u8>> {
2213 let mut instruction_data = Vec::new();
2214
2215 instruction_data.push(TOKEN_INSTRUCTION_INITIALIZE_MINT);
2217
2218 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
2220
2221 instruction_data.push(decimals);
2223
2224 instruction_data.extend_from_slice(&mint_authority);
2226
2227 let (freeze_auth, has_freeze_auth) = match freeze_authority {
2229 Some(auth) => (auth, 1u8),
2230 None => ([0u8; 32], 0u8),
2231 };
2232 instruction_data.extend_from_slice(&freeze_auth);
2233 instruction_data.push(has_freeze_auth);
2234
2235 let ticker_bytes = ticker.as_bytes();
2237 if ticker_bytes.len() > 8 {
2238 return Err(anyhow::anyhow!("Ticker must be 8 characters or less"));
2239 }
2240
2241 instruction_data.push(ticker_bytes.len() as u8);
2242 let mut ticker_padded = [0u8; 8];
2243 ticker_padded[..ticker_bytes.len()].copy_from_slice(ticker_bytes);
2244 instruction_data.extend_from_slice(&ticker_padded);
2245
2246 instruction_data.extend_from_slice(&seed);
2248
2249 instruction_data.extend_from_slice(&state_proof);
2251
2252 Ok(instruction_data)
2253}
2254
2255fn build_token_initialize_account_instruction(
2257 token_account_idx: u16,
2258 mint_account_idx: u16,
2259 owner_account_idx: u16,
2260 seed: [u8; 32],
2261 state_proof: Vec<u8>,
2262) -> Result<Vec<u8>> {
2263 let mut instruction_data = Vec::new();
2264
2265 instruction_data.push(TOKEN_INSTRUCTION_INITIALIZE_ACCOUNT);
2267
2268 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
2270
2271 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
2273
2274 instruction_data.extend_from_slice(&owner_account_idx.to_le_bytes());
2276
2277 instruction_data.extend_from_slice(&seed);
2279
2280 instruction_data.extend_from_slice(&state_proof);
2282
2283 Ok(instruction_data)
2284}
2285
2286fn build_token_transfer_instruction(
2288 source_account_idx: u16,
2289 dest_account_idx: u16,
2290 amount: u64,
2291) -> Result<Vec<u8>> {
2292 let mut instruction_data = Vec::new();
2293
2294 instruction_data.push(TOKEN_INSTRUCTION_TRANSFER);
2296
2297 instruction_data.extend_from_slice(&source_account_idx.to_le_bytes());
2299
2300 instruction_data.extend_from_slice(&dest_account_idx.to_le_bytes());
2302
2303 instruction_data.extend_from_slice(&amount.to_le_bytes());
2305
2306 Ok(instruction_data)
2307}
2308
2309fn build_token_mint_to_instruction(
2311 mint_account_idx: u16,
2312 dest_account_idx: u16,
2313 authority_idx: u16,
2314 amount: u64,
2315) -> Result<Vec<u8>> {
2316 let mut instruction_data = Vec::new();
2317
2318 instruction_data.push(TOKEN_INSTRUCTION_MINT_TO);
2320
2321 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
2323
2324 instruction_data.extend_from_slice(&dest_account_idx.to_le_bytes());
2326
2327 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
2329
2330 instruction_data.extend_from_slice(&amount.to_le_bytes());
2332
2333 Ok(instruction_data)
2334}
2335
2336fn build_token_burn_instruction(
2338 token_account_idx: u16,
2339 mint_account_idx: u16,
2340 authority_idx: u16,
2341 amount: u64,
2342) -> Result<Vec<u8>> {
2343 let mut instruction_data = Vec::new();
2344
2345 instruction_data.push(TOKEN_INSTRUCTION_BURN);
2347
2348 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
2350
2351 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
2353
2354 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
2356
2357 instruction_data.extend_from_slice(&amount.to_le_bytes());
2359
2360 Ok(instruction_data)
2361}
2362
2363fn build_token_freeze_account_instruction(
2365 token_account_idx: u16,
2366 mint_account_idx: u16,
2367 authority_idx: u16,
2368) -> Result<Vec<u8>> {
2369 let mut instruction_data = Vec::new();
2370
2371 instruction_data.push(TOKEN_INSTRUCTION_FREEZE_ACCOUNT);
2373
2374 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
2376
2377 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
2379
2380 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
2382
2383 Ok(instruction_data)
2384}
2385
2386fn build_token_thaw_account_instruction(
2388 token_account_idx: u16,
2389 mint_account_idx: u16,
2390 authority_idx: u16,
2391) -> Result<Vec<u8>> {
2392 let mut instruction_data = Vec::new();
2393
2394 instruction_data.push(TOKEN_INSTRUCTION_THAW_ACCOUNT);
2396
2397 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
2399
2400 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
2402
2403 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
2405
2406 Ok(instruction_data)
2407}
2408
2409fn build_token_close_account_instruction(
2411 token_account_idx: u16,
2412 destination_idx: u16,
2413 authority_idx: u16,
2414) -> Result<Vec<u8>> {
2415 let mut instruction_data = Vec::new();
2416
2417 instruction_data.push(TOKEN_INSTRUCTION_CLOSE_ACCOUNT);
2419
2420 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
2422
2423 instruction_data.extend_from_slice(&destination_idx.to_le_bytes());
2425
2426 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
2428
2429 Ok(instruction_data)
2430}