1use super::SolanaSigner;
29use crate::error::SignerError;
30use ed25519_dalek::Signer as DalekSigner;
31
32#[must_use]
38pub fn encode_compact_u16(val: u16) -> Vec<u8> {
39 if val < 0x80 {
40 vec![val as u8]
41 } else if val < 0x4000 {
42 vec![(val & 0x7F | 0x80) as u8, (val >> 7) as u8]
43 } else {
44 vec![
45 (val & 0x7F | 0x80) as u8,
46 ((val >> 7) & 0x7F | 0x80) as u8,
47 (val >> 14) as u8,
48 ]
49 }
50}
51
52pub fn decode_compact_u16(data: &[u8]) -> Result<(u16, usize), SignerError> {
54 if data.is_empty() {
55 return Err(SignerError::ParseError("compact-u16: empty".into()));
56 }
57 let b0 = data[0] as u16;
58 if b0 < 0x80 {
59 return Ok((b0, 1));
60 }
61 if data.len() < 2 {
62 return Err(SignerError::ParseError("compact-u16: truncated".into()));
63 }
64 let b1 = data[1] as u16;
65 if b1 < 0x80 {
66 return Ok(((b0 & 0x7F) | (b1 << 7), 2));
67 }
68 if data.len() < 3 {
69 return Err(SignerError::ParseError("compact-u16: truncated".into()));
70 }
71 let b2 = data[2] as u16;
72 Ok(((b0 & 0x7F) | ((b1 & 0x7F) << 7) | (b2 << 14), 3))
73}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct AccountMeta {
80 pub pubkey: [u8; 32],
82 pub is_signer: bool,
84 pub is_writable: bool,
86}
87
88impl AccountMeta {
89 #[must_use]
91 pub fn new(pubkey: [u8; 32], is_signer: bool) -> Self {
92 Self {
93 pubkey,
94 is_signer,
95 is_writable: true,
96 }
97 }
98
99 #[must_use]
101 pub fn new_readonly(pubkey: [u8; 32], is_signer: bool) -> Self {
102 Self {
103 pubkey,
104 is_signer,
105 is_writable: false,
106 }
107 }
108}
109
110#[derive(Debug, Clone)]
114pub struct Instruction {
115 pub program_id: [u8; 32],
117 pub accounts: Vec<AccountMeta>,
119 pub data: Vec<u8>,
121}
122
123#[derive(Debug, Clone)]
127pub struct Message {
128 pub num_required_signatures: u8,
130 pub num_readonly_signed_accounts: u8,
132 pub num_readonly_unsigned_accounts: u8,
134 pub account_keys: Vec<[u8; 32]>,
136 pub recent_blockhash: [u8; 32],
138 pub instructions: Vec<CompiledInstruction>,
140}
141
142#[derive(Debug, Clone)]
144pub struct CompiledInstruction {
145 pub program_id_index: u8,
147 pub accounts: Vec<u8>,
149 pub data: Vec<u8>,
151}
152
153impl Message {
154 #[must_use]
162 pub fn new(instructions: &[Instruction], fee_payer: [u8; 32]) -> Self {
163 let mut writable_signers: Vec<[u8; 32]> = vec![fee_payer];
164 let mut readonly_signers: Vec<[u8; 32]> = Vec::new();
165 let mut writable_nonsigners: Vec<[u8; 32]> = Vec::new();
166 let mut readonly_nonsigners: Vec<[u8; 32]> = Vec::new();
167
168 for ix in instructions {
169 for acc in &ix.accounts {
170 if acc.pubkey == fee_payer {
172 continue;
173 }
174 match (acc.is_signer, acc.is_writable) {
175 (true, true) => {
176 if !writable_signers.contains(&acc.pubkey) {
177 writable_signers.push(acc.pubkey);
178 }
179 }
180 (true, false) => {
181 if !readonly_signers.contains(&acc.pubkey) {
182 readonly_signers.push(acc.pubkey);
183 }
184 }
185 (false, true) => {
186 if !writable_nonsigners.contains(&acc.pubkey) {
187 writable_nonsigners.push(acc.pubkey);
188 }
189 }
190 (false, false) => {
191 if !readonly_nonsigners.contains(&acc.pubkey) {
192 readonly_nonsigners.push(acc.pubkey);
193 }
194 }
195 }
196 }
197 if !writable_signers.contains(&ix.program_id)
199 && !readonly_signers.contains(&ix.program_id)
200 && !writable_nonsigners.contains(&ix.program_id)
201 && !readonly_nonsigners.contains(&ix.program_id)
202 {
203 readonly_nonsigners.push(ix.program_id);
204 }
205 }
206
207 let num_required_signatures = (writable_signers.len() + readonly_signers.len()) as u8;
208 let num_readonly_signed = readonly_signers.len() as u8;
209 let num_readonly_unsigned = readonly_nonsigners.len() as u8;
210
211 let mut account_keys = Vec::new();
212 account_keys.extend_from_slice(&writable_signers);
213 account_keys.extend_from_slice(&readonly_signers);
214 account_keys.extend_from_slice(&writable_nonsigners);
215 account_keys.extend_from_slice(&readonly_nonsigners);
216
217 let compiled = instructions
219 .iter()
220 .map(|ix| {
221 let program_id_index = account_keys
222 .iter()
223 .position(|k| *k == ix.program_id)
224 .unwrap_or(0) as u8;
225 let accounts: Vec<u8> = ix
226 .accounts
227 .iter()
228 .map(|a| {
229 account_keys
230 .iter()
231 .position(|k| *k == a.pubkey)
232 .unwrap_or(0) as u8
233 })
234 .collect();
235 CompiledInstruction {
236 program_id_index,
237 accounts,
238 data: ix.data.clone(),
239 }
240 })
241 .collect();
242
243 Self {
244 num_required_signatures,
245 num_readonly_signed_accounts: num_readonly_signed,
246 num_readonly_unsigned_accounts: num_readonly_unsigned,
247 account_keys,
248 recent_blockhash: [0u8; 32], instructions: compiled,
250 }
251 }
252
253 #[must_use]
255 pub fn serialize(&self) -> Vec<u8> {
256 let mut buf = Vec::new();
257 buf.push(self.num_required_signatures);
258 buf.push(self.num_readonly_signed_accounts);
259 buf.push(self.num_readonly_unsigned_accounts);
260
261 buf.extend_from_slice(&encode_compact_u16(self.account_keys.len() as u16));
262 for key in &self.account_keys {
263 buf.extend_from_slice(key);
264 }
265
266 buf.extend_from_slice(&self.recent_blockhash);
267
268 buf.extend_from_slice(&encode_compact_u16(self.instructions.len() as u16));
269 for ix in &self.instructions {
270 buf.push(ix.program_id_index);
271 buf.extend_from_slice(&encode_compact_u16(ix.accounts.len() as u16));
272 buf.extend_from_slice(&ix.accounts);
273 buf.extend_from_slice(&encode_compact_u16(ix.data.len() as u16));
274 buf.extend_from_slice(&ix.data);
275 }
276 buf
277 }
278}
279
280#[derive(Debug, Clone)]
284pub struct Transaction {
285 pub signatures: Vec<[u8; 64]>,
287 pub message: Message,
289}
290
291impl Transaction {
292 pub fn sign(
294 message: &Message,
295 signers: &[&SolanaSigner],
296 recent_blockhash: [u8; 32],
297 ) -> Result<Self, SignerError> {
298 let mut msg = message.clone();
299 msg.recent_blockhash = recent_blockhash;
300 let serialized = msg.serialize();
301
302 let mut signatures = Vec::new();
303 for signer in signers {
304 let sig = signer.signing_key.sign(&serialized);
305 signatures.push(sig.to_bytes());
306 }
307
308 Ok(Self {
309 signatures,
310 message: msg,
311 })
312 }
313
314 #[must_use]
316 pub fn serialize(&self) -> Vec<u8> {
317 let mut buf = Vec::new();
318 buf.extend_from_slice(&encode_compact_u16(self.signatures.len() as u16));
319 for sig in &self.signatures {
320 buf.extend_from_slice(sig);
321 }
322 buf.extend_from_slice(&self.message.serialize());
323 buf
324 }
325}
326
327pub mod system_program {
333 use super::*;
334
335 pub const ID: [u8; 32] = [0; 32];
337
338 #[must_use]
345 pub fn transfer(from: &[u8; 32], to: &[u8; 32], lamports: u64) -> Instruction {
346 let mut data = vec![2, 0, 0, 0]; data.extend_from_slice(&lamports.to_le_bytes());
348 Instruction {
349 program_id: ID,
350 accounts: vec![AccountMeta::new(*from, true), AccountMeta::new(*to, false)],
351 data,
352 }
353 }
354
355 #[must_use]
357 pub fn create_account(
358 from: &[u8; 32],
359 new_account: &[u8; 32],
360 lamports: u64,
361 space: u64,
362 owner: &[u8; 32],
363 ) -> Instruction {
364 let mut data = vec![0, 0, 0, 0]; data.extend_from_slice(&lamports.to_le_bytes());
366 data.extend_from_slice(&space.to_le_bytes());
367 data.extend_from_slice(owner);
368 Instruction {
369 program_id: ID,
370 accounts: vec![
371 AccountMeta::new(*from, true),
372 AccountMeta::new(*new_account, true),
373 ],
374 data,
375 }
376 }
377
378 #[must_use]
380 pub fn allocate(account: &[u8; 32], space: u64) -> Instruction {
381 let mut data = vec![8, 0, 0, 0]; data.extend_from_slice(&space.to_le_bytes());
383 Instruction {
384 program_id: ID,
385 accounts: vec![AccountMeta::new(*account, true)],
386 data,
387 }
388 }
389}
390
391pub mod spl_token {
397 use super::*;
398
399 pub const ID: [u8; 32] = [
401 0x06, 0xDD, 0xF6, 0xE1, 0xD7, 0x65, 0xA1, 0x93, 0xD9, 0xCB, 0xE1, 0x46, 0xCE, 0xEB, 0x79,
402 0xAC, 0x1C, 0xB4, 0x85, 0xED, 0x5F, 0x5B, 0x37, 0x91, 0x3A, 0x8C, 0xF5, 0x85, 0x7E, 0xFF,
403 0x00, 0xA9,
404 ];
405
406 #[must_use]
414 pub fn transfer(
415 source: &[u8; 32],
416 destination: &[u8; 32],
417 authority: &[u8; 32],
418 amount: u64,
419 ) -> Instruction {
420 let mut data = vec![3]; data.extend_from_slice(&amount.to_le_bytes());
422 Instruction {
423 program_id: ID,
424 accounts: vec![
425 AccountMeta::new(*source, false),
426 AccountMeta::new(*destination, false),
427 AccountMeta::new_readonly(*authority, true),
428 ],
429 data,
430 }
431 }
432
433 #[must_use]
435 pub fn approve(
436 source: &[u8; 32],
437 delegate: &[u8; 32],
438 authority: &[u8; 32],
439 amount: u64,
440 ) -> Instruction {
441 let mut data = vec![4]; data.extend_from_slice(&amount.to_le_bytes());
443 Instruction {
444 program_id: ID,
445 accounts: vec![
446 AccountMeta::new(*source, false),
447 AccountMeta::new_readonly(*delegate, false),
448 AccountMeta::new_readonly(*authority, true),
449 ],
450 data,
451 }
452 }
453
454 #[must_use]
456 pub fn mint_to(
457 mint: &[u8; 32],
458 destination: &[u8; 32],
459 authority: &[u8; 32],
460 amount: u64,
461 ) -> Instruction {
462 let mut data = vec![7]; data.extend_from_slice(&amount.to_le_bytes());
464 Instruction {
465 program_id: ID,
466 accounts: vec![
467 AccountMeta::new(*mint, false),
468 AccountMeta::new(*destination, false),
469 AccountMeta::new_readonly(*authority, true),
470 ],
471 data,
472 }
473 }
474
475 #[must_use]
477 pub fn burn(
478 token_account: &[u8; 32],
479 mint: &[u8; 32],
480 authority: &[u8; 32],
481 amount: u64,
482 ) -> Instruction {
483 let mut data = vec![8]; data.extend_from_slice(&amount.to_le_bytes());
485 Instruction {
486 program_id: ID,
487 accounts: vec![
488 AccountMeta::new(*token_account, false),
489 AccountMeta::new(*mint, false),
490 AccountMeta::new_readonly(*authority, true),
491 ],
492 data,
493 }
494 }
495
496 #[must_use]
498 pub fn close_account(
499 account: &[u8; 32],
500 destination: &[u8; 32],
501 authority: &[u8; 32],
502 ) -> Instruction {
503 Instruction {
504 program_id: ID,
505 accounts: vec![
506 AccountMeta::new(*account, false),
507 AccountMeta::new(*destination, false),
508 AccountMeta::new_readonly(*authority, true),
509 ],
510 data: vec![9], }
512 }
513
514 #[must_use]
516 pub fn freeze_account(
517 account: &[u8; 32],
518 mint: &[u8; 32],
519 freeze_authority: &[u8; 32],
520 ) -> Instruction {
521 Instruction {
522 program_id: ID,
523 accounts: vec![
524 AccountMeta::new(*account, false),
525 AccountMeta::new_readonly(*mint, false),
526 AccountMeta::new_readonly(*freeze_authority, true),
527 ],
528 data: vec![10], }
530 }
531
532 #[must_use]
534 pub fn thaw_account(
535 account: &[u8; 32],
536 mint: &[u8; 32],
537 freeze_authority: &[u8; 32],
538 ) -> Instruction {
539 Instruction {
540 program_id: ID,
541 accounts: vec![
542 AccountMeta::new(*account, false),
543 AccountMeta::new_readonly(*mint, false),
544 AccountMeta::new_readonly(*freeze_authority, true),
545 ],
546 data: vec![11], }
548 }
549
550 #[must_use]
558 pub fn initialize_mint(
559 mint: &[u8; 32],
560 decimals: u8,
561 mint_authority: &[u8; 32],
562 freeze_authority: Option<&[u8; 32]>,
563 ) -> Instruction {
564 let rent_sysvar: [u8; 32] = {
565 let mut id = [0u8; 32];
566 id[0] = 0x06;
568 id[1] = 0xa7;
569 id[2] = 0xd5;
570 id[3] = 0x17;
571 id[4] = 0x19;
572 id[5] = 0x2c;
573 id
574 };
575 let mut data = vec![0]; data.push(decimals);
577 data.extend_from_slice(mint_authority);
578 match freeze_authority {
579 Some(auth) => {
580 data.push(1); data.extend_from_slice(auth);
582 }
583 None => {
584 data.push(0); data.extend_from_slice(&[0u8; 32]);
586 }
587 }
588 Instruction {
589 program_id: ID,
590 accounts: vec![
591 AccountMeta::new(*mint, false),
592 AccountMeta::new_readonly(rent_sysvar, false),
593 ],
594 data,
595 }
596 }
597
598 #[must_use]
600 pub fn initialize_account(
601 account: &[u8; 32],
602 mint: &[u8; 32],
603 owner: &[u8; 32],
604 ) -> Instruction {
605 let rent_sysvar: [u8; 32] = {
606 let mut id = [0u8; 32];
607 id[0] = 0x06;
608 id[1] = 0xa7;
609 id[2] = 0xd5;
610 id[3] = 0x17;
611 id[4] = 0x19;
612 id[5] = 0x2c;
613 id
614 };
615 Instruction {
616 program_id: ID,
617 accounts: vec![
618 AccountMeta::new(*account, false),
619 AccountMeta::new_readonly(*mint, false),
620 AccountMeta::new_readonly(*owner, false),
621 AccountMeta::new_readonly(rent_sysvar, false),
622 ],
623 data: vec![1], }
625 }
626
627 #[derive(Debug, Clone, Copy)]
629 pub enum AuthorityType {
630 MintTokens = 0,
632 FreezeAccount = 1,
634 AccountOwner = 2,
636 CloseAccount = 3,
638 }
639
640 #[must_use]
642 pub fn set_authority(
643 account_or_mint: &[u8; 32],
644 current_authority: &[u8; 32],
645 authority_type: AuthorityType,
646 new_authority: Option<&[u8; 32]>,
647 ) -> Instruction {
648 let mut data = vec![6]; data.push(authority_type as u8);
650 match new_authority {
651 Some(auth) => {
652 data.push(1); data.extend_from_slice(auth);
654 }
655 None => {
656 data.push(0); data.extend_from_slice(&[0u8; 32]);
658 }
659 }
660 Instruction {
661 program_id: ID,
662 accounts: vec![
663 AccountMeta::new(*account_or_mint, false),
664 AccountMeta::new_readonly(*current_authority, true),
665 ],
666 data,
667 }
668 }
669
670 #[must_use]
672 pub fn revoke(source: &[u8; 32], authority: &[u8; 32]) -> Instruction {
673 Instruction {
674 program_id: ID,
675 accounts: vec![
676 AccountMeta::new(*source, false),
677 AccountMeta::new_readonly(*authority, true),
678 ],
679 data: vec![5], }
681 }
682
683 #[must_use]
687 pub fn transfer_checked(
688 source: &[u8; 32],
689 mint: &[u8; 32],
690 destination: &[u8; 32],
691 authority: &[u8; 32],
692 amount: u64,
693 decimals: u8,
694 ) -> Instruction {
695 let mut data = vec![12]; data.extend_from_slice(&amount.to_le_bytes());
697 data.push(decimals);
698 Instruction {
699 program_id: ID,
700 accounts: vec![
701 AccountMeta::new(*source, false),
702 AccountMeta::new_readonly(*mint, false),
703 AccountMeta::new(*destination, false),
704 AccountMeta::new_readonly(*authority, true),
705 ],
706 data,
707 }
708 }
709}
710
711pub mod spl_token_2022 {
717 use super::*;
718
719 pub const ID: [u8; 32] = [
721 0x06, 0xDD, 0xF6, 0xE1, 0xD7, 0x65, 0xA1, 0x93, 0xD9, 0xCB, 0xE1, 0x46, 0xCE, 0xEB, 0x79,
722 0xAC, 0x1C, 0xB4, 0x85, 0xED, 0x5F, 0x5B, 0x37, 0x91, 0x3A, 0x8C, 0xF5, 0x85, 0x7E, 0xFF,
723 0x00, 0xAA, ];
725
726 #[must_use]
728 pub fn transfer_checked(
729 source: &[u8; 32],
730 mint: &[u8; 32],
731 destination: &[u8; 32],
732 authority: &[u8; 32],
733 amount: u64,
734 decimals: u8,
735 ) -> Instruction {
736 let mut data = vec![12]; data.extend_from_slice(&amount.to_le_bytes());
738 data.push(decimals);
739 Instruction {
740 program_id: ID,
741 accounts: vec![
742 AccountMeta::new(*source, false),
743 AccountMeta::new_readonly(*mint, false),
744 AccountMeta::new(*destination, false),
745 AccountMeta::new_readonly(*authority, true),
746 ],
747 data,
748 }
749 }
750
751 #[must_use]
755 pub fn transfer_checked_with_fee(
756 source: &[u8; 32],
757 mint: &[u8; 32],
758 destination: &[u8; 32],
759 authority: &[u8; 32],
760 amount: u64,
761 decimals: u8,
762 fee: u64,
763 ) -> Instruction {
764 let mut data = vec![26]; data.extend_from_slice(&amount.to_le_bytes());
766 data.push(decimals);
767 data.extend_from_slice(&fee.to_le_bytes());
768 Instruction {
769 program_id: ID,
770 accounts: vec![
771 AccountMeta::new(*source, false),
772 AccountMeta::new_readonly(*mint, false),
773 AccountMeta::new(*destination, false),
774 AccountMeta::new_readonly(*authority, true),
775 ],
776 data,
777 }
778 }
779}
780
781pub mod compute_budget {
787 use super::*;
788
789 pub const ID: [u8; 32] = [
791 0x03, 0x06, 0x46, 0x6F, 0xE5, 0x21, 0x17, 0x32, 0xFF, 0xEC, 0xAD, 0xBA, 0x72, 0xC3, 0x9B,
792 0xE7, 0xBC, 0x8C, 0xE5, 0xBB, 0xC5, 0xF7, 0x12, 0x6B, 0x2C, 0x43, 0x9B, 0x3A, 0x40, 0x00,
793 0x00, 0x00,
794 ];
795
796 #[must_use]
798 pub fn set_compute_unit_limit(units: u32) -> Instruction {
799 let mut data = vec![2]; data.extend_from_slice(&units.to_le_bytes());
801 Instruction {
802 program_id: ID,
803 accounts: vec![],
804 data,
805 }
806 }
807
808 #[must_use]
810 pub fn set_compute_unit_price(micro_lamports: u64) -> Instruction {
811 let mut data = vec![3]; data.extend_from_slice(µ_lamports.to_le_bytes());
813 Instruction {
814 program_id: ID,
815 accounts: vec![],
816 data,
817 }
818 }
819}
820
821#[derive(Debug, Clone)]
827pub struct AddressLookupTable {
828 pub account_key: [u8; 32],
830 pub writable_indexes: Vec<u8>,
832 pub readonly_indexes: Vec<u8>,
834}
835
836#[derive(Debug, Clone)]
838pub struct MessageV0 {
839 pub message: Message,
841 pub address_table_lookups: Vec<AddressLookupTable>,
843}
844
845impl MessageV0 {
846 #[must_use]
850 pub fn serialize(&self) -> Vec<u8> {
851 let mut buf = Vec::new();
852 buf.push(0x80); buf.extend_from_slice(&self.message.serialize());
854
855 buf.extend_from_slice(&encode_compact_u16(self.address_table_lookups.len() as u16));
857 for table in &self.address_table_lookups {
858 buf.extend_from_slice(&table.account_key);
859 buf.extend_from_slice(&encode_compact_u16(table.writable_indexes.len() as u16));
860 buf.extend_from_slice(&table.writable_indexes);
861 buf.extend_from_slice(&encode_compact_u16(table.readonly_indexes.len() as u16));
862 buf.extend_from_slice(&table.readonly_indexes);
863 }
864 buf
865 }
866}
867
868#[derive(Debug, Clone)]
870pub struct VersionedTransaction {
871 pub signatures: Vec<[u8; 64]>,
873 pub message: MessageV0,
875}
876
877impl VersionedTransaction {
878 pub fn sign(
880 message: &MessageV0,
881 signers: &[&SolanaSigner],
882 recent_blockhash: [u8; 32],
883 ) -> Result<Self, SignerError> {
884 let mut msg = message.clone();
885 msg.message.recent_blockhash = recent_blockhash;
886 let serialized = msg.serialize();
887
888 let mut signatures = Vec::new();
889 for signer in signers {
890 let sig = signer.signing_key.sign(&serialized);
891 signatures.push(sig.to_bytes());
892 }
893
894 Ok(Self {
895 signatures,
896 message: msg,
897 })
898 }
899
900 #[must_use]
902 pub fn serialize(&self) -> Vec<u8> {
903 let mut buf = Vec::new();
904 buf.extend_from_slice(&encode_compact_u16(self.signatures.len() as u16));
905 for sig in &self.signatures {
906 buf.extend_from_slice(sig);
907 }
908 buf.extend_from_slice(&self.message.serialize());
909 buf
910 }
911}
912
913pub fn find_program_address(
935 seeds: &[&[u8]],
936 program_id: &[u8; 32],
937) -> Result<([u8; 32], u8), SignerError> {
938 for bump in (0..=255u8).rev() {
939 if let Ok(addr) = create_program_address(seeds, &[bump], program_id) {
940 return Ok((addr, bump));
941 }
942 }
943 Err(SignerError::SigningFailed(
944 "PDA: no valid bump found".into(),
945 ))
946}
947
948pub fn create_program_address(
952 seeds: &[&[u8]],
953 bump: &[u8],
954 program_id: &[u8; 32],
955) -> Result<[u8; 32], SignerError> {
956 use sha2::{Digest, Sha256};
957
958 let mut hasher = Sha256::new();
959 for seed in seeds {
960 if seed.len() > 32 {
961 return Err(SignerError::SigningFailed("PDA seed > 32 bytes".into()));
962 }
963 hasher.update(seed);
964 }
965 hasher.update(bump);
966 hasher.update(program_id);
967 hasher.update(b"ProgramDerivedAddress");
968 let hash = hasher.finalize();
969
970 let mut candidate = [0u8; 32];
971 candidate.copy_from_slice(&hash);
972
973 if ed25519_dalek::VerifyingKey::from_bytes(&candidate).is_ok() {
978 return Err(SignerError::SigningFailed("PDA: on curve".into()));
979 }
980
981 Ok(candidate)
982}
983
984impl Message {
989 pub fn deserialize(data: &[u8]) -> Result<Self, SignerError> {
991 if data.len() < 3 {
992 return Err(SignerError::ParseError("message too short".into()));
993 }
994 let num_required_signatures = data[0];
995 let num_readonly_signed_accounts = data[1];
996 let num_readonly_unsigned_accounts = data[2];
997 let mut pos = 3;
998
999 let (num_keys, consumed) = decode_compact_u16(&data[pos..])?;
1001 pos += consumed;
1002 let mut account_keys = Vec::with_capacity(num_keys as usize);
1003 for _ in 0..num_keys {
1004 if pos + 32 > data.len() {
1005 return Err(SignerError::ParseError(
1006 "message: truncated account key".into(),
1007 ));
1008 }
1009 let mut key = [0u8; 32];
1010 key.copy_from_slice(&data[pos..pos + 32]);
1011 account_keys.push(key);
1012 pos += 32;
1013 }
1014
1015 if pos + 32 > data.len() {
1017 return Err(SignerError::ParseError(
1018 "message: truncated blockhash".into(),
1019 ));
1020 }
1021 let mut recent_blockhash = [0u8; 32];
1022 recent_blockhash.copy_from_slice(&data[pos..pos + 32]);
1023 pos += 32;
1024
1025 let (num_ix, consumed) = decode_compact_u16(&data[pos..])?;
1027 pos += consumed;
1028 let mut instructions = Vec::with_capacity(num_ix as usize);
1029 for _ in 0..num_ix {
1030 if pos >= data.len() {
1031 return Err(SignerError::ParseError(
1032 "message: truncated instruction".into(),
1033 ));
1034 }
1035 let program_id_index = data[pos];
1036 pos += 1;
1037
1038 let (num_accounts, consumed) = decode_compact_u16(&data[pos..])?;
1039 pos += consumed;
1040 if pos + num_accounts as usize > data.len() {
1041 return Err(SignerError::ParseError(
1042 "message: truncated instruction accounts".into(),
1043 ));
1044 }
1045 let accounts = data[pos..pos + num_accounts as usize].to_vec();
1046 pos += num_accounts as usize;
1047
1048 let (data_len, consumed) = decode_compact_u16(&data[pos..])?;
1049 pos += consumed;
1050 if pos + data_len as usize > data.len() {
1051 return Err(SignerError::ParseError(
1052 "message: truncated instruction data".into(),
1053 ));
1054 }
1055 let ix_data = data[pos..pos + data_len as usize].to_vec();
1056 pos += data_len as usize;
1057
1058 instructions.push(CompiledInstruction {
1059 program_id_index,
1060 accounts,
1061 data: ix_data,
1062 });
1063 }
1064
1065 Ok(Self {
1066 num_required_signatures,
1067 num_readonly_signed_accounts,
1068 num_readonly_unsigned_accounts,
1069 account_keys,
1070 recent_blockhash,
1071 instructions,
1072 })
1073 }
1074}
1075
1076impl Transaction {
1077 pub fn deserialize(data: &[u8]) -> Result<Self, SignerError> {
1079 let mut pos = 0;
1080
1081 let (num_sigs, consumed) = decode_compact_u16(&data[pos..])?;
1083 pos += consumed;
1084 let mut signatures = Vec::with_capacity(num_sigs as usize);
1085 for _ in 0..num_sigs {
1086 if pos + 64 > data.len() {
1087 return Err(SignerError::ParseError(
1088 "transaction: truncated signature".into(),
1089 ));
1090 }
1091 let mut sig = [0u8; 64];
1092 sig.copy_from_slice(&data[pos..pos + 64]);
1093 signatures.push(sig);
1094 pos += 64;
1095 }
1096
1097 let message = Message::deserialize(&data[pos..])?;
1099
1100 Ok(Self {
1101 signatures,
1102 message,
1103 })
1104 }
1105}
1106
1107pub struct InstructionDataBuilder {
1128 buf: Vec<u8>,
1129}
1130
1131impl InstructionDataBuilder {
1132 #[must_use]
1134 pub fn new() -> Self {
1135 Self { buf: Vec::new() }
1136 }
1137
1138 #[must_use]
1140 pub fn write_u8(mut self, val: u8) -> Self {
1141 self.buf.push(val);
1142 self
1143 }
1144
1145 #[must_use]
1147 pub fn write_u16(mut self, val: u16) -> Self {
1148 self.buf.extend_from_slice(&val.to_le_bytes());
1149 self
1150 }
1151
1152 #[must_use]
1154 pub fn write_u32(mut self, val: u32) -> Self {
1155 self.buf.extend_from_slice(&val.to_le_bytes());
1156 self
1157 }
1158
1159 #[must_use]
1161 pub fn write_u64(mut self, val: u64) -> Self {
1162 self.buf.extend_from_slice(&val.to_le_bytes());
1163 self
1164 }
1165
1166 #[must_use]
1168 pub fn write_i64(mut self, val: i64) -> Self {
1169 self.buf.extend_from_slice(&val.to_le_bytes());
1170 self
1171 }
1172
1173 #[must_use]
1175 pub fn write_bool(mut self, val: bool) -> Self {
1176 self.buf.push(u8::from(val));
1177 self
1178 }
1179
1180 #[must_use]
1182 pub fn write_bytes(mut self, data: &[u8]) -> Self {
1183 self.buf.extend_from_slice(data);
1184 self
1185 }
1186
1187 #[must_use]
1189 pub fn write_pubkey(self, key: &[u8; 32]) -> Self {
1190 self.write_bytes(key)
1191 }
1192
1193 #[must_use]
1195 pub fn write_string(mut self, s: &str) -> Self {
1196 let bytes = s.as_bytes();
1197 self.buf
1198 .extend_from_slice(&(bytes.len() as u32).to_le_bytes());
1199 self.buf.extend_from_slice(bytes);
1200 self
1201 }
1202
1203 #[must_use]
1205 pub fn write_option(mut self, val: Option<&[u8]>) -> Self {
1206 match val {
1207 None => {
1208 self.buf.push(0);
1209 }
1210 Some(data) => {
1211 self.buf.push(1);
1212 self.buf.extend_from_slice(data);
1213 }
1214 }
1215 self
1216 }
1217
1218 #[must_use]
1220 pub fn write_vec(mut self, data: &[u8]) -> Self {
1221 self.buf
1222 .extend_from_slice(&(data.len() as u32).to_le_bytes());
1223 self.buf.extend_from_slice(data);
1224 self
1225 }
1226
1227 #[must_use]
1229 pub fn build(self) -> Vec<u8> {
1230 self.buf
1231 }
1232}
1233
1234impl Default for InstructionDataBuilder {
1235 fn default() -> Self {
1236 Self::new()
1237 }
1238}
1239
1240pub struct InstructionDataReader<'a> {
1244 data: &'a [u8],
1245 pos: usize,
1246}
1247
1248impl<'a> InstructionDataReader<'a> {
1249 pub fn new(data: &'a [u8]) -> Self {
1251 Self { data, pos: 0 }
1252 }
1253
1254 pub fn read_u8(&mut self) -> Result<u8, SignerError> {
1256 if self.pos >= self.data.len() {
1257 return Err(SignerError::ParseError("read_u8: EOF".into()));
1258 }
1259 let val = self.data[self.pos];
1260 self.pos += 1;
1261 Ok(val)
1262 }
1263
1264 pub fn read_u16(&mut self) -> Result<u16, SignerError> {
1266 if self.pos + 2 > self.data.len() {
1267 return Err(SignerError::ParseError("read_u16: EOF".into()));
1268 }
1269 let val = u16::from_le_bytes(
1270 self.data[self.pos..self.pos + 2]
1271 .try_into()
1272 .map_err(|_| SignerError::ParseError("read_u16: bad bytes".into()))?,
1273 );
1274 self.pos += 2;
1275 Ok(val)
1276 }
1277
1278 pub fn read_u32(&mut self) -> Result<u32, SignerError> {
1280 if self.pos + 4 > self.data.len() {
1281 return Err(SignerError::ParseError("read_u32: EOF".into()));
1282 }
1283 let val = u32::from_le_bytes(
1284 self.data[self.pos..self.pos + 4]
1285 .try_into()
1286 .map_err(|_| SignerError::ParseError("read_u32: bad bytes".into()))?,
1287 );
1288 self.pos += 4;
1289 Ok(val)
1290 }
1291
1292 pub fn read_u64(&mut self) -> Result<u64, SignerError> {
1294 if self.pos + 8 > self.data.len() {
1295 return Err(SignerError::ParseError("read_u64: EOF".into()));
1296 }
1297 let val = u64::from_le_bytes(
1298 self.data[self.pos..self.pos + 8]
1299 .try_into()
1300 .map_err(|_| SignerError::ParseError("read_u64: bad bytes".into()))?,
1301 );
1302 self.pos += 8;
1303 Ok(val)
1304 }
1305
1306 pub fn read_bool(&mut self) -> Result<bool, SignerError> {
1308 Ok(self.read_u8()? != 0)
1309 }
1310
1311 pub fn read_bytes(&mut self, n: usize) -> Result<&'a [u8], SignerError> {
1313 if self.pos + n > self.data.len() {
1314 return Err(SignerError::ParseError(format!("read_bytes({n}): EOF")));
1315 }
1316 let slice = &self.data[self.pos..self.pos + n];
1317 self.pos += n;
1318 Ok(slice)
1319 }
1320
1321 pub fn read_pubkey(&mut self) -> Result<[u8; 32], SignerError> {
1323 let bytes = self.read_bytes(32)?;
1324 let mut key = [0u8; 32];
1325 key.copy_from_slice(bytes);
1326 Ok(key)
1327 }
1328
1329 pub fn read_string(&mut self) -> Result<String, SignerError> {
1331 let len = self.read_u32()? as usize;
1332 let bytes = self.read_bytes(len)?;
1333 String::from_utf8(bytes.to_vec())
1334 .map_err(|e| SignerError::ParseError(format!("read_string: {e}")))
1335 }
1336
1337 #[must_use]
1339 pub fn remaining(&self) -> &'a [u8] {
1340 &self.data[self.pos..]
1341 }
1342
1343 #[must_use]
1345 pub fn is_empty(&self) -> bool {
1346 self.pos >= self.data.len()
1347 }
1348}
1349
1350#[cfg(test)]
1355#[allow(clippy::unwrap_used, clippy::expect_used)]
1356mod tests {
1357 use super::*;
1358 use crate::traits::KeyPair;
1359
1360 #[test]
1363 fn test_compact_u16_zero() {
1364 assert_eq!(encode_compact_u16(0), vec![0]);
1365 assert_eq!(decode_compact_u16(&[0]).unwrap(), (0, 1));
1366 }
1367
1368 #[test]
1369 fn test_compact_u16_small() {
1370 assert_eq!(encode_compact_u16(5), vec![5]);
1371 assert_eq!(decode_compact_u16(&[5]).unwrap(), (5, 1));
1372 }
1373
1374 #[test]
1375 fn test_compact_u16_127_boundary() {
1376 assert_eq!(encode_compact_u16(0x7F), vec![0x7F]);
1377 assert_eq!(decode_compact_u16(&[0x7F]).unwrap(), (0x7F, 1));
1378 }
1379
1380 #[test]
1381 fn test_compact_u16_128() {
1382 let encoded = encode_compact_u16(0x80);
1383 assert_eq!(encoded.len(), 2);
1384 assert_eq!(decode_compact_u16(&encoded).unwrap(), (0x80, 2));
1385 }
1386
1387 #[test]
1388 fn test_compact_u16_16383() {
1389 let encoded = encode_compact_u16(0x3FFF);
1391 assert_eq!(encoded.len(), 2);
1392 assert_eq!(decode_compact_u16(&encoded).unwrap(), (0x3FFF, 2));
1393 }
1394
1395 #[test]
1396 fn test_compact_u16_16384() {
1397 let encoded = encode_compact_u16(0x4000);
1399 assert_eq!(encoded.len(), 3);
1400 assert_eq!(decode_compact_u16(&encoded).unwrap(), (0x4000, 3));
1401 }
1402
1403 #[test]
1404 fn test_compact_u16_roundtrip_all_boundaries() {
1405 for val in [0u16, 1, 127, 128, 255, 256, 16383, 16384, 32767, 65535] {
1406 let encoded = encode_compact_u16(val);
1407 let (decoded, _) = decode_compact_u16(&encoded).unwrap();
1408 assert_eq!(decoded, val, "roundtrip failed for {val}");
1409 }
1410 }
1411
1412 #[test]
1415 fn test_system_transfer_instruction() {
1416 let from = [0xAA; 32];
1417 let to = [0xBB; 32];
1418 let ix = system_program::transfer(&from, &to, 1_000_000_000);
1419 assert_eq!(ix.program_id, system_program::ID);
1420 assert_eq!(ix.accounts.len(), 2);
1421 assert_eq!(ix.accounts[0].pubkey, from);
1422 assert!(ix.accounts[0].is_signer);
1423 assert!(!ix.accounts[1].is_signer);
1424 assert_eq!(ix.data.len(), 12);
1426 assert_eq!(&ix.data[..4], &[2, 0, 0, 0]);
1427 let lamports = u64::from_le_bytes(ix.data[4..12].try_into().unwrap());
1428 assert_eq!(lamports, 1_000_000_000);
1429 }
1430
1431 #[test]
1432 fn test_system_create_account() {
1433 let from = [0xAA; 32];
1434 let new = [0xBB; 32];
1435 let owner = [0xCC; 32];
1436 let ix = system_program::create_account(&from, &new, 1_000_000, 165, &owner);
1437 assert_eq!(ix.program_id, system_program::ID);
1438 assert_eq!(ix.accounts.len(), 2);
1439 assert_eq!(ix.data.len(), 52);
1441 }
1442
1443 #[test]
1444 fn test_system_allocate() {
1445 let account = [0xAA; 32];
1446 let ix = system_program::allocate(&account, 1024);
1447 assert_eq!(ix.data.len(), 12);
1448 }
1449
1450 #[test]
1453 fn test_spl_token_transfer() {
1454 let src = [0x11; 32];
1455 let dst = [0x22; 32];
1456 let auth = [0x33; 32];
1457 let ix = spl_token::transfer(&src, &dst, &auth, 1_000_000);
1458 assert_eq!(ix.program_id, spl_token::ID);
1459 assert_eq!(ix.accounts.len(), 3);
1460 assert!(!ix.accounts[0].is_signer); assert!(!ix.accounts[1].is_signer); assert!(ix.accounts[2].is_signer); assert_eq!(ix.data[0], 3); assert_eq!(ix.data.len(), 9);
1465 }
1466
1467 #[test]
1468 fn test_spl_token_approve() {
1469 let src = [0x11; 32];
1470 let delegate = [0x22; 32];
1471 let auth = [0x33; 32];
1472 let ix = spl_token::approve(&src, &delegate, &auth, 500_000);
1473 assert_eq!(ix.data[0], 4); }
1475
1476 #[test]
1477 fn test_spl_token_mint_to() {
1478 let mint = [0x11; 32];
1479 let dst = [0x22; 32];
1480 let auth = [0x33; 32];
1481 let ix = spl_token::mint_to(&mint, &dst, &auth, 1_000);
1482 assert_eq!(ix.data[0], 7); }
1484
1485 #[test]
1488 fn test_compute_unit_limit() {
1489 let ix = compute_budget::set_compute_unit_limit(200_000);
1490 assert_eq!(ix.data[0], 2);
1491 let units = u32::from_le_bytes(ix.data[1..5].try_into().unwrap());
1492 assert_eq!(units, 200_000);
1493 assert!(ix.accounts.is_empty());
1494 }
1495
1496 #[test]
1497 fn test_compute_unit_price() {
1498 let ix = compute_budget::set_compute_unit_price(50_000);
1499 assert_eq!(ix.data[0], 3);
1500 let price = u64::from_le_bytes(ix.data[1..9].try_into().unwrap());
1501 assert_eq!(price, 50_000);
1502 }
1503
1504 #[test]
1507 fn test_message_building() {
1508 let payer = [0xAA; 32];
1509 let to = [0xBB; 32];
1510 let ix = system_program::transfer(&payer, &to, 100);
1511 let msg = Message::new(&[ix], payer);
1512
1513 assert_eq!(msg.num_required_signatures, 1);
1514 assert_eq!(msg.num_readonly_signed_accounts, 0);
1515 assert_eq!(msg.num_readonly_unsigned_accounts, 1);
1517 assert_eq!(msg.account_keys.len(), 3);
1519 assert_eq!(msg.account_keys[0], payer); }
1521
1522 #[test]
1523 fn test_message_serialization() {
1524 let payer = [0xAA; 32];
1525 let to = [0xBB; 32];
1526 let ix = system_program::transfer(&payer, &to, 100);
1527 let msg = Message::new(&[ix], payer);
1528 let bytes = msg.serialize();
1529 assert!(!bytes.is_empty());
1530 assert_eq!(bytes[0], 1); assert_eq!(bytes[1], 0); assert_eq!(bytes[2], 1); }
1535
1536 #[test]
1539 fn test_transaction_sign_and_serialize() {
1540 let signer = SolanaSigner::generate().unwrap();
1541 let payer = signer.public_key_bytes_32();
1542 let to = [0xBB; 32];
1543 let ix = system_program::transfer(&payer, &to, 1_000_000);
1544 let msg = Message::new(&[ix], payer);
1545 let blockhash = [0xCC; 32];
1546
1547 let tx = Transaction::sign(&msg, &[&signer], blockhash).unwrap();
1548 assert_eq!(tx.signatures.len(), 1);
1549 assert_eq!(tx.signatures[0].len(), 64);
1550
1551 let raw = tx.serialize();
1552 assert!(!raw.is_empty());
1553 assert_eq!(raw[0], 1);
1555 }
1556
1557 #[test]
1558 fn test_transaction_deterministic() {
1559 let signer = SolanaSigner::from_bytes(&[0x42; 32]).unwrap();
1560 let payer = signer.public_key_bytes_32();
1561 let ix = system_program::transfer(&payer, &[0xBB; 32], 100);
1562 let msg = Message::new(&[ix], payer);
1563
1564 let tx1 = Transaction::sign(&msg, &[&signer], [0; 32]).unwrap();
1565 let tx2 = Transaction::sign(&msg, &[&signer], [0; 32]).unwrap();
1566 assert_eq!(tx1.serialize(), tx2.serialize());
1567 }
1568
1569 #[test]
1572 fn test_v0_message_has_version_prefix() {
1573 let payer = [0xAA; 32];
1574 let ix = system_program::transfer(&payer, &[0xBB; 32], 100);
1575 let msg = Message::new(&[ix], payer);
1576 let v0 = MessageV0 {
1577 message: msg,
1578 address_table_lookups: vec![],
1579 };
1580 let bytes = v0.serialize();
1581 assert_eq!(bytes[0], 0x80, "v0 messages start with 0x80");
1582 }
1583
1584 #[test]
1585 fn test_v0_with_lookup_table() {
1586 let payer = [0xAA; 32];
1587 let ix = system_program::transfer(&payer, &[0xBB; 32], 100);
1588 let msg = Message::new(&[ix], payer);
1589 let v0 = MessageV0 {
1590 message: msg,
1591 address_table_lookups: vec![AddressLookupTable {
1592 account_key: [0xDD; 32],
1593 writable_indexes: vec![0, 1],
1594 readonly_indexes: vec![2],
1595 }],
1596 };
1597 let bytes = v0.serialize();
1598 assert_eq!(bytes[0], 0x80);
1599 assert!(bytes.len() > 100); }
1601
1602 #[test]
1605 fn test_find_program_address() {
1606 let program_id = [0xAA; 32];
1607 let (pda, bump) = find_program_address(&[b"test_seed"], &program_id).unwrap();
1608 assert_eq!(pda.len(), 32);
1609 assert!(bump <= 255);
1610 let (pda2, bump2) = find_program_address(&[b"test_seed"], &program_id).unwrap();
1612 assert_eq!(pda, pda2);
1613 assert_eq!(bump, bump2);
1614 }
1615
1616 #[test]
1617 fn test_pda_different_seeds() {
1618 let program_id = [0xBB; 32];
1619 let (pda1, _) = find_program_address(&[b"seed_a"], &program_id).unwrap();
1620 let (pda2, _) = find_program_address(&[b"seed_b"], &program_id).unwrap();
1621 assert_ne!(pda1, pda2);
1622 }
1623
1624 #[test]
1625 fn test_pda_seed_too_long() {
1626 let program_id = [0xCC; 32];
1627 let long_seed = [0u8; 33]; let result = create_program_address(&[&long_seed[..]], &[0], &program_id);
1629 assert!(result.is_err());
1630 }
1631
1632 #[test]
1635 fn test_message_serialize_deserialize_roundtrip() {
1636 let payer = [0xAA; 32];
1637 let to = [0xBB; 32];
1638 let ix = system_program::transfer(&payer, &to, 1_000_000);
1639 let msg = Message::new(&[ix], payer);
1640 let serialized = msg.serialize();
1641
1642 let restored = Message::deserialize(&serialized).unwrap();
1643 assert_eq!(
1644 restored.num_required_signatures,
1645 msg.num_required_signatures
1646 );
1647 assert_eq!(restored.account_keys.len(), msg.account_keys.len());
1648 assert_eq!(restored.instructions.len(), msg.instructions.len());
1649 assert_eq!(restored.instructions[0].data, msg.instructions[0].data);
1650 }
1651
1652 #[test]
1653 fn test_transaction_serialize_deserialize_roundtrip() {
1654 let signer = SolanaSigner::from_bytes(&[0x42; 32]).unwrap();
1655 let payer = signer.public_key_bytes_32();
1656 let ix = system_program::transfer(&payer, &[0xBB; 32], 100);
1657 let msg = Message::new(&[ix], payer);
1658 let tx = Transaction::sign(&msg, &[&signer], [0xDD; 32]).unwrap();
1659
1660 let raw = tx.serialize();
1661 let restored = Transaction::deserialize(&raw).unwrap();
1662
1663 assert_eq!(restored.signatures.len(), tx.signatures.len());
1664 assert_eq!(restored.signatures[0], tx.signatures[0]);
1665 assert_eq!(
1666 restored.message.account_keys.len(),
1667 tx.message.account_keys.len()
1668 );
1669 }
1670
1671 #[test]
1672 fn test_deserialize_empty_fails() {
1673 assert!(Transaction::deserialize(&[]).is_err());
1674 assert!(Message::deserialize(&[]).is_err());
1675 assert!(Message::deserialize(&[0, 0]).is_err()); }
1677
1678 #[test]
1681 fn test_instruction_data_builder_reader_roundtrip() {
1682 let data = InstructionDataBuilder::new()
1683 .write_u8(42)
1684 .write_u16(1000)
1685 .write_u32(100_000)
1686 .write_u64(1_000_000_000)
1687 .write_bool(true)
1688 .write_pubkey(&[0xAA; 32])
1689 .write_string("hello solana")
1690 .build();
1691
1692 let mut reader = InstructionDataReader::new(&data);
1693 assert_eq!(reader.read_u8().unwrap(), 42);
1694 assert_eq!(reader.read_u16().unwrap(), 1000);
1695 assert_eq!(reader.read_u32().unwrap(), 100_000);
1696 assert_eq!(reader.read_u64().unwrap(), 1_000_000_000);
1697 assert!(reader.read_bool().unwrap());
1698 assert_eq!(reader.read_pubkey().unwrap(), [0xAA; 32]);
1699 assert_eq!(reader.read_string().unwrap(), "hello solana");
1700 assert!(reader.is_empty());
1701 }
1702
1703 #[test]
1704 fn test_instruction_data_builder_option() {
1705 let none_data = InstructionDataBuilder::new().write_option(None).build();
1706 assert_eq!(none_data, vec![0]);
1707
1708 let some_data = InstructionDataBuilder::new()
1709 .write_option(Some(&[1, 2, 3]))
1710 .build();
1711 assert_eq!(some_data, vec![1, 1, 2, 3]);
1712 }
1713
1714 #[test]
1715 fn test_reader_eof_errors() {
1716 let mut reader = InstructionDataReader::new(&[]);
1717 assert!(reader.read_u8().is_err());
1718 assert!(reader.read_u64().is_err());
1719 assert!(reader.read_pubkey().is_err());
1720 }
1721
1722 #[test]
1725 fn test_spl_token_burn() {
1726 let account = [0x11; 32];
1727 let mint = [0x22; 32];
1728 let auth = [0x33; 32];
1729 let ix = spl_token::burn(&account, &mint, &auth, 500);
1730 assert_eq!(ix.data[0], 8);
1731 assert_eq!(ix.accounts.len(), 3);
1732 }
1733
1734 #[test]
1735 fn test_spl_token_close_account() {
1736 let ix = spl_token::close_account(&[0x11; 32], &[0x22; 32], &[0x33; 32]);
1737 assert_eq!(ix.data, vec![9]);
1738 }
1739
1740 #[test]
1741 fn test_spl_token_freeze_thaw() {
1742 let ix_freeze = spl_token::freeze_account(&[0x11; 32], &[0x22; 32], &[0x33; 32]);
1743 assert_eq!(ix_freeze.data, vec![10]);
1744 let ix_thaw = spl_token::thaw_account(&[0x11; 32], &[0x22; 32], &[0x33; 32]);
1745 assert_eq!(ix_thaw.data, vec![11]);
1746 }
1747
1748 #[test]
1749 fn test_spl_token_initialize_mint() {
1750 let mint = [0x11; 32];
1751 let auth = [0x22; 32];
1752 let ix = spl_token::initialize_mint(&mint, 6, &auth, Some(&[0x33; 32]));
1753 assert_eq!(ix.data[0], 0); assert_eq!(ix.data[1], 6); }
1756
1757 #[test]
1758 fn test_spl_token_set_authority() {
1759 let ix = spl_token::set_authority(
1760 &[0x11; 32],
1761 &[0x22; 32],
1762 spl_token::AuthorityType::MintTokens,
1763 Some(&[0x33; 32]),
1764 );
1765 assert_eq!(ix.data[0], 6); assert_eq!(ix.data[1], 0); assert_eq!(ix.data[2], 1); }
1769
1770 #[test]
1771 fn test_spl_token_revoke() {
1772 let ix = spl_token::revoke(&[0x11; 32], &[0x22; 32]);
1773 assert_eq!(ix.data, vec![5]);
1774 }
1775
1776 #[test]
1777 fn test_spl_token_transfer_checked() {
1778 let ix = spl_token::transfer_checked(
1779 &[0x11; 32],
1780 &[0x22; 32],
1781 &[0x33; 32],
1782 &[0x44; 32],
1783 1000,
1784 6,
1785 );
1786 assert_eq!(ix.data[0], 12); assert_eq!(ix.accounts.len(), 4); }
1789
1790 #[test]
1793 fn test_token_2022_transfer_checked() {
1794 let ix = spl_token_2022::transfer_checked(
1795 &[0x11; 32],
1796 &[0x22; 32],
1797 &[0x33; 32],
1798 &[0x44; 32],
1799 1000,
1800 9,
1801 );
1802 assert_eq!(ix.program_id, spl_token_2022::ID);
1803 assert_eq!(ix.data[0], 12);
1804 }
1805
1806 #[test]
1807 fn test_token_2022_transfer_checked_with_fee() {
1808 let ix = spl_token_2022::transfer_checked_with_fee(
1809 &[0x11; 32],
1810 &[0x22; 32],
1811 &[0x33; 32],
1812 &[0x44; 32],
1813 1000,
1814 9,
1815 50,
1816 );
1817 assert_eq!(ix.data[0], 26); let fee = u64::from_le_bytes(ix.data[10..18].try_into().unwrap());
1819 assert_eq!(fee, 50);
1820 }
1821
1822 #[test]
1825 fn test_versioned_transaction_sign() {
1826 let signer = SolanaSigner::from_bytes(&[0x42; 32]).unwrap();
1827 let payer = signer.public_key_bytes_32();
1828 let ix = system_program::transfer(&payer, &[0xBB; 32], 100);
1829 let msg = Message::new(&[ix], payer);
1830 let v0 = MessageV0 {
1831 message: msg,
1832 address_table_lookups: vec![],
1833 };
1834
1835 let vtx = VersionedTransaction::sign(&v0, &[&signer], [0xCC; 32]).unwrap();
1836 assert_eq!(vtx.signatures.len(), 1);
1837 let raw = vtx.serialize();
1838 assert!(!raw.is_empty());
1839 }
1840
1841 #[test]
1842 fn test_versioned_transaction_deterministic() {
1843 let signer = SolanaSigner::from_bytes(&[0x42; 32]).unwrap();
1844 let payer = signer.public_key_bytes_32();
1845 let ix = system_program::transfer(&payer, &[0xBB; 32], 100);
1846 let msg = Message::new(&[ix], payer);
1847 let v0 = MessageV0 {
1848 message: msg,
1849 address_table_lookups: vec![],
1850 };
1851
1852 let vtx1 = VersionedTransaction::sign(&v0, &[&signer], [0; 32]).unwrap();
1853 let vtx2 = VersionedTransaction::sign(&v0, &[&signer], [0; 32]).unwrap();
1854 assert_eq!(vtx1.serialize(), vtx2.serialize());
1855 }
1856}