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
9#[derive(Debug, Clone)]
10pub struct TransactionBuilder {
11 }
16
17impl TransactionBuilder {
18 pub fn build_create_with_fee_payer_proof(
20 fee_payer: TnPubkey,
21 start_slot: u64,
22 fee_payer_state_proof: &StateProof,
23 ) -> Result<Transaction> {
24 let mut noop_program = [0u8; 32];
25 noop_program[31] = 0x02;
26 let tx = Transaction::new(fee_payer, noop_program, 0, 0)
27 .with_fee_payer_state_proof(fee_payer_state_proof)
28 .with_start_slot(start_slot)
29 .with_expiry_after(100)
30 .with_compute_units(10_000)
31 .with_memory_units(10_000)
32 .with_state_units(10_000);
33 Ok(tx)
34 }
35
36 pub fn build_transfer(
38 fee_payer: TnPubkey,
39 program: TnPubkey,
40 to_account: TnPubkey,
41 amount: u64,
42 fee: u64,
43 nonce: u64,
44 start_slot: u64,
45 ) -> Result<Transaction> {
46 let from_account_idx = 0u16; let to_account_idx = 2u16; let instruction_data =
51 build_transfer_instruction(from_account_idx, to_account_idx, amount)?;
52
53 let tx = Transaction::new(fee_payer, program, fee, nonce)
54 .with_start_slot(start_slot)
55 .add_rw_account(to_account) .with_instructions(instruction_data)
57 .with_expiry_after(100)
58 .with_compute_units(10000)
59 .with_memory_units(10000)
60 .with_state_units(10000);
61
62 Ok(tx)
63 }
64
65 pub fn build_create_account(
67 fee_payer: TnPubkey,
68 program: TnPubkey,
69 target_account: TnPubkey,
70 seed: &str,
71 state_proof: Option<&[u8]>,
72 fee: u64,
73 nonce: u64,
74 start_slot: u64,
75 ) -> Result<Transaction> {
76 let target_account_idx = 2u16; let instruction_data =
79 build_create_account_instruction(target_account_idx, seed, state_proof)?;
80
81 let tx = Transaction::new(fee_payer, program, fee, nonce)
82 .with_start_slot(start_slot)
83 .add_rw_account(target_account)
84 .with_instructions(instruction_data)
85 .with_expiry_after(100)
86 .with_compute_units(10_000)
87 .with_memory_units(10_000)
88 .with_state_units(10_000);
89
90 Ok(tx)
91 }
92
93 pub fn build_create_ephemeral_account(
95 fee_payer: TnPubkey,
96 program: TnPubkey,
97 target_account: TnPubkey,
98 seed: &[u8; 32],
99 fee: u64,
100 nonce: u64,
101 start_slot: u64,
102 ) -> Result<Transaction> {
103 let target_account_idx = 2u16; let instruction_data = build_ephemeral_account_instruction(target_account_idx, seed)?;
106
107 let tx = Transaction::new(fee_payer, program, fee, nonce)
108 .with_start_slot(start_slot)
109 .add_rw_account(target_account)
110 .with_instructions(instruction_data)
111 .with_expiry_after(100)
112 .with_compute_units(50_000)
113 .with_memory_units(10_000)
114 .with_state_units(10_000);
115 Ok(tx)
116 }
117
118 pub fn build_resize_account(
120 fee_payer: TnPubkey,
121 program: TnPubkey,
122 target_account: TnPubkey,
123 new_size: u64,
124 fee: u64,
125 nonce: u64,
126 start_slot: u64,
127 ) -> Result<Transaction> {
128 let target_account_idx = 2u16; let instruction_data = build_resize_instruction(target_account_idx, new_size)?;
131
132 let tx = Transaction::new(fee_payer, program, fee, nonce)
133 .with_start_slot(start_slot)
134 .with_expiry_after(100)
135 .with_compute_units(100032)
136 .with_state_units(1 + new_size.checked_div(4096).unwrap() as u16)
137 .with_memory_units(10000)
138 .add_rw_account(target_account)
139 .with_instructions(instruction_data)
140 .with_expiry_after(100)
141 .with_compute_units(10_000 + 2 * new_size as u32)
142 .with_memory_units(10_000)
143 .with_state_units(10_000);
144
145 Ok(tx)
146 }
147
148 pub fn build_compress_account(
150 fee_payer: TnPubkey,
151 program: TnPubkey,
152 target_account: TnPubkey,
153 state_proof: &[u8],
154 fee: u64,
155 nonce: u64,
156 start_slot: u64,
157 account_size: u32,
158 ) -> Result<Transaction> {
159 let target_account_idx = 2u16; let instruction_data = build_compress_instruction(target_account_idx, state_proof)?;
162
163 let tx = Transaction::new(fee_payer, program, fee, nonce)
164 .with_start_slot(start_slot)
165 .with_may_compress_account()
166 .add_rw_account(target_account)
167 .with_instructions(instruction_data)
168 .with_expiry_after(100)
169 .with_compute_units(100_300 + account_size * 2)
170 .with_memory_units(10000)
171 .with_state_units(10000);
172
173 Ok(tx)
174 }
175
176 pub fn build_decompress_account(
178 fee_payer: TnPubkey,
179 program: TnPubkey,
180 target_account: TnPubkey,
181 account_data: &[u8],
182 state_proof: &[u8],
183 fee: u64,
184 nonce: u64,
185 start_slot: u64,
186 ) -> Result<Transaction> {
187 let target_account_idx = 2u16; let instruction_data =
190 build_decompress_instruction(target_account_idx, account_data, state_proof)?;
191
192 let tx = Transaction::new(fee_payer, program, fee, nonce)
193 .with_start_slot(start_slot)
194 .add_rw_account(target_account)
195 .with_instructions(instruction_data)
196 .with_compute_units(100_300 + account_data.len() as u32 * 2)
197 .with_state_units(10_000)
198 .with_memory_units(10_000)
199 .with_expiry_after(100);
200 Ok(tx)
201 }
202
203 pub fn build_write_data(
205 fee_payer: TnPubkey,
206 program: TnPubkey,
207 target_account: TnPubkey,
208 offset: u16,
209 data: &[u8],
210 fee: u64,
211 nonce: u64,
212 start_slot: u64,
213 ) -> Result<Transaction> {
214 let target_account_idx = 2u16; let instruction_data = build_write_instruction(target_account_idx, offset, data)?;
217
218 let tx = Transaction::new(fee_payer, program, fee, nonce)
219 .with_start_slot(start_slot)
220 .with_expiry_after(100)
221 .with_compute_units(100045)
222 .with_state_units(10000)
223 .with_memory_units(10000)
224 .add_rw_account(target_account)
225 .with_instructions(instruction_data);
226
227 Ok(tx)
228 }
229}
230
231fn build_transfer_instruction(
233 from_account_idx: u16,
234 to_account_idx: u16,
235 amount: u64,
236) -> Result<Vec<u8>> {
237 let mut instruction = Vec::new();
238
239 instruction.push(0x03);
241
242 instruction.extend_from_slice(&from_account_idx.to_le_bytes());
244
245 instruction.extend_from_slice(&to_account_idx.to_le_bytes());
247
248 instruction.extend_from_slice(&amount.to_le_bytes());
250
251 Ok(instruction)
252}
253
254fn build_create_account_instruction(
256 target_account_idx: u16,
257 seed: &str,
258 state_proof: Option<&[u8]>,
259) -> Result<Vec<u8>> {
260 let mut instruction = Vec::new();
261
262 instruction.push(0x00);
264
265 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
267
268 let seed_bytes =
270 hex::decode(seed).map_err(|e| anyhow::anyhow!("Failed to decode hex seed: {}", e))?;
271
272 instruction.extend_from_slice(&(seed_bytes.len() as u64).to_le_bytes());
274
275 let has_proof = state_proof.is_some();
277 instruction.push(if has_proof { 1u8 } else { 0u8 });
278
279 instruction.extend_from_slice(&seed_bytes);
281
282 if let Some(proof) = state_proof {
284 instruction.extend_from_slice(proof);
285 }
286
287 Ok(instruction)
288}
289
290fn build_ephemeral_account_instruction(
292 target_account_idx: u16,
293 seed: &[u8; 32],
294) -> Result<Vec<u8>> {
295 let mut instruction = Vec::new();
296
297 instruction.push(0x01);
299
300 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
302
303 instruction.extend_from_slice(&(seed.len() as u64).to_le_bytes());
305
306 instruction.extend_from_slice(seed);
308
309 Ok(instruction)
310}
311
312fn build_resize_instruction(target_account_idx: u16, new_size: u64) -> Result<Vec<u8>> {
314 let mut instruction = Vec::new();
315
316 instruction.push(0x04);
318
319 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
321
322 instruction.extend_from_slice(&new_size.to_le_bytes());
324
325 Ok(instruction)
326}
327
328fn build_write_instruction(target_account_idx: u16, offset: u16, data: &[u8]) -> Result<Vec<u8>> {
330 let mut instruction = Vec::new();
331
332 instruction.push(0xC8);
334
335 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
337
338 instruction.extend_from_slice(&offset.to_le_bytes());
340
341 instruction.extend_from_slice(&(data.len() as u16).to_le_bytes());
343
344 instruction.extend_from_slice(data);
346
347 Ok(instruction)
348}
349
350fn build_compress_instruction(target_account_idx: u16, state_proof: &[u8]) -> Result<Vec<u8>> {
352 let mut instruction = Vec::new();
353
354 instruction.push(0x05);
357
358 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
360
361 instruction.extend_from_slice(state_proof);
363
364 Ok(instruction)
365}
366
367fn build_decompress_instruction(
368 target_account_idx: u16,
369 account_data: &[u8],
370 state_proof: &[u8],
371) -> Result<Vec<u8>> {
372 let mut instruction = Vec::new();
373
374 instruction.push(0x06);
376
377 instruction.extend_from_slice(&target_account_idx.to_le_bytes());
379 instruction.extend_from_slice(&(account_data.len() as u64).to_le_bytes());
380
381 instruction.extend_from_slice(account_data);
383
384 instruction.extend_from_slice(state_proof);
386
387 Ok(instruction)
388}
389
390pub fn generate_ephemeral_address(seed: &str) -> Result<String> {
395 let owner_pubkey = [0u8; 32];
397
398 let seed_bytes =
400 hex::decode(seed).map_err(|e| anyhow::anyhow!("Failed to decode hex seed: {}", e))?;
401
402 let mut seed_32 = [0u8; 32];
404 let copy_len = std::cmp::min(seed_bytes.len(), 32);
405 seed_32[..copy_len].copy_from_slice(&seed_bytes[..copy_len]);
406
407 Ok(
409 crate::tn_public_address::create_program_defined_account_address_string(
410 &owner_pubkey,
411 true, &seed_32,
413 ),
414 )
415}
416
417pub fn generate_system_derived_address(seed: &str, is_ephemeral: bool) -> Result<String> {
418 let seed_bytes =
420 hex::decode(seed).map_err(|e| anyhow::anyhow!("Failed to decode hex seed: {}", e))?;
421
422 let pubkey = generate_derived_address(&seed_bytes, &[0u8; 32], is_ephemeral)?;
423
424 Ok(tn_pubkey_to_address_string(&pubkey))
425}
426
427pub fn generate_derived_address(
428 seed: &[u8],
429 owner_pubkey: &[u8; 32],
430 is_ephemeral: bool,
431) -> Result<[u8; 32]> {
432 use sha2::{Digest, Sha256};
433
434 let mut hasher = Sha256::new();
436
437 hasher.update(&owner_pubkey);
439
440 hasher.update(&[is_ephemeral as u8]);
442
443 hasher.update(&seed);
445
446 Ok(hasher.finalize().into())
448}
449
450#[cfg(test)]
451mod tests {
452 use super::*;
453
454 #[test]
455 fn test_ephemeral_address_generation() {
456 let hex_seed1 = hex::encode("test_seed_123");
458 let hex_seed2 = hex::encode("test_seed_123");
459 let hex_seed3 = hex::encode("different_seed");
460
461 let addr1 = generate_ephemeral_address(&hex_seed1).unwrap();
462 let addr2 = generate_ephemeral_address(&hex_seed2).unwrap();
463 let addr3 = generate_ephemeral_address(&hex_seed3).unwrap();
464
465 assert_eq!(addr1, addr2);
467
468 assert_ne!(addr1, addr3);
470
471 assert!(addr1.starts_with("ta"));
473 assert!(addr2.starts_with("ta"));
474 assert!(addr3.starts_with("ta"));
475
476 assert_eq!(addr1.len(), 46);
478 assert_eq!(addr2.len(), 46);
479 assert_eq!(addr3.len(), 46);
480 }
481}
482
483pub const TN_UPLOADER_PROGRAM_INSTRUCTION_CREATE: u32 = 0x00;
485pub const TN_UPLOADER_PROGRAM_INSTRUCTION_WRITE: u32 = 0x01;
486pub const TN_UPLOADER_PROGRAM_INSTRUCTION_DESTROY: u32 = 0x02;
487pub const TN_UPLOADER_PROGRAM_INSTRUCTION_FINALIZE: u32 = 0x03;
488
489#[repr(C, packed)]
491#[derive(Debug, Clone, Copy)]
492pub struct UploaderCreateArgs {
493 pub buffer_account_idx: u16,
494 pub meta_account_idx: u16,
495 pub authority_account_idx: u16,
496 pub buffer_account_sz: u32,
497 pub expected_account_hash: [u8; 32],
498 pub seed_len: u32,
499 }
501
502#[repr(C, packed)]
504#[derive(Debug, Clone, Copy)]
505pub struct UploaderWriteArgs {
506 pub buffer_account_idx: u16,
507 pub meta_account_idx: u16,
508 pub data_len: u32,
509 pub data_offset: u32,
510 }
512
513#[repr(C, packed)]
515#[derive(Debug, Clone, Copy)]
516pub struct UploaderFinalizeArgs {
517 pub buffer_account_idx: u16,
518 pub meta_account_idx: u16,
519 pub expected_account_hash: [u8; 32],
520}
521
522#[repr(C, packed)]
524#[derive(Debug, Clone, Copy)]
525pub struct UploaderDestroyArgs {
526 pub buffer_account_idx: u16,
527 pub meta_account_idx: u16,
528}
529
530pub const MANAGER_INSTRUCTION_CREATE_PERMANENT: u8 = 0x00;
532pub const MANAGER_INSTRUCTION_CREATE_EPHEMERAL: u8 = 0x01;
533pub const MANAGER_INSTRUCTION_UPGRADE: u8 = 0x02;
534pub const MANAGER_INSTRUCTION_SET_PAUSE: u8 = 0x03;
535pub const MANAGER_INSTRUCTION_DESTROY: u8 = 0x04;
536pub const MANAGER_INSTRUCTION_FINALIZE: u8 = 0x05;
537pub const MANAGER_INSTRUCTION_SET_AUTHORITY: u8 = 0x06;
538pub const MANAGER_INSTRUCTION_CLAIM_AUTHORITY: u8 = 0x07;
539
540#[repr(C, packed)]
542#[derive(Debug, Clone, Copy)]
543pub struct ManagerHeaderArgs {
544 pub discriminant: u8,
545 pub meta_account_idx: u16,
546 pub program_account_idx: u16,
547}
548
549#[repr(C, packed)]
551#[derive(Debug, Clone, Copy)]
552pub struct ManagerCreateArgs {
553 pub discriminant: u8,
554 pub meta_account_idx: u16,
555 pub program_account_idx: u16,
556 pub srcbuf_account_idx: u16,
557 pub srcbuf_offset: u32,
558 pub srcbuf_size: u32,
559 pub authority_account_idx: u16,
560 pub seed_len: u32,
561 }
563
564#[repr(C, packed)]
566#[derive(Debug, Clone, Copy)]
567pub struct ManagerUpgradeArgs {
568 pub discriminant: u8,
569 pub meta_account_idx: u16,
570 pub program_account_idx: u16,
571 pub srcbuf_account_idx: u16,
572 pub srcbuf_offset: u32,
573 pub srcbuf_size: u32,
574}
575
576#[repr(C, packed)]
578#[derive(Debug, Clone, Copy)]
579pub struct ManagerSetPauseArgs {
580 pub discriminant: u8,
581 pub meta_account_idx: u16,
582 pub program_account_idx: u16,
583 pub is_paused: u8,
584}
585
586#[repr(C, packed)]
588#[derive(Debug, Clone, Copy)]
589pub struct ManagerSetAuthorityArgs {
590 pub discriminant: u8,
591 pub meta_account_idx: u16,
592 pub program_account_idx: u16,
593 pub authority_candidate: [u8; 32],
594}
595
596pub const TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_CREATE: u8 = 0x00;
598pub const TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_WRITE: u8 = 0x01;
599
600#[repr(C, packed)]
602#[derive(Debug, Clone, Copy)]
603pub struct TestUploaderCreateArgs {
604 pub account_idx: u16,
605 pub is_ephemeral: u8,
606 pub account_sz: u32,
607 pub seed_len: u32,
608 }
610
611#[repr(C, packed)]
613#[derive(Debug, Clone, Copy)]
614pub struct TestUploaderWriteArgs {
615 pub target_account_idx: u16,
616 pub target_offset: u32,
617 pub data_len: u32,
618 }
620
621#[repr(C, packed)]
623#[derive(Debug, Clone, Copy)]
624pub struct SystemProgramDecompress2Args {
625 pub target_account_idx: u16,
626 pub meta_account_idx: u16,
627 pub data_account_idx: u16,
628 pub data_offset: u32,
629}
630
631impl TransactionBuilder {
632 pub fn build_uploader_create(
634 fee_payer: TnPubkey,
635 uploader_program: TnPubkey,
636 meta_account: TnPubkey,
637 buffer_account: TnPubkey,
638 buffer_size: u32,
639 expected_hash: [u8; 32],
640 seed: &[u8],
641 fee: u64,
642 nonce: u64,
643 start_slot: u64,
644 ) -> Result<Transaction> {
645 let authority_account_idx = 0u16;
647
648 let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
649 .with_start_slot(start_slot)
650 .with_expiry_after(10)
651 .with_compute_units(50_000 + 2 * buffer_size as u32)
652 .with_memory_units(10_000)
653 .with_state_units(10_000);
654
655 let mut meta_account_idx = 2u16;
656 let mut buffer_account_idx = 3u16;
657 if meta_account > buffer_account {
658 meta_account_idx = 3u16;
659 buffer_account_idx = 2u16;
660 tx = tx
661 .add_rw_account(buffer_account)
662 .add_rw_account(meta_account)
663 } else {
664 tx = tx
665 .add_rw_account(meta_account)
666 .add_rw_account(buffer_account)
667 }
668
669 let instruction_data = build_uploader_create_instruction(
670 buffer_account_idx,
671 meta_account_idx,
672 authority_account_idx,
673 buffer_size,
674 expected_hash,
675 seed,
676 )?;
677
678 tx = tx.with_instructions(instruction_data);
679
680 Ok(tx)
681 }
682
683 pub fn build_uploader_write(
685 fee_payer: TnPubkey,
686 uploader_program: TnPubkey,
687 meta_account: TnPubkey,
688 buffer_account: TnPubkey,
689 data: &[u8],
690 offset: u32,
691 fee: u64,
692 nonce: u64,
693 start_slot: u64,
694 ) -> Result<Transaction> {
695 let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
697 .with_start_slot(start_slot)
698 .with_expiry_after(10000)
699 .with_compute_units(500_000_000)
700 .with_memory_units(5000)
701 .with_state_units(5000);
702
703 let mut meta_account_idx = 2u16;
704 let mut buffer_account_idx = 3u16;
705 if meta_account > buffer_account {
706 meta_account_idx = 3u16;
707 buffer_account_idx = 2u16;
708 tx = tx
709 .add_rw_account(buffer_account)
710 .add_rw_account(meta_account)
711 } else {
712 tx = tx
713 .add_rw_account(meta_account)
714 .add_rw_account(buffer_account)
715 }
716
717 let instruction_data =
718 build_uploader_write_instruction(buffer_account_idx, meta_account_idx, data, offset)?;
719
720 tx = tx.with_instructions(instruction_data);
721
722 Ok(tx)
723 }
724
725 pub fn build_uploader_finalize(
727 fee_payer: TnPubkey,
728 uploader_program: TnPubkey,
729 meta_account: TnPubkey,
730 buffer_account: TnPubkey,
731 buffer_size: u32,
732 expected_hash: [u8; 32],
733 fee: u64,
734 nonce: u64,
735 start_slot: u64,
736 ) -> Result<Transaction> {
737 let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
738 .with_start_slot(start_slot)
739 .with_expiry_after(10000)
740 .with_compute_units(50_000 + 180 * buffer_size as u32)
741 .with_memory_units(5000)
742 .with_state_units(5000);
743
744 let mut meta_account_idx = 2u16;
746 let mut buffer_account_idx = 3u16;
747 if meta_account > buffer_account {
748 meta_account_idx = 3u16;
749 buffer_account_idx = 2u16;
750 tx = tx
751 .add_rw_account(buffer_account)
752 .add_rw_account(meta_account)
753 } else {
754 tx = tx
755 .add_rw_account(meta_account)
756 .add_rw_account(buffer_account)
757 }
758
759 let instruction_data = build_uploader_finalize_instruction(
760 buffer_account_idx,
761 meta_account_idx,
762 expected_hash,
763 )?;
764
765 tx = tx.with_instructions(instruction_data);
766
767 Ok(tx)
768 }
769
770 pub fn build_uploader_destroy(
772 fee_payer: TnPubkey,
773 uploader_program: TnPubkey,
774 meta_account: TnPubkey,
775 buffer_account: TnPubkey,
776 fee: u64,
777 nonce: u64,
778 start_slot: u64,
779 ) -> Result<Transaction> {
780 let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
781 .with_start_slot(start_slot)
782 .with_expiry_after(10000)
783 .with_compute_units(50000)
784 .with_memory_units(5000)
785 .with_state_units(5000);
786
787 let mut meta_account_idx = 2u16;
789 let mut buffer_account_idx = 3u16;
790 if meta_account > buffer_account {
791 meta_account_idx = 3u16;
792 buffer_account_idx = 2u16;
793 tx = tx
794 .add_rw_account(buffer_account)
795 .add_rw_account(meta_account)
796 } else {
797 tx = tx
798 .add_rw_account(meta_account)
799 .add_rw_account(buffer_account)
800 }
801
802 let instruction_data =
803 build_uploader_destroy_instruction(buffer_account_idx, meta_account_idx)?;
804
805 tx = tx.with_instructions(instruction_data);
806 Ok(tx)
807 }
808
809 pub fn build_manager_create(
811 fee_payer: TnPubkey,
812 manager_program: TnPubkey,
813 meta_account: TnPubkey,
814 program_account: TnPubkey,
815 srcbuf_account: TnPubkey,
816 authority_account: TnPubkey,
817 srcbuf_offset: u32,
818 srcbuf_size: u32,
819 seed: &[u8],
820 is_ephemeral: bool,
821 meta_proof: Option<&[u8]>,
822 program_proof: Option<&[u8]>,
823 fee: u64,
824 nonce: u64,
825 start_slot: u64,
826 ) -> Result<Transaction> {
827 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
828 .with_start_slot(start_slot)
829 .with_expiry_after(10000)
830 .with_compute_units(500_000_000)
831 .with_memory_units(5000)
832 .with_state_units(5000);
833
834 let authority_is_fee_payer = authority_account == fee_payer;
836
837 let mut rw_accounts = vec![(meta_account, "meta"), (program_account, "program")];
839
840 let mut r_accounts = vec![(srcbuf_account, "srcbuf")];
841
842 if !authority_is_fee_payer {
844 r_accounts.push((authority_account, "authority"));
845 }
846
847 rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
849
850 r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
852
853 let mut accounts = rw_accounts;
855 accounts.extend(r_accounts);
856
857 let mut meta_account_idx = 0u16;
858 let mut program_account_idx = 0u16;
859 let mut srcbuf_account_idx = 0u16;
860 let mut authority_account_idx = if authority_is_fee_payer {
861 0u16 } else {
863 0u16 };
865
866 for (i, (account, account_type)) in accounts.iter().enumerate() {
867 let idx = (i + 2) as u16; match *account_type {
869 "meta" => {
870 meta_account_idx = idx;
871 tx = tx.add_rw_account(*account);
872 }
873 "program" => {
874 program_account_idx = idx;
875 tx = tx.add_rw_account(*account);
876 }
877 "srcbuf" => {
878 srcbuf_account_idx = idx;
879 tx = tx.add_r_account(*account);
880 }
881 "authority" => {
882 authority_account_idx = idx;
883 tx = tx.add_r_account(*account);
884 }
885 _ => unreachable!(),
886 }
887 }
888
889 let discriminant = if is_ephemeral {
890 MANAGER_INSTRUCTION_CREATE_EPHEMERAL
891 } else {
892 MANAGER_INSTRUCTION_CREATE_PERMANENT
893 };
894
895 let combined_proof = if let (Some(meta), Some(program)) = (meta_proof, program_proof) {
897 let mut combined = Vec::with_capacity(meta.len() + program.len());
898 combined.extend_from_slice(meta);
899 combined.extend_from_slice(program);
900 Some(combined)
901 } else {
902 None
903 };
904
905 let instruction_data = build_manager_create_instruction(
906 discriminant,
907 meta_account_idx,
908 program_account_idx,
909 srcbuf_account_idx,
910 authority_account_idx,
911 srcbuf_offset,
912 srcbuf_size,
913 seed,
914 combined_proof.as_deref(),
915 )?;
916
917 tx = tx.with_instructions(instruction_data);
918 Ok(tx)
919 }
920
921 pub fn build_manager_upgrade(
923 fee_payer: TnPubkey,
924 manager_program: TnPubkey,
925 meta_account: TnPubkey,
926 program_account: TnPubkey,
927 srcbuf_account: TnPubkey,
928 srcbuf_offset: u32,
929 srcbuf_size: u32,
930 fee: u64,
931 nonce: u64,
932 start_slot: u64,
933 ) -> Result<Transaction> {
934 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
935 .with_start_slot(start_slot)
936 .with_expiry_after(10000)
937 .with_compute_units(500_000_000)
938 .with_memory_units(5000)
939 .with_state_units(5000);
940
941 let mut rw_accounts = vec![(meta_account, "meta"), (program_account, "program")];
943
944 let mut r_accounts = vec![(srcbuf_account, "srcbuf")];
945
946 rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
948
949 r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
951
952 let mut accounts = rw_accounts;
954 accounts.extend(r_accounts);
955
956 let mut meta_account_idx = 0u16;
957 let mut program_account_idx = 0u16;
958 let mut srcbuf_account_idx = 0u16;
959
960 for (i, (account, account_type)) in accounts.iter().enumerate() {
961 let idx = (i + 2) as u16; match *account_type {
963 "meta" => {
964 meta_account_idx = idx;
965 tx = tx.add_rw_account(*account);
966 }
967 "program" => {
968 program_account_idx = idx;
969 tx = tx.add_rw_account(*account);
970 }
971 "srcbuf" => {
972 srcbuf_account_idx = idx;
973 tx = tx.add_r_account(*account);
974 }
975 _ => unreachable!(),
976 }
977 }
978
979 let instruction_data = build_manager_upgrade_instruction(
980 meta_account_idx,
981 program_account_idx,
982 srcbuf_account_idx,
983 srcbuf_offset,
984 srcbuf_size,
985 )?;
986
987 tx = tx.with_instructions(instruction_data);
988 Ok(tx)
989 }
990
991 pub fn build_manager_set_pause(
993 fee_payer: TnPubkey,
994 manager_program: TnPubkey,
995 meta_account: TnPubkey,
996 program_account: TnPubkey,
997 is_paused: bool,
998 fee: u64,
999 nonce: u64,
1000 start_slot: u64,
1001 ) -> Result<Transaction> {
1002 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
1003 .with_start_slot(start_slot)
1004 .with_expiry_after(10000)
1005 .with_compute_units(100_000_000)
1006 .with_memory_units(5000)
1007 .with_state_units(5000);
1008
1009 let mut accounts = vec![(meta_account, "meta"), (program_account, "program")];
1011 accounts.sort_by(|a, b| a.0.cmp(&b.0));
1012
1013 let mut meta_account_idx = 0u16;
1014 let mut program_account_idx = 0u16;
1015
1016 for (i, (account, account_type)) in accounts.iter().enumerate() {
1017 let idx = (i + 2) as u16;
1018 match *account_type {
1019 "meta" => {
1020 meta_account_idx = idx;
1021 tx = tx.add_rw_account(*account);
1022 }
1023 "program" => {
1024 program_account_idx = idx;
1025 tx = tx.add_rw_account(*account);
1026 }
1027 _ => unreachable!(),
1028 }
1029 }
1030
1031 let instruction_data =
1032 build_manager_set_pause_instruction(meta_account_idx, program_account_idx, is_paused)?;
1033
1034 tx = tx.with_instructions(instruction_data);
1035 Ok(tx)
1036 }
1037
1038 pub fn build_manager_simple(
1040 fee_payer: TnPubkey,
1041 manager_program: TnPubkey,
1042 meta_account: TnPubkey,
1043 program_account: TnPubkey,
1044 instruction_type: u8,
1045 fee: u64,
1046 nonce: u64,
1047 start_slot: u64,
1048 ) -> Result<Transaction> {
1049 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
1050 .with_start_slot(start_slot)
1051 .with_expiry_after(10000)
1052 .with_compute_units(100_000_000)
1053 .with_memory_units(5000)
1054 .with_state_units(5000);
1055
1056 let mut accounts = vec![(meta_account, "meta"), (program_account, "program")];
1058 accounts.sort_by(|a, b| a.0.cmp(&b.0));
1059
1060 let mut meta_account_idx = 0u16;
1061 let mut program_account_idx = 0u16;
1062
1063 for (i, (account, account_type)) in accounts.iter().enumerate() {
1064 let idx = (i + 2) as u16;
1065 match *account_type {
1066 "meta" => {
1067 meta_account_idx = idx;
1068 tx = tx.add_rw_account(*account);
1069 }
1070 "program" => {
1071 program_account_idx = idx;
1072 tx = tx.add_rw_account(*account);
1073 }
1074 _ => unreachable!(),
1075 }
1076 }
1077
1078 let instruction_data = build_manager_header_instruction(
1079 instruction_type,
1080 meta_account_idx,
1081 program_account_idx,
1082 )?;
1083
1084 tx = tx.with_instructions(instruction_data);
1085 Ok(tx)
1086 }
1087
1088 pub fn build_manager_set_authority(
1090 fee_payer: TnPubkey,
1091 manager_program: TnPubkey,
1092 meta_account: TnPubkey,
1093 program_account: TnPubkey,
1094 authority_candidate: [u8; 32],
1095 fee: u64,
1096 nonce: u64,
1097 start_slot: u64,
1098 ) -> Result<Transaction> {
1099 let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
1100 .with_start_slot(start_slot)
1101 .with_expiry_after(10000)
1102 .with_compute_units(100_000_000)
1103 .with_memory_units(5000)
1104 .with_state_units(5000);
1105
1106 let mut accounts = vec![(meta_account, "meta"), (program_account, "program")];
1108 accounts.sort_by(|a, b| a.0.cmp(&b.0));
1109
1110 let mut meta_account_idx = 0u16;
1111 let mut program_account_idx = 0u16;
1112
1113 for (i, (account, account_type)) in accounts.iter().enumerate() {
1114 let idx = (i + 2) as u16;
1115 match *account_type {
1116 "meta" => {
1117 meta_account_idx = idx;
1118 tx = tx.add_rw_account(*account);
1119 }
1120 "program" => {
1121 program_account_idx = idx;
1122 tx = tx.add_rw_account(*account);
1123 }
1124 _ => unreachable!(),
1125 }
1126 }
1127
1128 let instruction_data = build_manager_set_authority_instruction(
1129 meta_account_idx,
1130 program_account_idx,
1131 authority_candidate,
1132 )?;
1133
1134 tx = tx.with_instructions(instruction_data);
1135 Ok(tx)
1136 }
1137
1138 pub fn build_test_uploader_create(
1140 fee_payer: TnPubkey,
1141 test_uploader_program: TnPubkey,
1142 target_account: TnPubkey,
1143 account_sz: u32,
1144 seed: &[u8],
1145 is_ephemeral: bool,
1146 state_proof: Option<&[u8]>,
1147 fee: u64,
1148 nonce: u64,
1149 start_slot: u64,
1150 ) -> Result<Transaction> {
1151 let target_account_idx = 2u16;
1153
1154 let tx = Transaction::new(fee_payer, test_uploader_program, fee, nonce)
1155 .with_start_slot(start_slot)
1156 .with_expiry_after(100)
1157 .with_compute_units(100_000 + account_sz)
1158 .with_memory_units(10_000)
1159 .with_state_units(10_000)
1160 .add_rw_account(target_account);
1161
1162 let instruction_data = build_test_uploader_create_instruction(
1163 target_account_idx,
1164 account_sz,
1165 seed,
1166 is_ephemeral,
1167 state_proof,
1168 )?;
1169
1170 let tx = tx.with_instructions(instruction_data);
1171 Ok(tx)
1172 }
1173
1174 pub fn build_test_uploader_write(
1176 fee_payer: TnPubkey,
1177 test_uploader_program: TnPubkey,
1178 target_account: TnPubkey,
1179 offset: u32,
1180 data: &[u8],
1181 fee: u64,
1182 nonce: u64,
1183 start_slot: u64,
1184 ) -> Result<Transaction> {
1185 let target_account_idx = 2u16;
1187
1188 let tx = Transaction::new(fee_payer, test_uploader_program, fee, nonce)
1189 .with_start_slot(start_slot)
1190 .with_expiry_after(10_000)
1191 .with_compute_units(100_000 + 18 * data.len() as u32)
1192 .with_memory_units(10_000)
1193 .with_state_units(10_000)
1194 .add_rw_account(target_account);
1195
1196 let instruction_data =
1197 build_test_uploader_write_instruction(target_account_idx, offset, data)?;
1198
1199 let tx = tx.with_instructions(instruction_data);
1200 Ok(tx)
1201 }
1202
1203 pub fn build_decompress2(
1205 fee_payer: TnPubkey,
1206 program: TnPubkey,
1207 target_account: TnPubkey,
1208 meta_account: TnPubkey,
1209 data_account: TnPubkey,
1210 data_offset: u32,
1211 state_proof: &[u8],
1212 fee: u64,
1213 nonce: u64,
1214 start_slot: u64,
1215 data_sz: u32,
1216 ) -> Result<Transaction> {
1217 let mut tx = Transaction::new(fee_payer, program, fee, nonce)
1219 .with_start_slot(start_slot)
1220 .with_expiry_after(100)
1221 .with_compute_units(10_000 + 2 * data_sz)
1222 .with_memory_units(10_000)
1223 .with_state_units(10);
1224
1225 let target_account_idx = 2u16;
1227 tx = tx.add_rw_account(target_account);
1228
1229 let mut meta_account_idx = 0u16;
1230 let mut data_account_idx = 0u16;
1231
1232 if meta_account == data_account {
1234 let account_idx = 3u16;
1236 meta_account_idx = account_idx;
1237 data_account_idx = account_idx;
1238 tx = tx.add_r_account(meta_account);
1239 } else {
1240 let mut read_accounts = vec![(meta_account, "meta"), (data_account, "data")];
1242 read_accounts.sort_by(|a, b| a.0.cmp(&b.0));
1243
1244 for (i, (account, account_type)) in read_accounts.iter().enumerate() {
1245 let idx = (3 + i) as u16; match *account_type {
1247 "meta" => {
1248 meta_account_idx = idx;
1249 tx = tx.add_r_account(*account);
1250 }
1251 "data" => {
1252 data_account_idx = idx;
1253 tx = tx.add_r_account(*account);
1254 }
1255 _ => unreachable!(),
1256 }
1257 }
1258 }
1259
1260 let instruction_data = build_decompress2_instruction(
1261 target_account_idx,
1262 meta_account_idx,
1263 data_account_idx,
1264 data_offset,
1265 state_proof,
1266 )?;
1267
1268 tx = tx.with_instructions(instruction_data);
1269 Ok(tx)
1270 }
1271}
1272
1273fn build_uploader_create_instruction(
1275 buffer_account_idx: u16,
1276 meta_account_idx: u16,
1277 authority_account_idx: u16,
1278 buffer_size: u32,
1279 expected_hash: [u8; 32],
1280 seed: &[u8],
1281) -> Result<Vec<u8>> {
1282 let mut instruction = Vec::new();
1283
1284 instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_CREATE.to_le_bytes());
1286
1287 let args = UploaderCreateArgs {
1289 buffer_account_idx,
1290 meta_account_idx,
1291 authority_account_idx,
1292 buffer_account_sz: buffer_size,
1293 expected_account_hash: expected_hash,
1294 seed_len: seed.len() as u32,
1295 };
1296
1297 let args_bytes = unsafe {
1299 std::slice::from_raw_parts(
1300 &args as *const _ as *const u8,
1301 std::mem::size_of::<UploaderCreateArgs>(),
1302 )
1303 };
1304 instruction.extend_from_slice(args_bytes);
1305
1306 instruction.extend_from_slice(seed);
1308
1309 Ok(instruction)
1310}
1311
1312fn build_uploader_write_instruction(
1314 buffer_account_idx: u16,
1315 meta_account_idx: u16,
1316 data: &[u8],
1317 offset: u32,
1318) -> Result<Vec<u8>> {
1319 let mut instruction = Vec::new();
1320
1321 instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_WRITE.to_le_bytes());
1323
1324 let args = UploaderWriteArgs {
1326 buffer_account_idx,
1327 meta_account_idx,
1328 data_len: data.len() as u32,
1329 data_offset: offset,
1330 };
1331
1332 let args_bytes = unsafe {
1334 std::slice::from_raw_parts(
1335 &args as *const _ as *const u8,
1336 std::mem::size_of::<UploaderWriteArgs>(),
1337 )
1338 };
1339 instruction.extend_from_slice(args_bytes);
1340
1341 instruction.extend_from_slice(data);
1343
1344 Ok(instruction)
1345}
1346
1347fn build_uploader_finalize_instruction(
1349 buffer_account_idx: u16,
1350 meta_account_idx: u16,
1351 expected_hash: [u8; 32],
1352) -> Result<Vec<u8>> {
1353 let mut instruction = Vec::new();
1354
1355 instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_FINALIZE.to_le_bytes());
1357
1358 let args = UploaderFinalizeArgs {
1360 buffer_account_idx,
1361 meta_account_idx,
1362 expected_account_hash: expected_hash,
1363 };
1364
1365 let args_bytes = unsafe {
1367 std::slice::from_raw_parts(
1368 &args as *const _ as *const u8,
1369 std::mem::size_of::<UploaderFinalizeArgs>(),
1370 )
1371 };
1372 instruction.extend_from_slice(args_bytes);
1373
1374 Ok(instruction)
1375}
1376
1377fn build_uploader_destroy_instruction(
1379 buffer_account_idx: u16,
1380 meta_account_idx: u16,
1381) -> Result<Vec<u8>> {
1382 let mut instruction = Vec::new();
1383
1384 instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_DESTROY.to_le_bytes());
1386
1387 let args = UploaderDestroyArgs {
1389 buffer_account_idx,
1390 meta_account_idx,
1391 };
1392
1393 let args_bytes = unsafe {
1395 std::slice::from_raw_parts(
1396 &args as *const _ as *const u8,
1397 std::mem::size_of::<UploaderDestroyArgs>(),
1398 )
1399 };
1400 instruction.extend_from_slice(args_bytes);
1401
1402 Ok(instruction)
1403}
1404
1405fn build_manager_create_instruction(
1407 discriminant: u8,
1408 meta_account_idx: u16,
1409 program_account_idx: u16,
1410 srcbuf_account_idx: u16,
1411 authority_account_idx: u16,
1412 srcbuf_offset: u32,
1413 srcbuf_size: u32,
1414 seed: &[u8],
1415 proof: Option<&[u8]>,
1416) -> Result<Vec<u8>> {
1417 let mut instruction = Vec::new();
1418
1419 let args = ManagerCreateArgs {
1421 discriminant,
1422 meta_account_idx,
1423 program_account_idx,
1424 srcbuf_account_idx,
1425 srcbuf_offset,
1426 srcbuf_size,
1427 authority_account_idx,
1428 seed_len: seed.len() as u32,
1429 };
1430
1431 let args_bytes = unsafe {
1433 std::slice::from_raw_parts(
1434 &args as *const ManagerCreateArgs as *const u8,
1435 std::mem::size_of::<ManagerCreateArgs>(),
1436 )
1437 };
1438 instruction.extend_from_slice(args_bytes);
1439
1440 instruction.extend_from_slice(seed);
1442
1443 if let Some(proof_bytes) = proof {
1445 instruction.extend_from_slice(proof_bytes);
1446 }
1447
1448 Ok(instruction)
1449}
1450
1451fn build_manager_upgrade_instruction(
1453 meta_account_idx: u16,
1454 program_account_idx: u16,
1455 srcbuf_account_idx: u16,
1456 srcbuf_offset: u32,
1457 srcbuf_size: u32,
1458) -> Result<Vec<u8>> {
1459 let mut instruction = Vec::new();
1460
1461 let args = ManagerUpgradeArgs {
1462 discriminant: MANAGER_INSTRUCTION_UPGRADE,
1463 meta_account_idx,
1464 program_account_idx,
1465 srcbuf_account_idx,
1466 srcbuf_offset,
1467 srcbuf_size,
1468 };
1469
1470 let args_bytes = unsafe {
1471 std::slice::from_raw_parts(
1472 &args as *const ManagerUpgradeArgs as *const u8,
1473 std::mem::size_of::<ManagerUpgradeArgs>(),
1474 )
1475 };
1476 instruction.extend_from_slice(args_bytes);
1477
1478 Ok(instruction)
1479}
1480
1481fn build_manager_set_pause_instruction(
1483 meta_account_idx: u16,
1484 program_account_idx: u16,
1485 is_paused: bool,
1486) -> Result<Vec<u8>> {
1487 let mut instruction = Vec::new();
1488
1489 let args = ManagerSetPauseArgs {
1490 discriminant: MANAGER_INSTRUCTION_SET_PAUSE,
1491 meta_account_idx,
1492 program_account_idx,
1493 is_paused: if is_paused { 1 } else { 0 },
1494 };
1495
1496 let args_bytes = unsafe {
1497 std::slice::from_raw_parts(
1498 &args as *const ManagerSetPauseArgs as *const u8,
1499 std::mem::size_of::<ManagerSetPauseArgs>(),
1500 )
1501 };
1502 instruction.extend_from_slice(args_bytes);
1503
1504 Ok(instruction)
1505}
1506
1507fn build_manager_header_instruction(
1509 discriminant: u8,
1510 meta_account_idx: u16,
1511 program_account_idx: u16,
1512) -> Result<Vec<u8>> {
1513 let mut instruction = Vec::new();
1514
1515 let args = ManagerHeaderArgs {
1516 discriminant,
1517 meta_account_idx,
1518 program_account_idx,
1519 };
1520
1521 let args_bytes = unsafe {
1522 std::slice::from_raw_parts(
1523 &args as *const ManagerHeaderArgs as *const u8,
1524 std::mem::size_of::<ManagerHeaderArgs>(),
1525 )
1526 };
1527 instruction.extend_from_slice(args_bytes);
1528
1529 Ok(instruction)
1530}
1531
1532fn build_manager_set_authority_instruction(
1534 meta_account_idx: u16,
1535 program_account_idx: u16,
1536 authority_candidate: [u8; 32],
1537) -> Result<Vec<u8>> {
1538 let mut instruction = Vec::new();
1539
1540 let args = ManagerSetAuthorityArgs {
1541 discriminant: MANAGER_INSTRUCTION_SET_AUTHORITY,
1542 meta_account_idx,
1543 program_account_idx,
1544 authority_candidate,
1545 };
1546
1547 let args_bytes = unsafe {
1548 std::slice::from_raw_parts(
1549 &args as *const ManagerSetAuthorityArgs as *const u8,
1550 std::mem::size_of::<ManagerSetAuthorityArgs>(),
1551 )
1552 };
1553 instruction.extend_from_slice(args_bytes);
1554
1555 Ok(instruction)
1556}
1557
1558fn build_test_uploader_create_instruction(
1560 account_idx: u16,
1561 account_sz: u32,
1562 seed: &[u8],
1563 is_ephemeral: bool,
1564 state_proof: Option<&[u8]>,
1565) -> Result<Vec<u8>> {
1566 let mut instruction = Vec::new();
1567
1568 instruction.push(TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_CREATE);
1570
1571 let args = TestUploaderCreateArgs {
1573 account_idx,
1574 is_ephemeral: if is_ephemeral { 1u8 } else { 0u8 },
1575 account_sz,
1576 seed_len: seed.len() as u32,
1577 };
1578
1579 let args_bytes = unsafe {
1581 std::slice::from_raw_parts(
1582 &args as *const _ as *const u8,
1583 std::mem::size_of::<TestUploaderCreateArgs>(),
1584 )
1585 };
1586 instruction.extend_from_slice(args_bytes);
1587
1588 instruction.extend_from_slice(seed);
1590
1591 if let Some(proof) = state_proof {
1593 instruction.extend_from_slice(proof);
1594 }
1595
1596 Ok(instruction)
1597}
1598
1599fn build_test_uploader_write_instruction(
1601 target_account_idx: u16,
1602 target_offset: u32,
1603 data: &[u8],
1604) -> Result<Vec<u8>> {
1605 let mut instruction = Vec::new();
1606
1607 instruction.push(TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_WRITE);
1609
1610 let args = TestUploaderWriteArgs {
1612 target_account_idx,
1613 target_offset,
1614 data_len: data.len() as u32,
1615 };
1616
1617 let args_bytes = unsafe {
1619 std::slice::from_raw_parts(
1620 &args as *const _ as *const u8,
1621 std::mem::size_of::<TestUploaderWriteArgs>(),
1622 )
1623 };
1624 instruction.extend_from_slice(args_bytes);
1625
1626 instruction.extend_from_slice(data);
1628
1629 Ok(instruction)
1630}
1631
1632pub fn build_decompress2_instruction(
1634 target_account_idx: u16,
1635 meta_account_idx: u16,
1636 data_account_idx: u16,
1637 data_offset: u32,
1638 state_proof: &[u8],
1639) -> Result<Vec<u8>> {
1640 let mut instruction = Vec::new();
1641
1642 instruction.push(0x08);
1644
1645 let args = SystemProgramDecompress2Args {
1647 target_account_idx,
1648 meta_account_idx,
1649 data_account_idx,
1650 data_offset,
1651 };
1652
1653 let args_bytes = unsafe {
1655 std::slice::from_raw_parts(
1656 &args as *const _ as *const u8,
1657 std::mem::size_of::<SystemProgramDecompress2Args>(),
1658 )
1659 };
1660 instruction.extend_from_slice(args_bytes);
1661
1662 instruction.extend_from_slice(state_proof);
1664
1665 Ok(instruction)
1666}
1667
1668pub const TOKEN_INSTRUCTION_INITIALIZE_MINT: u8 = 0x00;
1670pub const TOKEN_INSTRUCTION_INITIALIZE_ACCOUNT: u8 = 0x01;
1671pub const TOKEN_INSTRUCTION_TRANSFER: u8 = 0x02;
1672pub const TOKEN_INSTRUCTION_MINT_TO: u8 = 0x03;
1673pub const TOKEN_INSTRUCTION_BURN: u8 = 0x04;
1674pub const TOKEN_INSTRUCTION_CLOSE_ACCOUNT: u8 = 0x05;
1675pub const TOKEN_INSTRUCTION_FREEZE_ACCOUNT: u8 = 0x06;
1676pub const TOKEN_INSTRUCTION_THAW_ACCOUNT: u8 = 0x07;
1677
1678fn add_sorted_accounts(tx: Transaction, accounts: &[(TnPubkey, bool)]) -> (Transaction, Vec<u16>) {
1680 let mut sorted_accounts: Vec<_> = accounts.iter().enumerate().collect();
1681 sorted_accounts.sort_by(|a, b| a.1.0.cmp(&b.1.0));
1682
1683 let mut updated_tx = tx;
1684 let mut indices = vec![0u16; accounts.len()];
1685
1686 for (original_idx, (i, (account, writable))) in sorted_accounts.iter().enumerate() {
1687 let account_idx = (original_idx + 2) as u16; indices[*i] = account_idx;
1689
1690 if *writable {
1691 updated_tx = updated_tx.add_rw_account(*account);
1692 } else {
1693 updated_tx = updated_tx.add_r_account(*account);
1694 }
1695 }
1696
1697 (updated_tx, indices)
1698}
1699
1700fn add_authority_account(
1702 mut tx: Transaction,
1703 authority: TnPubkey,
1704 fee_payer: TnPubkey,
1705 next_idx: u16,
1706) -> (Transaction, u16) {
1707 if authority == fee_payer {
1708 (tx, 0u16)
1709 } else {
1710 tx = tx.add_r_account(authority);
1711 (tx, next_idx)
1712 }
1713}
1714
1715impl TransactionBuilder {
1716 pub fn build_token_initialize_mint(
1718 fee_payer: TnPubkey,
1719 token_program: TnPubkey,
1720 mint_account: TnPubkey,
1721 mint_authority: TnPubkey,
1722 freeze_authority: Option<TnPubkey>,
1723 decimals: u8,
1724 ticker: &str,
1725 seed: [u8; 32],
1726 state_proof: Vec<u8>,
1727 fee: u64,
1728 nonce: u64,
1729 start_slot: u64,
1730 ) -> Result<Transaction> {
1731 let mint_account_idx = 2u16;
1733
1734 let instruction_data = build_token_initialize_mint_instruction(
1735 mint_account_idx,
1736 decimals,
1737 mint_authority,
1738 freeze_authority,
1739 ticker,
1740 seed,
1741 state_proof,
1742 )?;
1743
1744 let tx = Transaction::new(fee_payer, token_program, fee, nonce)
1745 .with_start_slot(start_slot)
1746 .add_rw_account(mint_account)
1747 .with_instructions(instruction_data)
1748 .with_expiry_after(100)
1749 .with_compute_units(300_000)
1750 .with_state_units(10_000)
1751 .with_memory_units(10_000);
1752
1753 Ok(tx)
1754 }
1755
1756 pub fn build_token_initialize_account(
1758 fee_payer: TnPubkey,
1759 token_program: TnPubkey,
1760 token_account: TnPubkey,
1761 mint_account: TnPubkey,
1762 owner: TnPubkey,
1763 seed: [u8; 32],
1764 state_proof: Vec<u8>,
1765 fee: u64,
1766 nonce: u64,
1767 start_slot: u64,
1768 ) -> Result<Transaction> {
1769 let token_account_idx = 2u16;
1771 let mint_account_idx = 3u16;
1772
1773 let owner_is_fee_payer = owner == fee_payer;
1775 let owner_account_idx = if owner_is_fee_payer {
1776 0u16 } else {
1778 4u16 };
1780
1781 let instruction_data = build_token_initialize_account_instruction(
1782 token_account_idx,
1783 mint_account_idx,
1784 owner_account_idx,
1785 seed,
1786 state_proof,
1787 )?;
1788
1789 let mut tx = Transaction::new(fee_payer, token_program, fee, nonce)
1790 .with_start_slot(start_slot)
1791 .add_rw_account(token_account)
1792 .add_r_account(mint_account);
1793
1794 if !owner_is_fee_payer {
1796 tx = tx.add_r_account(owner);
1797 }
1798
1799 tx = tx
1800 .with_instructions(instruction_data)
1801 .with_expiry_after(100)
1802 .with_compute_units(300_000)
1803 .with_state_units(10_000)
1804 .with_memory_units(10_000);
1805
1806 Ok(tx)
1807 }
1808
1809 pub fn build_token_transfer(
1811 fee_payer: TnPubkey,
1812 token_program: TnPubkey,
1813 source_account: TnPubkey,
1814 dest_account: TnPubkey,
1815 authority: TnPubkey,
1816 amount: u64,
1817 fee: u64,
1818 nonce: u64,
1819 start_slot: u64,
1820 ) -> Result<Transaction> {
1821 let mut tx = Transaction::new(fee_payer, token_program, fee, nonce)
1822 .with_start_slot(start_slot)
1823 .with_expiry_after(100)
1824 .with_compute_units(300_000)
1825 .with_state_units(10_000)
1826 .with_memory_units(10_000);
1827
1828 let is_self_transfer = source_account == dest_account;
1829 let (source_account_idx, dest_account_idx) = if is_self_transfer {
1830 tx = tx.add_rw_account(source_account);
1832 (2u16, 2u16)
1833 } else {
1834 let accounts = &[(source_account, true), (dest_account, true)];
1836 let (updated_tx, indices) = add_sorted_accounts(tx, accounts);
1837 tx = updated_tx;
1838 (indices[0], indices[1])
1839 };
1840
1841 let instruction_data =
1845 build_token_transfer_instruction(source_account_idx, dest_account_idx, amount)?;
1846
1847 Ok(tx.with_instructions(instruction_data))
1848 }
1849
1850 pub fn build_token_mint_to(
1852 fee_payer: TnPubkey,
1853 token_program: TnPubkey,
1854 mint_account: TnPubkey,
1855 dest_account: TnPubkey,
1856 authority: TnPubkey,
1857 amount: u64,
1858 fee: u64,
1859 nonce: u64,
1860 start_slot: u64,
1861 ) -> Result<Transaction> {
1862 let tx = Transaction::new(fee_payer, token_program, fee, nonce)
1863 .with_start_slot(start_slot)
1864 .with_expiry_after(100)
1865 .with_compute_units(300_000)
1866 .with_state_units(10_000)
1867 .with_memory_units(10_000);
1868
1869 let accounts = &[(mint_account, true), (dest_account, true)];
1871 let (tx, indices) = add_sorted_accounts(tx, accounts);
1872 let (mint_account_idx, dest_account_idx) = (indices[0], indices[1]);
1873
1874 let (tx, authority_account_idx) = add_authority_account(tx, authority, fee_payer, 4);
1875
1876 let instruction_data = build_token_mint_to_instruction(
1877 mint_account_idx,
1878 dest_account_idx,
1879 authority_account_idx,
1880 amount,
1881 )?;
1882
1883 Ok(tx.with_instructions(instruction_data))
1884 }
1885
1886 pub fn build_token_burn(
1888 fee_payer: TnPubkey,
1889 token_program: TnPubkey,
1890 token_account: TnPubkey,
1891 mint_account: TnPubkey,
1892 authority: TnPubkey,
1893 amount: u64,
1894 fee: u64,
1895 nonce: u64,
1896 start_slot: u64,
1897 ) -> Result<Transaction> {
1898 let tx = Transaction::new(fee_payer, token_program, fee, nonce)
1899 .with_start_slot(start_slot)
1900 .with_expiry_after(100)
1901 .with_compute_units(300_000)
1902 .with_state_units(10_000)
1903 .with_memory_units(10_000);
1904
1905 let accounts = &[(token_account, true), (mint_account, true)];
1907 let (tx, indices) = add_sorted_accounts(tx, accounts);
1908 let (token_account_idx, mint_account_idx) = (indices[0], indices[1]);
1909
1910 let (tx, authority_account_idx) = add_authority_account(tx, authority, fee_payer, 4);
1911
1912 let instruction_data = build_token_burn_instruction(
1913 token_account_idx,
1914 mint_account_idx,
1915 authority_account_idx,
1916 amount,
1917 )?;
1918
1919 Ok(tx.with_instructions(instruction_data))
1920 }
1921
1922 pub fn build_token_freeze_account(
1924 fee_payer: TnPubkey,
1925 token_program: TnPubkey,
1926 token_account: TnPubkey,
1927 mint_account: TnPubkey,
1928 authority: TnPubkey,
1929 fee: u64,
1930 nonce: u64,
1931 start_slot: u64,
1932 ) -> Result<Transaction> {
1933 let tx = Transaction::new(fee_payer, token_program, fee, nonce)
1934 .with_start_slot(start_slot)
1935 .with_expiry_after(100)
1936 .with_compute_units(300_000)
1937 .with_state_units(10_000)
1938 .with_memory_units(10_000);
1939
1940 let accounts = &[(token_account, true), (mint_account, true)];
1942 let (tx, indices) = add_sorted_accounts(tx, accounts);
1943 let (token_account_idx, mint_account_idx) = (indices[0], indices[1]);
1944
1945 let (tx, authority_account_idx) = add_authority_account(tx, authority, fee_payer, 4);
1946
1947 let instruction_data = build_token_freeze_account_instruction(
1948 token_account_idx,
1949 mint_account_idx,
1950 authority_account_idx,
1951 )?;
1952
1953 Ok(tx.with_instructions(instruction_data))
1954 }
1955
1956 pub fn build_token_thaw_account(
1958 fee_payer: TnPubkey,
1959 token_program: TnPubkey,
1960 token_account: TnPubkey,
1961 mint_account: TnPubkey,
1962 authority: TnPubkey,
1963 fee: u64,
1964 nonce: u64,
1965 start_slot: u64,
1966 ) -> Result<Transaction> {
1967 let tx = Transaction::new(fee_payer, token_program, fee, nonce)
1968 .with_start_slot(start_slot)
1969 .with_expiry_after(100)
1970 .with_compute_units(300_000)
1971 .with_state_units(10_000)
1972 .with_memory_units(10_000);
1973
1974 let accounts = &[(token_account, true), (mint_account, true)];
1976 let (tx, indices) = add_sorted_accounts(tx, accounts);
1977 let (token_account_idx, mint_account_idx) = (indices[0], indices[1]);
1978
1979 let (tx, authority_account_idx) = add_authority_account(tx, authority, fee_payer, 4);
1980
1981 let instruction_data = build_token_thaw_account_instruction(
1982 token_account_idx,
1983 mint_account_idx,
1984 authority_account_idx,
1985 )?;
1986
1987 Ok(tx.with_instructions(instruction_data))
1988 }
1989
1990 pub fn build_token_close_account(
1992 fee_payer: TnPubkey,
1993 token_program: TnPubkey,
1994 token_account: TnPubkey,
1995 destination: TnPubkey,
1996 authority: TnPubkey,
1997 fee: u64,
1998 nonce: u64,
1999 start_slot: u64,
2000 ) -> Result<Transaction> {
2001 let tx = Transaction::new(fee_payer, token_program, fee, nonce)
2002 .with_start_slot(start_slot)
2003 .with_expiry_after(100)
2004 .with_compute_units(300_000)
2005 .with_state_units(10_000)
2006 .with_memory_units(10_000);
2007
2008 let accounts = &[(token_account, true), (destination, true)];
2010 let (tx, indices) = add_sorted_accounts(tx, accounts);
2011 let (token_account_idx, destination_idx) = (indices[0], indices[1]);
2012
2013 let (tx, authority_account_idx) = add_authority_account(tx, authority, fee_payer, 4);
2014
2015 let instruction_data = build_token_close_account_instruction(
2016 token_account_idx,
2017 destination_idx,
2018 authority_account_idx,
2019 )?;
2020
2021 Ok(tx.with_instructions(instruction_data))
2022 }
2023}
2024
2025fn build_token_initialize_mint_instruction(
2027 mint_account_idx: u16,
2028 decimals: u8,
2029 mint_authority: TnPubkey,
2030 freeze_authority: Option<TnPubkey>,
2031 ticker: &str,
2032 seed: [u8; 32],
2033 state_proof: Vec<u8>,
2034) -> Result<Vec<u8>> {
2035 let mut instruction_data = Vec::new();
2036
2037 instruction_data.push(TOKEN_INSTRUCTION_INITIALIZE_MINT);
2039
2040 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
2042
2043 instruction_data.push(decimals);
2045
2046 instruction_data.extend_from_slice(&mint_authority);
2048
2049 let (freeze_auth, has_freeze_auth) = match freeze_authority {
2051 Some(auth) => (auth, 1u8),
2052 None => ([0u8; 32], 0u8),
2053 };
2054 instruction_data.extend_from_slice(&freeze_auth);
2055 instruction_data.push(has_freeze_auth);
2056
2057 let ticker_bytes = ticker.as_bytes();
2059 if ticker_bytes.len() > 8 {
2060 return Err(anyhow::anyhow!("Ticker must be 8 characters or less"));
2061 }
2062
2063 instruction_data.push(ticker_bytes.len() as u8);
2064 let mut ticker_padded = [0u8; 8];
2065 ticker_padded[..ticker_bytes.len()].copy_from_slice(ticker_bytes);
2066 instruction_data.extend_from_slice(&ticker_padded);
2067
2068 instruction_data.extend_from_slice(&seed);
2070
2071 instruction_data.extend_from_slice(&state_proof);
2073
2074 Ok(instruction_data)
2075}
2076
2077fn build_token_initialize_account_instruction(
2079 token_account_idx: u16,
2080 mint_account_idx: u16,
2081 owner_account_idx: u16,
2082 seed: [u8; 32],
2083 state_proof: Vec<u8>,
2084) -> Result<Vec<u8>> {
2085 let mut instruction_data = Vec::new();
2086
2087 instruction_data.push(TOKEN_INSTRUCTION_INITIALIZE_ACCOUNT);
2089
2090 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
2092
2093 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
2095
2096 instruction_data.extend_from_slice(&owner_account_idx.to_le_bytes());
2098
2099 instruction_data.extend_from_slice(&seed);
2101
2102 instruction_data.extend_from_slice(&state_proof);
2104
2105 Ok(instruction_data)
2106}
2107
2108fn build_token_transfer_instruction(
2110 source_account_idx: u16,
2111 dest_account_idx: u16,
2112 amount: u64,
2113) -> Result<Vec<u8>> {
2114 let mut instruction_data = Vec::new();
2115
2116 instruction_data.push(TOKEN_INSTRUCTION_TRANSFER);
2118
2119 instruction_data.extend_from_slice(&source_account_idx.to_le_bytes());
2121
2122 instruction_data.extend_from_slice(&dest_account_idx.to_le_bytes());
2124
2125 instruction_data.extend_from_slice(&amount.to_le_bytes());
2127
2128 Ok(instruction_data)
2129}
2130
2131fn build_token_mint_to_instruction(
2133 mint_account_idx: u16,
2134 dest_account_idx: u16,
2135 authority_idx: u16,
2136 amount: u64,
2137) -> Result<Vec<u8>> {
2138 let mut instruction_data = Vec::new();
2139
2140 instruction_data.push(TOKEN_INSTRUCTION_MINT_TO);
2142
2143 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
2145
2146 instruction_data.extend_from_slice(&dest_account_idx.to_le_bytes());
2148
2149 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
2151
2152 instruction_data.extend_from_slice(&amount.to_le_bytes());
2154
2155 Ok(instruction_data)
2156}
2157
2158fn build_token_burn_instruction(
2160 token_account_idx: u16,
2161 mint_account_idx: u16,
2162 authority_idx: u16,
2163 amount: u64,
2164) -> Result<Vec<u8>> {
2165 let mut instruction_data = Vec::new();
2166
2167 instruction_data.push(TOKEN_INSTRUCTION_BURN);
2169
2170 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
2172
2173 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
2175
2176 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
2178
2179 instruction_data.extend_from_slice(&amount.to_le_bytes());
2181
2182 Ok(instruction_data)
2183}
2184
2185fn build_token_freeze_account_instruction(
2187 token_account_idx: u16,
2188 mint_account_idx: u16,
2189 authority_idx: u16,
2190) -> Result<Vec<u8>> {
2191 let mut instruction_data = Vec::new();
2192
2193 instruction_data.push(TOKEN_INSTRUCTION_FREEZE_ACCOUNT);
2195
2196 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
2198
2199 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
2201
2202 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
2204
2205 Ok(instruction_data)
2206}
2207
2208fn build_token_thaw_account_instruction(
2210 token_account_idx: u16,
2211 mint_account_idx: u16,
2212 authority_idx: u16,
2213) -> Result<Vec<u8>> {
2214 let mut instruction_data = Vec::new();
2215
2216 instruction_data.push(TOKEN_INSTRUCTION_THAW_ACCOUNT);
2218
2219 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
2221
2222 instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
2224
2225 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
2227
2228 Ok(instruction_data)
2229}
2230
2231fn build_token_close_account_instruction(
2233 token_account_idx: u16,
2234 destination_idx: u16,
2235 authority_idx: u16,
2236) -> Result<Vec<u8>> {
2237 let mut instruction_data = Vec::new();
2238
2239 instruction_data.push(TOKEN_INSTRUCTION_CLOSE_ACCOUNT);
2241
2242 instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
2244
2245 instruction_data.extend_from_slice(&destination_idx.to_le_bytes());
2247
2248 instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
2250
2251 Ok(instruction_data)
2252}