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> {
56 if data.is_empty() {
57 return Err(SignerError::ParseError("compact-u16: empty".into()));
58 }
59 let b0 = data[0] as u16;
60 if b0 < 0x80 {
61 return Ok((b0, 1));
62 }
63 if data.len() < 2 {
64 return Err(SignerError::ParseError("compact-u16: truncated".into()));
65 }
66 let b1 = data[1] as u16;
67 if b1 < 0x80 {
68 let val = (b0 & 0x7F) | (b1 << 7);
69 if b1 == 0 {
71 return Err(SignerError::ParseError(
72 "compact-u16: overlong 2-byte encoding".into(),
73 ));
74 }
75 return Ok((val, 2));
76 }
77 if data.len() < 3 {
78 return Err(SignerError::ParseError("compact-u16: truncated".into()));
79 }
80 let b2 = data[2] as u16;
81 if b2 > 3 {
84 return Err(SignerError::ParseError(
85 "compact-u16: value exceeds u16::MAX".into(),
86 ));
87 }
88 let val = (b0 & 0x7F) | ((b1 & 0x7F) << 7) | (b2 << 14);
89 if b2 == 0 {
91 return Err(SignerError::ParseError(
92 "compact-u16: overlong 3-byte encoding".into(),
93 ));
94 }
95 Ok((val, 3))
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
102pub struct AccountMeta {
103 pub pubkey: [u8; 32],
105 pub is_signer: bool,
107 pub is_writable: bool,
109}
110
111impl AccountMeta {
112 #[must_use]
114 pub fn new(pubkey: [u8; 32], is_signer: bool) -> Self {
115 Self {
116 pubkey,
117 is_signer,
118 is_writable: true,
119 }
120 }
121
122 #[must_use]
124 pub fn new_readonly(pubkey: [u8; 32], is_signer: bool) -> Self {
125 Self {
126 pubkey,
127 is_signer,
128 is_writable: false,
129 }
130 }
131}
132
133#[derive(Debug, Clone)]
137pub struct Instruction {
138 pub program_id: [u8; 32],
140 pub accounts: Vec<AccountMeta>,
142 pub data: Vec<u8>,
144}
145
146#[derive(Debug, Clone)]
150pub struct Message {
151 pub num_required_signatures: u8,
153 pub num_readonly_signed_accounts: u8,
155 pub num_readonly_unsigned_accounts: u8,
157 pub account_keys: Vec<[u8; 32]>,
159 pub recent_blockhash: [u8; 32],
161 pub instructions: Vec<CompiledInstruction>,
163}
164
165#[derive(Debug, Clone)]
167pub struct CompiledInstruction {
168 pub program_id_index: u8,
170 pub accounts: Vec<u8>,
172 pub data: Vec<u8>,
174}
175
176impl Message {
177 #[must_use]
184 pub fn new(instructions: &[Instruction], fee_payer: [u8; 32]) -> Self {
185 match Self::try_new(instructions, fee_payer) {
186 Ok(msg) => msg,
187 Err(_) => {
188 #[allow(clippy::panic)]
192 {
193 panic!(
194 "Message::new: instruction references unknown account key \
195 or key list exceeds u8::MAX"
196 );
197 }
198 }
199 }
200 }
201
202 pub fn try_new(instructions: &[Instruction], fee_payer: [u8; 32]) -> Result<Self, SignerError> {
207 let mut writable_signers: Vec<[u8; 32]> = vec![fee_payer];
208 let mut readonly_signers: Vec<[u8; 32]> = Vec::new();
209 let mut writable_nonsigners: Vec<[u8; 32]> = Vec::new();
210 let mut readonly_nonsigners: Vec<[u8; 32]> = Vec::new();
211
212 for ix in instructions {
213 for acc in &ix.accounts {
214 if acc.pubkey == fee_payer {
216 continue;
217 }
218 match (acc.is_signer, acc.is_writable) {
219 (true, true) => {
220 if !writable_signers.contains(&acc.pubkey) {
221 writable_signers.push(acc.pubkey);
222 }
223 }
224 (true, false) => {
225 if !readonly_signers.contains(&acc.pubkey) {
226 readonly_signers.push(acc.pubkey);
227 }
228 }
229 (false, true) => {
230 if !writable_nonsigners.contains(&acc.pubkey) {
231 writable_nonsigners.push(acc.pubkey);
232 }
233 }
234 (false, false) => {
235 if !readonly_nonsigners.contains(&acc.pubkey) {
236 readonly_nonsigners.push(acc.pubkey);
237 }
238 }
239 }
240 }
241 if !writable_signers.contains(&ix.program_id)
243 && !readonly_signers.contains(&ix.program_id)
244 && !writable_nonsigners.contains(&ix.program_id)
245 && !readonly_nonsigners.contains(&ix.program_id)
246 {
247 readonly_nonsigners.push(ix.program_id);
248 }
249 }
250
251 let total_keys = writable_signers.len()
252 + readonly_signers.len()
253 + writable_nonsigners.len()
254 + readonly_nonsigners.len();
255 if total_keys > 256 {
256 return Err(SignerError::ParseError(format!(
257 "too many account keys: {total_keys}, max 256"
258 )));
259 }
260
261 let num_required_signatures = (writable_signers.len() + readonly_signers.len()) as u8;
262 let num_readonly_signed = readonly_signers.len() as u8;
263 let num_readonly_unsigned = readonly_nonsigners.len() as u8;
264
265 let mut account_keys = Vec::new();
266 account_keys.extend_from_slice(&writable_signers);
267 account_keys.extend_from_slice(&readonly_signers);
268 account_keys.extend_from_slice(&writable_nonsigners);
269 account_keys.extend_from_slice(&readonly_nonsigners);
270
271 let mut compiled = Vec::with_capacity(instructions.len());
273 for ix in instructions {
274 let program_id_index = account_keys
275 .iter()
276 .position(|k| *k == ix.program_id)
277 .ok_or_else(|| {
278 SignerError::ParseError("program id not found in account keys".into())
279 })? as u8;
280 let mut accounts = Vec::with_capacity(ix.accounts.len());
281 for a in &ix.accounts {
282 let idx = account_keys
283 .iter()
284 .position(|k| *k == a.pubkey)
285 .ok_or_else(|| {
286 SignerError::ParseError("account key not found in key list".into())
287 })? as u8;
288 accounts.push(idx);
289 }
290 compiled.push(CompiledInstruction {
291 program_id_index,
292 accounts,
293 data: ix.data.clone(),
294 });
295 }
296
297 Ok(Self {
298 num_required_signatures,
299 num_readonly_signed_accounts: num_readonly_signed,
300 num_readonly_unsigned_accounts: num_readonly_unsigned,
301 account_keys,
302 recent_blockhash: [0u8; 32], instructions: compiled,
304 })
305 }
306
307 #[must_use]
309 pub fn serialize(&self) -> Vec<u8> {
310 let mut buf = Vec::new();
311 buf.push(self.num_required_signatures);
312 buf.push(self.num_readonly_signed_accounts);
313 buf.push(self.num_readonly_unsigned_accounts);
314
315 buf.extend_from_slice(&encode_compact_u16(self.account_keys.len() as u16));
316 for key in &self.account_keys {
317 buf.extend_from_slice(key);
318 }
319
320 buf.extend_from_slice(&self.recent_blockhash);
321
322 buf.extend_from_slice(&encode_compact_u16(self.instructions.len() as u16));
323 for ix in &self.instructions {
324 buf.push(ix.program_id_index);
325 buf.extend_from_slice(&encode_compact_u16(ix.accounts.len() as u16));
326 buf.extend_from_slice(&ix.accounts);
327 buf.extend_from_slice(&encode_compact_u16(ix.data.len() as u16));
328 buf.extend_from_slice(&ix.data);
329 }
330 buf
331 }
332}
333
334#[derive(Debug, Clone)]
338pub struct Transaction {
339 pub signatures: Vec<[u8; 64]>,
341 pub message: Message,
343}
344
345impl Transaction {
346 pub fn sign(
348 message: &Message,
349 signers: &[&SolanaSigner],
350 recent_blockhash: [u8; 32],
351 ) -> Result<Self, SignerError> {
352 let mut msg = message.clone();
353 msg.recent_blockhash = recent_blockhash;
354 let serialized = msg.serialize();
355
356 let mut signatures = Vec::new();
357 for signer in signers {
358 let sig = signer.signing_key.sign(&serialized);
359 signatures.push(sig.to_bytes());
360 }
361
362 Ok(Self {
363 signatures,
364 message: msg,
365 })
366 }
367
368 #[must_use]
370 pub fn serialize(&self) -> Vec<u8> {
371 let mut buf = Vec::new();
372 buf.extend_from_slice(&encode_compact_u16(self.signatures.len() as u16));
373 for sig in &self.signatures {
374 buf.extend_from_slice(sig);
375 }
376 buf.extend_from_slice(&self.message.serialize());
377 buf
378 }
379}
380
381pub mod system_program {
387 use super::*;
388
389 pub const ID: [u8; 32] = [0; 32];
391
392 #[must_use]
399 pub fn transfer(from: &[u8; 32], to: &[u8; 32], lamports: u64) -> Instruction {
400 let mut data = vec![2, 0, 0, 0]; data.extend_from_slice(&lamports.to_le_bytes());
402 Instruction {
403 program_id: ID,
404 accounts: vec![AccountMeta::new(*from, true), AccountMeta::new(*to, false)],
405 data,
406 }
407 }
408
409 #[must_use]
411 pub fn create_account(
412 from: &[u8; 32],
413 new_account: &[u8; 32],
414 lamports: u64,
415 space: u64,
416 owner: &[u8; 32],
417 ) -> Instruction {
418 let mut data = vec![0, 0, 0, 0]; data.extend_from_slice(&lamports.to_le_bytes());
420 data.extend_from_slice(&space.to_le_bytes());
421 data.extend_from_slice(owner);
422 Instruction {
423 program_id: ID,
424 accounts: vec![
425 AccountMeta::new(*from, true),
426 AccountMeta::new(*new_account, true),
427 ],
428 data,
429 }
430 }
431
432 #[must_use]
434 pub fn allocate(account: &[u8; 32], space: u64) -> Instruction {
435 let mut data = vec![8, 0, 0, 0]; data.extend_from_slice(&space.to_le_bytes());
437 Instruction {
438 program_id: ID,
439 accounts: vec![AccountMeta::new(*account, true)],
440 data,
441 }
442 }
443}
444
445pub mod spl_token {
451 use super::*;
452
453 pub const ID: [u8; 32] = [
455 0x06, 0xDD, 0xF6, 0xE1, 0xD7, 0x65, 0xA1, 0x93, 0xD9, 0xCB, 0xE1, 0x46, 0xCE, 0xEB, 0x79,
456 0xAC, 0x1C, 0xB4, 0x85, 0xED, 0x5F, 0x5B, 0x37, 0x91, 0x3A, 0x8C, 0xF5, 0x85, 0x7E, 0xFF,
457 0x00, 0xA9,
458 ];
459
460 #[must_use]
468 pub fn transfer(
469 source: &[u8; 32],
470 destination: &[u8; 32],
471 authority: &[u8; 32],
472 amount: u64,
473 ) -> Instruction {
474 let mut data = vec![3]; data.extend_from_slice(&amount.to_le_bytes());
476 Instruction {
477 program_id: ID,
478 accounts: vec![
479 AccountMeta::new(*source, false),
480 AccountMeta::new(*destination, false),
481 AccountMeta::new_readonly(*authority, true),
482 ],
483 data,
484 }
485 }
486
487 #[must_use]
489 pub fn approve(
490 source: &[u8; 32],
491 delegate: &[u8; 32],
492 authority: &[u8; 32],
493 amount: u64,
494 ) -> Instruction {
495 let mut data = vec![4]; data.extend_from_slice(&amount.to_le_bytes());
497 Instruction {
498 program_id: ID,
499 accounts: vec![
500 AccountMeta::new(*source, false),
501 AccountMeta::new_readonly(*delegate, false),
502 AccountMeta::new_readonly(*authority, true),
503 ],
504 data,
505 }
506 }
507
508 #[must_use]
510 pub fn mint_to(
511 mint: &[u8; 32],
512 destination: &[u8; 32],
513 authority: &[u8; 32],
514 amount: u64,
515 ) -> Instruction {
516 let mut data = vec![7]; data.extend_from_slice(&amount.to_le_bytes());
518 Instruction {
519 program_id: ID,
520 accounts: vec![
521 AccountMeta::new(*mint, false),
522 AccountMeta::new(*destination, false),
523 AccountMeta::new_readonly(*authority, true),
524 ],
525 data,
526 }
527 }
528
529 #[must_use]
531 pub fn burn(
532 token_account: &[u8; 32],
533 mint: &[u8; 32],
534 authority: &[u8; 32],
535 amount: u64,
536 ) -> Instruction {
537 let mut data = vec![8]; data.extend_from_slice(&amount.to_le_bytes());
539 Instruction {
540 program_id: ID,
541 accounts: vec![
542 AccountMeta::new(*token_account, false),
543 AccountMeta::new(*mint, false),
544 AccountMeta::new_readonly(*authority, true),
545 ],
546 data,
547 }
548 }
549
550 #[must_use]
552 pub fn close_account(
553 account: &[u8; 32],
554 destination: &[u8; 32],
555 authority: &[u8; 32],
556 ) -> Instruction {
557 Instruction {
558 program_id: ID,
559 accounts: vec![
560 AccountMeta::new(*account, false),
561 AccountMeta::new(*destination, false),
562 AccountMeta::new_readonly(*authority, true),
563 ],
564 data: vec![9], }
566 }
567
568 #[must_use]
570 pub fn freeze_account(
571 account: &[u8; 32],
572 mint: &[u8; 32],
573 freeze_authority: &[u8; 32],
574 ) -> Instruction {
575 Instruction {
576 program_id: ID,
577 accounts: vec![
578 AccountMeta::new(*account, false),
579 AccountMeta::new_readonly(*mint, false),
580 AccountMeta::new_readonly(*freeze_authority, true),
581 ],
582 data: vec![10], }
584 }
585
586 #[must_use]
588 pub fn thaw_account(
589 account: &[u8; 32],
590 mint: &[u8; 32],
591 freeze_authority: &[u8; 32],
592 ) -> Instruction {
593 Instruction {
594 program_id: ID,
595 accounts: vec![
596 AccountMeta::new(*account, false),
597 AccountMeta::new_readonly(*mint, false),
598 AccountMeta::new_readonly(*freeze_authority, true),
599 ],
600 data: vec![11], }
602 }
603
604 #[must_use]
612 pub fn initialize_mint(
613 mint: &[u8; 32],
614 decimals: u8,
615 mint_authority: &[u8; 32],
616 freeze_authority: Option<&[u8; 32]>,
617 ) -> Instruction {
618 let rent_sysvar: [u8; 32] = {
619 let mut id = [0u8; 32];
620 id[0] = 0x06;
622 id[1] = 0xa7;
623 id[2] = 0xd5;
624 id[3] = 0x17;
625 id[4] = 0x19;
626 id[5] = 0x2c;
627 id
628 };
629 let mut data = vec![0]; data.push(decimals);
631 data.extend_from_slice(mint_authority);
632 match freeze_authority {
633 Some(auth) => {
634 data.push(1); data.extend_from_slice(auth);
636 }
637 None => {
638 data.push(0); data.extend_from_slice(&[0u8; 32]);
640 }
641 }
642 Instruction {
643 program_id: ID,
644 accounts: vec![
645 AccountMeta::new(*mint, false),
646 AccountMeta::new_readonly(rent_sysvar, false),
647 ],
648 data,
649 }
650 }
651
652 #[must_use]
654 pub fn initialize_account(
655 account: &[u8; 32],
656 mint: &[u8; 32],
657 owner: &[u8; 32],
658 ) -> Instruction {
659 let rent_sysvar: [u8; 32] = {
660 let mut id = [0u8; 32];
661 id[0] = 0x06;
662 id[1] = 0xa7;
663 id[2] = 0xd5;
664 id[3] = 0x17;
665 id[4] = 0x19;
666 id[5] = 0x2c;
667 id
668 };
669 Instruction {
670 program_id: ID,
671 accounts: vec![
672 AccountMeta::new(*account, false),
673 AccountMeta::new_readonly(*mint, false),
674 AccountMeta::new_readonly(*owner, false),
675 AccountMeta::new_readonly(rent_sysvar, false),
676 ],
677 data: vec![1], }
679 }
680
681 #[derive(Debug, Clone, Copy)]
683 pub enum AuthorityType {
684 MintTokens = 0,
686 FreezeAccount = 1,
688 AccountOwner = 2,
690 CloseAccount = 3,
692 }
693
694 #[must_use]
696 pub fn set_authority(
697 account_or_mint: &[u8; 32],
698 current_authority: &[u8; 32],
699 authority_type: AuthorityType,
700 new_authority: Option<&[u8; 32]>,
701 ) -> Instruction {
702 let mut data = vec![6]; data.push(authority_type as u8);
704 match new_authority {
705 Some(auth) => {
706 data.push(1); data.extend_from_slice(auth);
708 }
709 None => {
710 data.push(0); data.extend_from_slice(&[0u8; 32]);
712 }
713 }
714 Instruction {
715 program_id: ID,
716 accounts: vec![
717 AccountMeta::new(*account_or_mint, false),
718 AccountMeta::new_readonly(*current_authority, true),
719 ],
720 data,
721 }
722 }
723
724 #[must_use]
726 pub fn revoke(source: &[u8; 32], authority: &[u8; 32]) -> Instruction {
727 Instruction {
728 program_id: ID,
729 accounts: vec![
730 AccountMeta::new(*source, false),
731 AccountMeta::new_readonly(*authority, true),
732 ],
733 data: vec![5], }
735 }
736
737 #[must_use]
741 pub fn transfer_checked(
742 source: &[u8; 32],
743 mint: &[u8; 32],
744 destination: &[u8; 32],
745 authority: &[u8; 32],
746 amount: u64,
747 decimals: u8,
748 ) -> Instruction {
749 let mut data = vec![12]; data.extend_from_slice(&amount.to_le_bytes());
751 data.push(decimals);
752 Instruction {
753 program_id: ID,
754 accounts: vec![
755 AccountMeta::new(*source, false),
756 AccountMeta::new_readonly(*mint, false),
757 AccountMeta::new(*destination, false),
758 AccountMeta::new_readonly(*authority, true),
759 ],
760 data,
761 }
762 }
763}
764
765pub mod spl_token_2022 {
771 use super::*;
772
773 pub const ID: [u8; 32] = [
775 0x06, 0xDD, 0xF6, 0xE1, 0xEE, 0x75, 0x8F, 0xDE, 0x18, 0x42, 0x5D, 0xBC, 0xE4, 0x6C, 0xCD,
776 0xDA, 0xB6, 0x1A, 0xFC, 0x4D, 0x83, 0xB9, 0x0D, 0x27, 0xFE, 0xBD, 0xF9, 0x28, 0xD8, 0xA1,
777 0x8B, 0xFC,
778 ];
779
780 #[must_use]
782 pub fn transfer_checked(
783 source: &[u8; 32],
784 mint: &[u8; 32],
785 destination: &[u8; 32],
786 authority: &[u8; 32],
787 amount: u64,
788 decimals: u8,
789 ) -> Instruction {
790 let mut data = vec![12]; data.extend_from_slice(&amount.to_le_bytes());
792 data.push(decimals);
793 Instruction {
794 program_id: ID,
795 accounts: vec![
796 AccountMeta::new(*source, false),
797 AccountMeta::new_readonly(*mint, false),
798 AccountMeta::new(*destination, false),
799 AccountMeta::new_readonly(*authority, true),
800 ],
801 data,
802 }
803 }
804
805 #[must_use]
809 pub fn transfer_checked_with_fee(
810 source: &[u8; 32],
811 mint: &[u8; 32],
812 destination: &[u8; 32],
813 authority: &[u8; 32],
814 amount: u64,
815 decimals: u8,
816 fee: u64,
817 ) -> Instruction {
818 let mut data = vec![26]; data.extend_from_slice(&amount.to_le_bytes());
820 data.push(decimals);
821 data.extend_from_slice(&fee.to_le_bytes());
822 Instruction {
823 program_id: ID,
824 accounts: vec![
825 AccountMeta::new(*source, false),
826 AccountMeta::new_readonly(*mint, false),
827 AccountMeta::new(*destination, false),
828 AccountMeta::new_readonly(*authority, true),
829 ],
830 data,
831 }
832 }
833}
834
835pub mod compute_budget {
841 use super::*;
842
843 pub const ID: [u8; 32] = [
845 0x03, 0x06, 0x46, 0x6F, 0xE5, 0x21, 0x17, 0x32, 0xFF, 0xEC, 0xAD, 0xBA, 0x72, 0xC3, 0x9B,
846 0xE7, 0xBC, 0x8C, 0xE5, 0xBB, 0xC5, 0xF7, 0x12, 0x6B, 0x2C, 0x43, 0x9B, 0x3A, 0x40, 0x00,
847 0x00, 0x00,
848 ];
849
850 #[must_use]
852 pub fn set_compute_unit_limit(units: u32) -> Instruction {
853 let mut data = vec![2]; data.extend_from_slice(&units.to_le_bytes());
855 Instruction {
856 program_id: ID,
857 accounts: vec![],
858 data,
859 }
860 }
861
862 #[must_use]
864 pub fn set_compute_unit_price(micro_lamports: u64) -> Instruction {
865 let mut data = vec![3]; data.extend_from_slice(µ_lamports.to_le_bytes());
867 Instruction {
868 program_id: ID,
869 accounts: vec![],
870 data,
871 }
872 }
873}
874
875#[derive(Debug, Clone)]
881pub struct AddressLookupTable {
882 pub account_key: [u8; 32],
884 pub writable_indexes: Vec<u8>,
886 pub readonly_indexes: Vec<u8>,
888}
889
890#[derive(Debug, Clone)]
892pub struct MessageV0 {
893 pub message: Message,
895 pub address_table_lookups: Vec<AddressLookupTable>,
897}
898
899impl MessageV0 {
900 #[must_use]
904 pub fn serialize(&self) -> Vec<u8> {
905 let mut buf = Vec::new();
906 buf.push(0x80); buf.extend_from_slice(&self.message.serialize());
908
909 buf.extend_from_slice(&encode_compact_u16(self.address_table_lookups.len() as u16));
911 for table in &self.address_table_lookups {
912 buf.extend_from_slice(&table.account_key);
913 buf.extend_from_slice(&encode_compact_u16(table.writable_indexes.len() as u16));
914 buf.extend_from_slice(&table.writable_indexes);
915 buf.extend_from_slice(&encode_compact_u16(table.readonly_indexes.len() as u16));
916 buf.extend_from_slice(&table.readonly_indexes);
917 }
918 buf
919 }
920}
921
922#[derive(Debug, Clone)]
924pub struct VersionedTransaction {
925 pub signatures: Vec<[u8; 64]>,
927 pub message: MessageV0,
929}
930
931impl VersionedTransaction {
932 pub fn sign(
934 message: &MessageV0,
935 signers: &[&SolanaSigner],
936 recent_blockhash: [u8; 32],
937 ) -> Result<Self, SignerError> {
938 let mut msg = message.clone();
939 msg.message.recent_blockhash = recent_blockhash;
940 let serialized = msg.serialize();
941
942 let mut signatures = Vec::new();
943 for signer in signers {
944 let sig = signer.signing_key.sign(&serialized);
945 signatures.push(sig.to_bytes());
946 }
947
948 Ok(Self {
949 signatures,
950 message: msg,
951 })
952 }
953
954 #[must_use]
956 pub fn serialize(&self) -> Vec<u8> {
957 let mut buf = Vec::new();
958 buf.extend_from_slice(&encode_compact_u16(self.signatures.len() as u16));
959 for sig in &self.signatures {
960 buf.extend_from_slice(sig);
961 }
962 buf.extend_from_slice(&self.message.serialize());
963 buf
964 }
965}
966
967pub fn find_program_address(
989 seeds: &[&[u8]],
990 program_id: &[u8; 32],
991) -> Result<([u8; 32], u8), SignerError> {
992 for bump in (0..=255u8).rev() {
993 if let Ok(addr) = create_program_address(seeds, &[bump], program_id) {
994 return Ok((addr, bump));
995 }
996 }
997 Err(SignerError::SigningFailed(
998 "PDA: no valid bump found".into(),
999 ))
1000}
1001
1002pub fn create_program_address(
1006 seeds: &[&[u8]],
1007 bump: &[u8],
1008 program_id: &[u8; 32],
1009) -> Result<[u8; 32], SignerError> {
1010 use sha2::{Digest, Sha256};
1011
1012 let mut hasher = Sha256::new();
1013 for seed in seeds {
1014 if seed.len() > 32 {
1015 return Err(SignerError::SigningFailed("PDA seed > 32 bytes".into()));
1016 }
1017 hasher.update(seed);
1018 }
1019 hasher.update(bump);
1020 hasher.update(program_id);
1021 hasher.update(b"ProgramDerivedAddress");
1022 let hash = hasher.finalize();
1023
1024 let mut candidate = [0u8; 32];
1025 candidate.copy_from_slice(&hash);
1026
1027 if ed25519_dalek::VerifyingKey::from_bytes(&candidate).is_ok() {
1032 return Err(SignerError::SigningFailed("PDA: on curve".into()));
1033 }
1034
1035 Ok(candidate)
1036}
1037
1038impl Message {
1043 pub fn deserialize(data: &[u8]) -> Result<Self, SignerError> {
1045 if data.len() < 3 {
1046 return Err(SignerError::ParseError("message too short".into()));
1047 }
1048 let num_required_signatures = data[0];
1049 let num_readonly_signed_accounts = data[1];
1050 let num_readonly_unsigned_accounts = data[2];
1051 let mut pos = 3;
1052
1053 let (num_keys, consumed) = decode_compact_u16(&data[pos..])?;
1055 pos += consumed;
1056
1057 if num_required_signatures as u16 > num_keys {
1059 return Err(SignerError::ParseError(
1060 "message: num_required_signatures exceeds account count".into(),
1061 ));
1062 }
1063 if (num_readonly_signed_accounts as u16) + (num_readonly_unsigned_accounts as u16)
1064 > num_keys
1065 {
1066 return Err(SignerError::ParseError(
1067 "message: readonly counts exceed account count".into(),
1068 ));
1069 }
1070
1071 let mut account_keys = Vec::with_capacity(num_keys as usize);
1072 for _ in 0..num_keys {
1073 if pos + 32 > data.len() {
1074 return Err(SignerError::ParseError(
1075 "message: truncated account key".into(),
1076 ));
1077 }
1078 let mut key = [0u8; 32];
1079 key.copy_from_slice(&data[pos..pos + 32]);
1080 account_keys.push(key);
1081 pos += 32;
1082 }
1083
1084 if pos + 32 > data.len() {
1086 return Err(SignerError::ParseError(
1087 "message: truncated blockhash".into(),
1088 ));
1089 }
1090 let mut recent_blockhash = [0u8; 32];
1091 recent_blockhash.copy_from_slice(&data[pos..pos + 32]);
1092 pos += 32;
1093
1094 let (num_ix, consumed) = decode_compact_u16(&data[pos..])?;
1096 pos += consumed;
1097 let mut instructions = Vec::with_capacity(num_ix as usize);
1098 for _ in 0..num_ix {
1099 if pos >= data.len() {
1100 return Err(SignerError::ParseError(
1101 "message: truncated instruction".into(),
1102 ));
1103 }
1104 let program_id_index = data[pos];
1105 pos += 1;
1106
1107 if program_id_index as u16 >= num_keys {
1109 return Err(SignerError::ParseError(
1110 "message: program_id_index out of range".into(),
1111 ));
1112 }
1113
1114 let (num_accounts, consumed) = decode_compact_u16(&data[pos..])?;
1115 pos += consumed;
1116 if pos + num_accounts as usize > data.len() {
1117 return Err(SignerError::ParseError(
1118 "message: truncated instruction accounts".into(),
1119 ));
1120 }
1121 let accounts = data[pos..pos + num_accounts as usize].to_vec();
1122 pos += num_accounts as usize;
1123
1124 for &acct_idx in &accounts {
1126 if acct_idx as u16 >= num_keys {
1127 return Err(SignerError::ParseError(
1128 "message: instruction account index out of range".into(),
1129 ));
1130 }
1131 }
1132
1133 let (data_len, consumed) = decode_compact_u16(&data[pos..])?;
1134 pos += consumed;
1135 if pos + data_len as usize > data.len() {
1136 return Err(SignerError::ParseError(
1137 "message: truncated instruction data".into(),
1138 ));
1139 }
1140 let ix_data = data[pos..pos + data_len as usize].to_vec();
1141 pos += data_len as usize;
1142
1143 instructions.push(CompiledInstruction {
1144 program_id_index,
1145 accounts,
1146 data: ix_data,
1147 });
1148 }
1149
1150 if pos != data.len() {
1152 return Err(SignerError::ParseError(format!(
1153 "message: {} trailing bytes",
1154 data.len() - pos
1155 )));
1156 }
1157
1158 Ok(Self {
1159 num_required_signatures,
1160 num_readonly_signed_accounts,
1161 num_readonly_unsigned_accounts,
1162 account_keys,
1163 recent_blockhash,
1164 instructions,
1165 })
1166 }
1167}
1168
1169impl Transaction {
1170 pub fn deserialize(data: &[u8]) -> Result<Self, SignerError> {
1172 let mut pos = 0;
1173
1174 let (num_sigs, consumed) = decode_compact_u16(&data[pos..])?;
1176 pos += consumed;
1177 let mut signatures = Vec::with_capacity(num_sigs as usize);
1178 for _ in 0..num_sigs {
1179 if pos + 64 > data.len() {
1180 return Err(SignerError::ParseError(
1181 "transaction: truncated signature".into(),
1182 ));
1183 }
1184 let mut sig = [0u8; 64];
1185 sig.copy_from_slice(&data[pos..pos + 64]);
1186 signatures.push(sig);
1187 pos += 64;
1188 }
1189
1190 let message = Message::deserialize(&data[pos..])?;
1192
1193 if signatures.len() != message.num_required_signatures as usize {
1195 return Err(SignerError::ParseError(format!(
1196 "transaction: {} signatures but header requires {}",
1197 signatures.len(),
1198 message.num_required_signatures
1199 )));
1200 }
1201
1202 Ok(Self {
1203 signatures,
1204 message,
1205 })
1206 }
1207}
1208
1209pub struct InstructionDataBuilder {
1230 buf: Vec<u8>,
1231}
1232
1233impl InstructionDataBuilder {
1234 #[must_use]
1236 pub fn new() -> Self {
1237 Self { buf: Vec::new() }
1238 }
1239
1240 #[must_use]
1242 pub fn write_u8(mut self, val: u8) -> Self {
1243 self.buf.push(val);
1244 self
1245 }
1246
1247 #[must_use]
1249 pub fn write_u16(mut self, val: u16) -> Self {
1250 self.buf.extend_from_slice(&val.to_le_bytes());
1251 self
1252 }
1253
1254 #[must_use]
1256 pub fn write_u32(mut self, val: u32) -> Self {
1257 self.buf.extend_from_slice(&val.to_le_bytes());
1258 self
1259 }
1260
1261 #[must_use]
1263 pub fn write_u64(mut self, val: u64) -> Self {
1264 self.buf.extend_from_slice(&val.to_le_bytes());
1265 self
1266 }
1267
1268 #[must_use]
1270 pub fn write_i64(mut self, val: i64) -> Self {
1271 self.buf.extend_from_slice(&val.to_le_bytes());
1272 self
1273 }
1274
1275 #[must_use]
1277 pub fn write_bool(mut self, val: bool) -> Self {
1278 self.buf.push(u8::from(val));
1279 self
1280 }
1281
1282 #[must_use]
1284 pub fn write_bytes(mut self, data: &[u8]) -> Self {
1285 self.buf.extend_from_slice(data);
1286 self
1287 }
1288
1289 #[must_use]
1291 pub fn write_pubkey(self, key: &[u8; 32]) -> Self {
1292 self.write_bytes(key)
1293 }
1294
1295 #[must_use]
1297 pub fn write_string(mut self, s: &str) -> Self {
1298 let bytes = s.as_bytes();
1299 self.buf
1300 .extend_from_slice(&(bytes.len() as u32).to_le_bytes());
1301 self.buf.extend_from_slice(bytes);
1302 self
1303 }
1304
1305 #[must_use]
1307 pub fn write_option(mut self, val: Option<&[u8]>) -> Self {
1308 match val {
1309 None => {
1310 self.buf.push(0);
1311 }
1312 Some(data) => {
1313 self.buf.push(1);
1314 self.buf.extend_from_slice(data);
1315 }
1316 }
1317 self
1318 }
1319
1320 #[must_use]
1322 pub fn write_vec(mut self, data: &[u8]) -> Self {
1323 self.buf
1324 .extend_from_slice(&(data.len() as u32).to_le_bytes());
1325 self.buf.extend_from_slice(data);
1326 self
1327 }
1328
1329 #[must_use]
1331 pub fn build(self) -> Vec<u8> {
1332 self.buf
1333 }
1334}
1335
1336impl Default for InstructionDataBuilder {
1337 fn default() -> Self {
1338 Self::new()
1339 }
1340}
1341
1342pub struct InstructionDataReader<'a> {
1346 data: &'a [u8],
1347 pos: usize,
1348}
1349
1350impl<'a> InstructionDataReader<'a> {
1351 pub fn new(data: &'a [u8]) -> Self {
1353 Self { data, pos: 0 }
1354 }
1355
1356 pub fn read_u8(&mut self) -> Result<u8, SignerError> {
1358 if self.pos >= self.data.len() {
1359 return Err(SignerError::ParseError("read_u8: EOF".into()));
1360 }
1361 let val = self.data[self.pos];
1362 self.pos += 1;
1363 Ok(val)
1364 }
1365
1366 pub fn read_u16(&mut self) -> Result<u16, SignerError> {
1368 if self.pos + 2 > self.data.len() {
1369 return Err(SignerError::ParseError("read_u16: EOF".into()));
1370 }
1371 let val = u16::from_le_bytes(
1372 self.data[self.pos..self.pos + 2]
1373 .try_into()
1374 .map_err(|_| SignerError::ParseError("read_u16: bad bytes".into()))?,
1375 );
1376 self.pos += 2;
1377 Ok(val)
1378 }
1379
1380 pub fn read_u32(&mut self) -> Result<u32, SignerError> {
1382 if self.pos + 4 > self.data.len() {
1383 return Err(SignerError::ParseError("read_u32: EOF".into()));
1384 }
1385 let val = u32::from_le_bytes(
1386 self.data[self.pos..self.pos + 4]
1387 .try_into()
1388 .map_err(|_| SignerError::ParseError("read_u32: bad bytes".into()))?,
1389 );
1390 self.pos += 4;
1391 Ok(val)
1392 }
1393
1394 pub fn read_u64(&mut self) -> Result<u64, SignerError> {
1396 if self.pos + 8 > self.data.len() {
1397 return Err(SignerError::ParseError("read_u64: EOF".into()));
1398 }
1399 let val = u64::from_le_bytes(
1400 self.data[self.pos..self.pos + 8]
1401 .try_into()
1402 .map_err(|_| SignerError::ParseError("read_u64: bad bytes".into()))?,
1403 );
1404 self.pos += 8;
1405 Ok(val)
1406 }
1407
1408 pub fn read_bool(&mut self) -> Result<bool, SignerError> {
1410 Ok(self.read_u8()? != 0)
1411 }
1412
1413 pub fn read_bytes(&mut self, n: usize) -> Result<&'a [u8], SignerError> {
1415 if self.pos + n > self.data.len() {
1416 return Err(SignerError::ParseError(format!("read_bytes({n}): EOF")));
1417 }
1418 let slice = &self.data[self.pos..self.pos + n];
1419 self.pos += n;
1420 Ok(slice)
1421 }
1422
1423 pub fn read_pubkey(&mut self) -> Result<[u8; 32], SignerError> {
1425 let bytes = self.read_bytes(32)?;
1426 let mut key = [0u8; 32];
1427 key.copy_from_slice(bytes);
1428 Ok(key)
1429 }
1430
1431 pub fn read_string(&mut self) -> Result<String, SignerError> {
1433 let len = self.read_u32()? as usize;
1434 let bytes = self.read_bytes(len)?;
1435 String::from_utf8(bytes.to_vec())
1436 .map_err(|e| SignerError::ParseError(format!("read_string: {e}")))
1437 }
1438
1439 #[must_use]
1441 pub fn remaining(&self) -> &'a [u8] {
1442 &self.data[self.pos..]
1443 }
1444
1445 #[must_use]
1447 pub fn is_empty(&self) -> bool {
1448 self.pos >= self.data.len()
1449 }
1450}
1451
1452#[cfg(test)]
1457#[allow(clippy::unwrap_used, clippy::expect_used)]
1458mod tests {
1459 use super::*;
1460 use crate::traits::KeyPair;
1461
1462 #[test]
1465 fn test_compact_u16_zero() {
1466 assert_eq!(encode_compact_u16(0), vec![0]);
1467 assert_eq!(decode_compact_u16(&[0]).unwrap(), (0, 1));
1468 }
1469
1470 #[test]
1471 fn test_compact_u16_small() {
1472 assert_eq!(encode_compact_u16(5), vec![5]);
1473 assert_eq!(decode_compact_u16(&[5]).unwrap(), (5, 1));
1474 }
1475
1476 #[test]
1477 fn test_compact_u16_127_boundary() {
1478 assert_eq!(encode_compact_u16(0x7F), vec![0x7F]);
1479 assert_eq!(decode_compact_u16(&[0x7F]).unwrap(), (0x7F, 1));
1480 }
1481
1482 #[test]
1483 fn test_compact_u16_128() {
1484 let encoded = encode_compact_u16(0x80);
1485 assert_eq!(encoded.len(), 2);
1486 assert_eq!(decode_compact_u16(&encoded).unwrap(), (0x80, 2));
1487 }
1488
1489 #[test]
1490 fn test_compact_u16_16383() {
1491 let encoded = encode_compact_u16(0x3FFF);
1493 assert_eq!(encoded.len(), 2);
1494 assert_eq!(decode_compact_u16(&encoded).unwrap(), (0x3FFF, 2));
1495 }
1496
1497 #[test]
1498 fn test_compact_u16_16384() {
1499 let encoded = encode_compact_u16(0x4000);
1501 assert_eq!(encoded.len(), 3);
1502 assert_eq!(decode_compact_u16(&encoded).unwrap(), (0x4000, 3));
1503 }
1504
1505 #[test]
1506 fn test_compact_u16_roundtrip_all_boundaries() {
1507 for val in [0u16, 1, 127, 128, 255, 256, 16383, 16384, 32767, 65535] {
1508 let encoded = encode_compact_u16(val);
1509 let (decoded, _) = decode_compact_u16(&encoded).unwrap();
1510 assert_eq!(decoded, val, "roundtrip failed for {val}");
1511 }
1512 }
1513
1514 #[test]
1517 fn test_system_transfer_instruction() {
1518 let from = [0xAA; 32];
1519 let to = [0xBB; 32];
1520 let ix = system_program::transfer(&from, &to, 1_000_000_000);
1521 assert_eq!(ix.program_id, system_program::ID);
1522 assert_eq!(ix.accounts.len(), 2);
1523 assert_eq!(ix.accounts[0].pubkey, from);
1524 assert!(ix.accounts[0].is_signer);
1525 assert!(!ix.accounts[1].is_signer);
1526 assert_eq!(ix.data.len(), 12);
1528 assert_eq!(&ix.data[..4], &[2, 0, 0, 0]);
1529 let lamports = u64::from_le_bytes(ix.data[4..12].try_into().unwrap());
1530 assert_eq!(lamports, 1_000_000_000);
1531 }
1532
1533 #[test]
1534 fn test_system_create_account() {
1535 let from = [0xAA; 32];
1536 let new = [0xBB; 32];
1537 let owner = [0xCC; 32];
1538 let ix = system_program::create_account(&from, &new, 1_000_000, 165, &owner);
1539 assert_eq!(ix.program_id, system_program::ID);
1540 assert_eq!(ix.accounts.len(), 2);
1541 assert_eq!(ix.data.len(), 52);
1543 }
1544
1545 #[test]
1546 fn test_system_allocate() {
1547 let account = [0xAA; 32];
1548 let ix = system_program::allocate(&account, 1024);
1549 assert_eq!(ix.data.len(), 12);
1550 }
1551
1552 #[test]
1555 fn test_spl_token_transfer() {
1556 let src = [0x11; 32];
1557 let dst = [0x22; 32];
1558 let auth = [0x33; 32];
1559 let ix = spl_token::transfer(&src, &dst, &auth, 1_000_000);
1560 assert_eq!(ix.program_id, spl_token::ID);
1561 assert_eq!(ix.accounts.len(), 3);
1562 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);
1567 }
1568
1569 #[test]
1570 fn test_spl_token_approve() {
1571 let src = [0x11; 32];
1572 let delegate = [0x22; 32];
1573 let auth = [0x33; 32];
1574 let ix = spl_token::approve(&src, &delegate, &auth, 500_000);
1575 assert_eq!(ix.data[0], 4); }
1577
1578 #[test]
1579 fn test_spl_token_mint_to() {
1580 let mint = [0x11; 32];
1581 let dst = [0x22; 32];
1582 let auth = [0x33; 32];
1583 let ix = spl_token::mint_to(&mint, &dst, &auth, 1_000);
1584 assert_eq!(ix.data[0], 7); }
1586
1587 #[test]
1590 fn test_compute_unit_limit() {
1591 let ix = compute_budget::set_compute_unit_limit(200_000);
1592 assert_eq!(ix.data[0], 2);
1593 let units = u32::from_le_bytes(ix.data[1..5].try_into().unwrap());
1594 assert_eq!(units, 200_000);
1595 assert!(ix.accounts.is_empty());
1596 }
1597
1598 #[test]
1599 fn test_compute_unit_price() {
1600 let ix = compute_budget::set_compute_unit_price(50_000);
1601 assert_eq!(ix.data[0], 3);
1602 let price = u64::from_le_bytes(ix.data[1..9].try_into().unwrap());
1603 assert_eq!(price, 50_000);
1604 }
1605
1606 #[test]
1609 fn test_message_building() {
1610 let payer = [0xAA; 32];
1611 let to = [0xBB; 32];
1612 let ix = system_program::transfer(&payer, &to, 100);
1613 let msg = Message::new(&[ix], payer);
1614
1615 assert_eq!(msg.num_required_signatures, 1);
1616 assert_eq!(msg.num_readonly_signed_accounts, 0);
1617 assert_eq!(msg.num_readonly_unsigned_accounts, 1);
1619 assert_eq!(msg.account_keys.len(), 3);
1621 assert_eq!(msg.account_keys[0], payer); }
1623
1624 #[test]
1625 fn test_message_serialization() {
1626 let payer = [0xAA; 32];
1627 let to = [0xBB; 32];
1628 let ix = system_program::transfer(&payer, &to, 100);
1629 let msg = Message::new(&[ix], payer);
1630 let bytes = msg.serialize();
1631 assert!(!bytes.is_empty());
1632 assert_eq!(bytes[0], 1); assert_eq!(bytes[1], 0); assert_eq!(bytes[2], 1); }
1637
1638 #[test]
1641 fn test_transaction_sign_and_serialize() {
1642 let signer = SolanaSigner::generate().unwrap();
1643 let payer = signer.public_key_bytes_32();
1644 let to = [0xBB; 32];
1645 let ix = system_program::transfer(&payer, &to, 1_000_000);
1646 let msg = Message::new(&[ix], payer);
1647 let blockhash = [0xCC; 32];
1648
1649 let tx = Transaction::sign(&msg, &[&signer], blockhash).unwrap();
1650 assert_eq!(tx.signatures.len(), 1);
1651 assert_eq!(tx.signatures[0].len(), 64);
1652
1653 let raw = tx.serialize();
1654 assert!(!raw.is_empty());
1655 assert_eq!(raw[0], 1);
1657 }
1658
1659 #[test]
1660 fn test_transaction_deterministic() {
1661 let signer = SolanaSigner::from_bytes(&[0x42; 32]).unwrap();
1662 let payer = signer.public_key_bytes_32();
1663 let ix = system_program::transfer(&payer, &[0xBB; 32], 100);
1664 let msg = Message::new(&[ix], payer);
1665
1666 let tx1 = Transaction::sign(&msg, &[&signer], [0; 32]).unwrap();
1667 let tx2 = Transaction::sign(&msg, &[&signer], [0; 32]).unwrap();
1668 assert_eq!(tx1.serialize(), tx2.serialize());
1669 }
1670
1671 #[test]
1674 fn test_v0_message_has_version_prefix() {
1675 let payer = [0xAA; 32];
1676 let ix = system_program::transfer(&payer, &[0xBB; 32], 100);
1677 let msg = Message::new(&[ix], payer);
1678 let v0 = MessageV0 {
1679 message: msg,
1680 address_table_lookups: vec![],
1681 };
1682 let bytes = v0.serialize();
1683 assert_eq!(bytes[0], 0x80, "v0 messages start with 0x80");
1684 }
1685
1686 #[test]
1687 fn test_v0_with_lookup_table() {
1688 let payer = [0xAA; 32];
1689 let ix = system_program::transfer(&payer, &[0xBB; 32], 100);
1690 let msg = Message::new(&[ix], payer);
1691 let v0 = MessageV0 {
1692 message: msg,
1693 address_table_lookups: vec![AddressLookupTable {
1694 account_key: [0xDD; 32],
1695 writable_indexes: vec![0, 1],
1696 readonly_indexes: vec![2],
1697 }],
1698 };
1699 let bytes = v0.serialize();
1700 assert_eq!(bytes[0], 0x80);
1701 assert!(bytes.len() > 100); }
1703
1704 #[test]
1707 fn test_find_program_address() {
1708 let program_id = [0xAA; 32];
1709 let (pda, bump) = find_program_address(&[b"test_seed"], &program_id).unwrap();
1710 assert_eq!(pda.len(), 32);
1711 assert_ne!(bump, 0, "bump should be valid");
1712 let (pda2, bump2) = find_program_address(&[b"test_seed"], &program_id).unwrap();
1714 assert_eq!(pda, pda2);
1715 assert_eq!(bump, bump2);
1716 }
1717
1718 #[test]
1719 fn test_pda_different_seeds() {
1720 let program_id = [0xBB; 32];
1721 let (pda1, _) = find_program_address(&[b"seed_a"], &program_id).unwrap();
1722 let (pda2, _) = find_program_address(&[b"seed_b"], &program_id).unwrap();
1723 assert_ne!(pda1, pda2);
1724 }
1725
1726 #[test]
1727 fn test_pda_seed_too_long() {
1728 let program_id = [0xCC; 32];
1729 let long_seed = [0u8; 33]; let result = create_program_address(&[&long_seed[..]], &[0], &program_id);
1731 assert!(result.is_err());
1732 }
1733
1734 #[test]
1737 fn test_message_serialize_deserialize_roundtrip() {
1738 let payer = [0xAA; 32];
1739 let to = [0xBB; 32];
1740 let ix = system_program::transfer(&payer, &to, 1_000_000);
1741 let msg = Message::new(&[ix], payer);
1742 let serialized = msg.serialize();
1743
1744 let restored = Message::deserialize(&serialized).unwrap();
1745 assert_eq!(
1746 restored.num_required_signatures,
1747 msg.num_required_signatures
1748 );
1749 assert_eq!(restored.account_keys.len(), msg.account_keys.len());
1750 assert_eq!(restored.instructions.len(), msg.instructions.len());
1751 assert_eq!(restored.instructions[0].data, msg.instructions[0].data);
1752 }
1753
1754 #[test]
1755 fn test_transaction_serialize_deserialize_roundtrip() {
1756 let signer = SolanaSigner::from_bytes(&[0x42; 32]).unwrap();
1757 let payer = signer.public_key_bytes_32();
1758 let ix = system_program::transfer(&payer, &[0xBB; 32], 100);
1759 let msg = Message::new(&[ix], payer);
1760 let tx = Transaction::sign(&msg, &[&signer], [0xDD; 32]).unwrap();
1761
1762 let raw = tx.serialize();
1763 let restored = Transaction::deserialize(&raw).unwrap();
1764
1765 assert_eq!(restored.signatures.len(), tx.signatures.len());
1766 assert_eq!(restored.signatures[0], tx.signatures[0]);
1767 assert_eq!(
1768 restored.message.account_keys.len(),
1769 tx.message.account_keys.len()
1770 );
1771 }
1772
1773 #[test]
1774 fn test_deserialize_empty_fails() {
1775 assert!(Transaction::deserialize(&[]).is_err());
1776 assert!(Message::deserialize(&[]).is_err());
1777 assert!(Message::deserialize(&[0, 0]).is_err()); }
1779
1780 #[test]
1783 fn test_instruction_data_builder_reader_roundtrip() {
1784 let data = InstructionDataBuilder::new()
1785 .write_u8(42)
1786 .write_u16(1000)
1787 .write_u32(100_000)
1788 .write_u64(1_000_000_000)
1789 .write_bool(true)
1790 .write_pubkey(&[0xAA; 32])
1791 .write_string("hello solana")
1792 .build();
1793
1794 let mut reader = InstructionDataReader::new(&data);
1795 assert_eq!(reader.read_u8().unwrap(), 42);
1796 assert_eq!(reader.read_u16().unwrap(), 1000);
1797 assert_eq!(reader.read_u32().unwrap(), 100_000);
1798 assert_eq!(reader.read_u64().unwrap(), 1_000_000_000);
1799 assert!(reader.read_bool().unwrap());
1800 assert_eq!(reader.read_pubkey().unwrap(), [0xAA; 32]);
1801 assert_eq!(reader.read_string().unwrap(), "hello solana");
1802 assert!(reader.is_empty());
1803 }
1804
1805 #[test]
1806 fn test_instruction_data_builder_option() {
1807 let none_data = InstructionDataBuilder::new().write_option(None).build();
1808 assert_eq!(none_data, vec![0]);
1809
1810 let some_data = InstructionDataBuilder::new()
1811 .write_option(Some(&[1, 2, 3]))
1812 .build();
1813 assert_eq!(some_data, vec![1, 1, 2, 3]);
1814 }
1815
1816 #[test]
1817 fn test_reader_eof_errors() {
1818 let mut reader = InstructionDataReader::new(&[]);
1819 assert!(reader.read_u8().is_err());
1820 assert!(reader.read_u64().is_err());
1821 assert!(reader.read_pubkey().is_err());
1822 }
1823
1824 #[test]
1827 fn test_spl_token_burn() {
1828 let account = [0x11; 32];
1829 let mint = [0x22; 32];
1830 let auth = [0x33; 32];
1831 let ix = spl_token::burn(&account, &mint, &auth, 500);
1832 assert_eq!(ix.data[0], 8);
1833 assert_eq!(ix.accounts.len(), 3);
1834 }
1835
1836 #[test]
1837 fn test_spl_token_close_account() {
1838 let ix = spl_token::close_account(&[0x11; 32], &[0x22; 32], &[0x33; 32]);
1839 assert_eq!(ix.data, vec![9]);
1840 }
1841
1842 #[test]
1843 fn test_spl_token_freeze_thaw() {
1844 let ix_freeze = spl_token::freeze_account(&[0x11; 32], &[0x22; 32], &[0x33; 32]);
1845 assert_eq!(ix_freeze.data, vec![10]);
1846 let ix_thaw = spl_token::thaw_account(&[0x11; 32], &[0x22; 32], &[0x33; 32]);
1847 assert_eq!(ix_thaw.data, vec![11]);
1848 }
1849
1850 #[test]
1851 fn test_spl_token_initialize_mint() {
1852 let mint = [0x11; 32];
1853 let auth = [0x22; 32];
1854 let ix = spl_token::initialize_mint(&mint, 6, &auth, Some(&[0x33; 32]));
1855 assert_eq!(ix.data[0], 0); assert_eq!(ix.data[1], 6); }
1858
1859 #[test]
1860 fn test_spl_token_set_authority() {
1861 let ix = spl_token::set_authority(
1862 &[0x11; 32],
1863 &[0x22; 32],
1864 spl_token::AuthorityType::MintTokens,
1865 Some(&[0x33; 32]),
1866 );
1867 assert_eq!(ix.data[0], 6); assert_eq!(ix.data[1], 0); assert_eq!(ix.data[2], 1); }
1871
1872 #[test]
1873 fn test_spl_token_revoke() {
1874 let ix = spl_token::revoke(&[0x11; 32], &[0x22; 32]);
1875 assert_eq!(ix.data, vec![5]);
1876 }
1877
1878 #[test]
1879 fn test_spl_token_transfer_checked() {
1880 let ix = spl_token::transfer_checked(
1881 &[0x11; 32],
1882 &[0x22; 32],
1883 &[0x33; 32],
1884 &[0x44; 32],
1885 1000,
1886 6,
1887 );
1888 assert_eq!(ix.data[0], 12); assert_eq!(ix.accounts.len(), 4); }
1891
1892 #[test]
1895 fn test_token_2022_transfer_checked() {
1896 let ix = spl_token_2022::transfer_checked(
1897 &[0x11; 32],
1898 &[0x22; 32],
1899 &[0x33; 32],
1900 &[0x44; 32],
1901 1000,
1902 9,
1903 );
1904 assert_eq!(ix.program_id, spl_token_2022::ID);
1905 assert_eq!(ix.data[0], 12);
1906 }
1907
1908 #[test]
1909 fn test_token_2022_transfer_checked_with_fee() {
1910 let ix = spl_token_2022::transfer_checked_with_fee(
1911 &[0x11; 32],
1912 &[0x22; 32],
1913 &[0x33; 32],
1914 &[0x44; 32],
1915 1000,
1916 9,
1917 50,
1918 );
1919 assert_eq!(ix.data[0], 26); let fee = u64::from_le_bytes(ix.data[10..18].try_into().unwrap());
1921 assert_eq!(fee, 50);
1922 }
1923
1924 #[test]
1927 fn test_versioned_transaction_sign() {
1928 let signer = SolanaSigner::from_bytes(&[0x42; 32]).unwrap();
1929 let payer = signer.public_key_bytes_32();
1930 let ix = system_program::transfer(&payer, &[0xBB; 32], 100);
1931 let msg = Message::new(&[ix], payer);
1932 let v0 = MessageV0 {
1933 message: msg,
1934 address_table_lookups: vec![],
1935 };
1936
1937 let vtx = VersionedTransaction::sign(&v0, &[&signer], [0xCC; 32]).unwrap();
1938 assert_eq!(vtx.signatures.len(), 1);
1939 let raw = vtx.serialize();
1940 assert!(!raw.is_empty());
1941 }
1942
1943 #[test]
1944 fn test_versioned_transaction_deterministic() {
1945 let signer = SolanaSigner::from_bytes(&[0x42; 32]).unwrap();
1946 let payer = signer.public_key_bytes_32();
1947 let ix = system_program::transfer(&payer, &[0xBB; 32], 100);
1948 let msg = Message::new(&[ix], payer);
1949 let v0 = MessageV0 {
1950 message: msg,
1951 address_table_lookups: vec![],
1952 };
1953
1954 let vtx1 = VersionedTransaction::sign(&v0, &[&signer], [0; 32]).unwrap();
1955 let vtx2 = VersionedTransaction::sign(&v0, &[&signer], [0; 32]).unwrap();
1956 assert_eq!(vtx1.serialize(), vtx2.serialize());
1957 }
1958}