1use super::transaction::{AccountMeta, Instruction};
4
5pub const ATA_PROGRAM_ID: [u8; 32] = [
11 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, 13, 131, 11, 90, 19, 153, 218,
12 255, 16, 132, 4, 142, 123, 216, 219, 233, 248, 89,
13];
14
15pub const SPL_TOKEN_PROGRAM_ID: [u8; 32] = [
17 6, 221, 246, 225, 215, 101, 161, 147, 217, 203, 225, 70, 206, 235, 121, 172, 28, 180, 133, 237,
18 95, 91, 55, 145, 58, 140, 245, 133, 126, 255, 0, 169,
19];
20
21const SYSTEM_PROGRAM_ID: [u8; 32] = [0; 32];
23
24pub fn derive_ata_address(
30 wallet: &[u8; 32],
31 mint: &[u8; 32],
32) -> Result<[u8; 32], crate::error::SignerError> {
33 use crate::solana::transaction::find_program_address;
34 let seeds: &[&[u8]] = &[wallet, &SPL_TOKEN_PROGRAM_ID, mint];
35 let (addr, _bump) = find_program_address(seeds, &ATA_PROGRAM_ID)?;
36 Ok(addr)
37}
38
39pub fn create_ata(
44 payer: [u8; 32],
45 wallet: [u8; 32],
46 mint: [u8; 32],
47) -> Result<Instruction, crate::error::SignerError> {
48 let ata = derive_ata_address(&wallet, &mint)?;
49
50 Ok(Instruction {
51 program_id: ATA_PROGRAM_ID,
52 accounts: vec![
53 AccountMeta::new(payer, true), AccountMeta::new(ata, false), AccountMeta::new_readonly(wallet, false), AccountMeta::new_readonly(mint, false), AccountMeta::new_readonly(SYSTEM_PROGRAM_ID, false), AccountMeta::new_readonly(SPL_TOKEN_PROGRAM_ID, false), ],
60 data: vec![0], })
62}
63
64pub fn create_ata_idempotent(
68 payer: [u8; 32],
69 wallet: [u8; 32],
70 mint: [u8; 32],
71) -> Result<Instruction, crate::error::SignerError> {
72 let ata = derive_ata_address(&wallet, &mint)?;
73
74 Ok(Instruction {
75 program_id: ATA_PROGRAM_ID,
76 accounts: vec![
77 AccountMeta::new(payer, true),
78 AccountMeta::new(ata, false),
79 AccountMeta::new_readonly(wallet, false),
80 AccountMeta::new_readonly(mint, false),
81 AccountMeta::new_readonly(SYSTEM_PROGRAM_ID, false),
82 AccountMeta::new_readonly(SPL_TOKEN_PROGRAM_ID, false),
83 ],
84 data: vec![1], })
86}
87
88pub const MEMO_PROGRAM_ID: [u8; 32] = [
94 0x05, 0x4A, 0x53, 0x5A, 0x99, 0x29, 0x21, 0x06, 0x4D, 0x24, 0xE8, 0x71, 0x60, 0xDA, 0x38, 0x7C,
95 0x7C, 0x35, 0xB5, 0xDD, 0xBC, 0x92, 0xBB, 0x81, 0xE4, 0x1F, 0xA8, 0x40, 0x41, 0x05, 0x44, 0x8D,
96];
97
98pub fn memo(memo_text: &str, signers: &[[u8; 32]]) -> Instruction {
104 let accounts: Vec<AccountMeta> = signers
105 .iter()
106 .map(|pk| AccountMeta::new_readonly(*pk, true))
107 .collect();
108
109 Instruction {
110 program_id: MEMO_PROGRAM_ID,
111 accounts,
112 data: memo_text.as_bytes().to_vec(),
113 }
114}
115
116pub fn memo_unsigned(memo_text: &str) -> Instruction {
118 Instruction {
119 program_id: MEMO_PROGRAM_ID,
120 accounts: vec![],
121 data: memo_text.as_bytes().to_vec(),
122 }
123}
124
125pub const STAKE_PROGRAM_ID: [u8; 32] = [
131 0x06, 0xA1, 0xD8, 0x17, 0x91, 0x37, 0x54, 0x2A, 0x98, 0x34, 0x37, 0xBD, 0xFE, 0x2A, 0x7A, 0xB2,
132 0x55, 0x7F, 0x53, 0x5C, 0x8A, 0x78, 0x72, 0x2B, 0x68, 0xA4, 0x9D, 0xC0, 0x00, 0x00, 0x00, 0x00,
133];
134
135pub const STAKE_CONFIG_ID: [u8; 32] = [
137 0x06, 0xA1, 0xD8, 0x17, 0xA5, 0x02, 0x05, 0x0B, 0x68, 0x07, 0x91, 0xE6, 0xCE, 0x6D, 0xB8, 0x8E,
138 0x1E, 0x5B, 0x71, 0x50, 0xF6, 0x1F, 0xC6, 0x79, 0x0A, 0x4E, 0xB4, 0xD1, 0x00, 0x00, 0x00, 0x00,
139];
140
141pub const CLOCK_SYSVAR: [u8; 32] = [
143 0x06, 0xA7, 0xD5, 0x17, 0x18, 0xC7, 0x74, 0xC9, 0x28, 0x56, 0x63, 0x98, 0x69, 0x1D, 0x5E, 0xB6,
144 0x8B, 0x5E, 0xB8, 0xA3, 0x9B, 0x4B, 0x6D, 0x5C, 0x73, 0x55, 0x5B, 0x21, 0x00, 0x00, 0x00, 0x00,
145];
146
147pub const STAKE_HISTORY_SYSVAR: [u8; 32] = [
149 0x06, 0xA7, 0xD5, 0x17, 0x19, 0x35, 0x84, 0xD0, 0xFE, 0xED, 0x9B, 0xB3, 0x43, 0x1D, 0x13, 0x20,
150 0x6B, 0xE5, 0x44, 0x28, 0x1B, 0x57, 0xB8, 0x56, 0x6C, 0xC5, 0x37, 0x5F, 0xF4, 0x00, 0x00, 0x00,
151];
152
153pub fn stake_delegate(
157 stake_account: [u8; 32],
158 vote_account: [u8; 32],
159 stake_authority: [u8; 32],
160) -> Instruction {
161 let mut data = vec![0u8; 4]; data[0] = 2;
163
164 Instruction {
165 program_id: STAKE_PROGRAM_ID,
166 accounts: vec![
167 AccountMeta::new(stake_account, false), AccountMeta::new_readonly(vote_account, false), AccountMeta::new_readonly(CLOCK_SYSVAR, false),
170 AccountMeta::new_readonly(STAKE_HISTORY_SYSVAR, false),
171 AccountMeta::new_readonly(STAKE_CONFIG_ID, false),
172 AccountMeta::new_readonly(stake_authority, true), ],
174 data,
175 }
176}
177
178pub fn stake_deactivate(stake_account: [u8; 32], stake_authority: [u8; 32]) -> Instruction {
182 let mut data = vec![0u8; 4];
183 data[0] = 5; Instruction {
186 program_id: STAKE_PROGRAM_ID,
187 accounts: vec![
188 AccountMeta::new(stake_account, false),
189 AccountMeta::new_readonly(CLOCK_SYSVAR, false),
190 AccountMeta::new_readonly(stake_authority, true),
191 ],
192 data,
193 }
194}
195
196pub fn stake_withdraw(
200 stake_account: [u8; 32],
201 withdraw_authority: [u8; 32],
202 recipient: [u8; 32],
203 lamports: u64,
204) -> Instruction {
205 let mut data = vec![0u8; 12];
206 data[0] = 4; data[4..12].copy_from_slice(&lamports.to_le_bytes());
208
209 Instruction {
210 program_id: STAKE_PROGRAM_ID,
211 accounts: vec![
212 AccountMeta::new(stake_account, false),
213 AccountMeta::new(recipient, false),
214 AccountMeta::new_readonly(CLOCK_SYSVAR, false),
215 AccountMeta::new_readonly(STAKE_HISTORY_SYSVAR, false),
216 AccountMeta::new_readonly(withdraw_authority, true),
217 ],
218 data,
219 }
220}
221
222pub fn advance_nonce(nonce_account: [u8; 32], nonce_authority: [u8; 32]) -> Instruction {
231 let recent_blockhashes_sysvar: [u8; 32] = [
233 0x06, 0xA7, 0xD5, 0x17, 0x19, 0x2C, 0x56, 0x8E, 0xE0, 0x8A, 0x84, 0x5F, 0x73, 0xD2, 0x97,
234 0x88, 0xCF, 0x03, 0x5C, 0x31, 0x45, 0xB2, 0x1A, 0xB3, 0x44, 0xD8, 0x06, 0x2E, 0xA9, 0x40,
235 0x00, 0x00,
236 ];
237
238 let mut data = vec![0u8; 4];
239 data[0] = 4; Instruction {
242 program_id: SYSTEM_PROGRAM_ID,
243 accounts: vec![
244 AccountMeta::new(nonce_account, false),
245 AccountMeta::new_readonly(recent_blockhashes_sysvar, false),
246 AccountMeta::new_readonly(nonce_authority, true),
247 ],
248 data,
249 }
250}
251
252pub fn initialize_nonce(nonce_account: [u8; 32], nonce_authority: [u8; 32]) -> Instruction {
256 let recent_blockhashes_sysvar: [u8; 32] = [
258 0x06, 0xA7, 0xD5, 0x17, 0x19, 0x2C, 0x56, 0x8E, 0xE0, 0x8A, 0x84, 0x5F, 0x73, 0xD2, 0x97,
259 0x88, 0xCF, 0x03, 0x5C, 0x31, 0x45, 0xB2, 0x1A, 0xB3, 0x44, 0xD8, 0x06, 0x2E, 0xA9, 0x40,
260 0x00, 0x00,
261 ];
262 let rent_sysvar: [u8; 32] = [
264 0x06, 0xA7, 0xD5, 0x17, 0x19, 0x2C, 0x5C, 0x51, 0x21, 0x8C, 0xC9, 0x4C, 0x3D, 0x4A, 0xF1,
265 0x7F, 0x58, 0xDA, 0xEE, 0x08, 0x9B, 0xA1, 0xFD, 0x44, 0xE3, 0xDB, 0xD9, 0x8A, 0x00, 0x00,
266 0x00, 0x00,
267 ];
268
269 let mut data = vec![0u8; 36];
270 data[0] = 6; data[4..36].copy_from_slice(&nonce_authority);
272
273 Instruction {
274 program_id: SYSTEM_PROGRAM_ID,
275 accounts: vec![
276 AccountMeta::new(nonce_account, false),
277 AccountMeta::new_readonly(recent_blockhashes_sysvar, false),
278 AccountMeta::new_readonly(rent_sysvar, false),
279 ],
280 data,
281 }
282}
283
284pub mod address_lookup_table {
290 use super::*;
291
292 pub const ID: [u8; 32] = [
294 0x02, 0x77, 0xA6, 0xAF, 0x97, 0x33, 0x9B, 0x7A, 0xC8, 0x8D, 0x18, 0x92, 0xC9, 0x04, 0x46,
295 0xF5, 0x00, 0x02, 0x30, 0x92, 0x66, 0xF6, 0x2E, 0x53, 0xC1, 0x18, 0x24, 0x49, 0x82, 0x00,
296 0x00, 0x00,
297 ];
298
299 #[must_use]
307 pub fn create(
308 authority: [u8; 32],
309 payer: [u8; 32],
310 lookup_table: [u8; 32],
311 recent_slot: u64,
312 ) -> Instruction {
313 let mut data = vec![0u8; 4]; data.extend_from_slice(&recent_slot.to_le_bytes());
315
316 Instruction {
317 program_id: ID,
318 accounts: vec![
319 AccountMeta::new(lookup_table, false),
320 AccountMeta::new_readonly(authority, true),
321 AccountMeta::new(payer, true),
322 AccountMeta::new_readonly(SYSTEM_PROGRAM_ID, false),
323 ],
324 data,
325 }
326 }
327
328 #[must_use]
330 pub fn extend(
331 lookup_table: [u8; 32],
332 authority: [u8; 32],
333 payer: [u8; 32],
334 new_addresses: &[[u8; 32]],
335 ) -> Instruction {
336 let mut data = vec![0u8; 4];
337 data[0] = 2; data.extend_from_slice(&(new_addresses.len() as u32).to_le_bytes());
340 for addr in new_addresses {
341 data.extend_from_slice(addr);
342 }
343
344 Instruction {
345 program_id: ID,
346 accounts: vec![
347 AccountMeta::new(lookup_table, false),
348 AccountMeta::new_readonly(authority, true),
349 AccountMeta::new(payer, true),
350 AccountMeta::new_readonly(SYSTEM_PROGRAM_ID, false),
351 ],
352 data,
353 }
354 }
355
356 #[must_use]
360 pub fn deactivate(lookup_table: [u8; 32], authority: [u8; 32]) -> Instruction {
361 let mut data = vec![0u8; 4];
362 data[0] = 3; Instruction {
365 program_id: ID,
366 accounts: vec![
367 AccountMeta::new(lookup_table, false),
368 AccountMeta::new_readonly(authority, true),
369 ],
370 data,
371 }
372 }
373
374 #[must_use]
376 pub fn close(lookup_table: [u8; 32], authority: [u8; 32], recipient: [u8; 32]) -> Instruction {
377 let mut data = vec![0u8; 4];
378 data[0] = 4; Instruction {
381 program_id: ID,
382 accounts: vec![
383 AccountMeta::new(lookup_table, false),
384 AccountMeta::new_readonly(authority, true),
385 AccountMeta::new(recipient, false),
386 ],
387 data,
388 }
389 }
390}
391
392pub mod token_metadata {
398 use super::*;
399
400 pub const ID: [u8; 32] = [
402 0x0B, 0x70, 0x65, 0xB1, 0xE3, 0xD1, 0x7C, 0x45, 0x38, 0x9D, 0x52, 0x7F, 0x6B, 0x04, 0xC3,
403 0xCD, 0x58, 0xB8, 0x6C, 0x73, 0x1A, 0xA0, 0xFD, 0xB5, 0x49, 0xB6, 0xD1, 0xBC, 0x03, 0xF8,
404 0x29, 0x46,
405 ];
406
407 #[derive(Debug, Clone)]
409 pub struct DataV2 {
410 pub name: String,
412 pub symbol: String,
414 pub uri: String,
416 pub seller_fee_basis_points: u16,
418 pub creators: Option<Vec<Creator>>,
420 }
421
422 #[derive(Debug, Clone)]
424 pub struct Creator {
425 pub address: [u8; 32],
427 pub verified: bool,
429 pub share: u8,
431 }
432
433 pub fn derive_metadata_address(mint: &[u8; 32]) -> Result<[u8; 32], crate::error::SignerError> {
437 use crate::solana::transaction::find_program_address;
438 let seeds: &[&[u8]] = &[b"metadata", &ID, mint];
439 let (addr, _bump) = find_program_address(seeds, &ID)?;
440 Ok(addr)
441 }
442
443 fn borsh_string(s: &str) -> Vec<u8> {
445 let bytes = s.as_bytes();
446 let mut out = Vec::with_capacity(4 + bytes.len());
447 out.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
448 out.extend_from_slice(bytes);
449 out
450 }
451
452 fn serialize_data_v2(data: &DataV2) -> Vec<u8> {
454 let mut buf = Vec::new();
455 buf.extend(borsh_string(&data.name));
456 buf.extend(borsh_string(&data.symbol));
457 buf.extend(borsh_string(&data.uri));
458 buf.extend_from_slice(&data.seller_fee_basis_points.to_le_bytes());
459
460 match &data.creators {
462 None => buf.push(0),
463 Some(creators) => {
464 buf.push(1);
465 buf.extend_from_slice(&(creators.len() as u32).to_le_bytes());
466 for c in creators {
467 buf.extend_from_slice(&c.address);
468 buf.push(u8::from(c.verified));
469 buf.push(c.share);
470 }
471 }
472 }
473 buf
474 }
475
476 pub fn create_metadata_v3(
487 metadata: [u8; 32],
488 mint: [u8; 32],
489 mint_authority: [u8; 32],
490 payer: [u8; 32],
491 update_authority: [u8; 32],
492 data: &DataV2,
493 is_mutable: bool,
494 ) -> Instruction {
495 let mut ix_data = vec![33];
497 ix_data.extend(serialize_data_v2(data));
498 ix_data.push(u8::from(is_mutable));
499 ix_data.push(0);
501
502 Instruction {
503 program_id: ID,
504 accounts: vec![
505 AccountMeta::new(metadata, false),
506 AccountMeta::new_readonly(mint, false),
507 AccountMeta::new_readonly(mint_authority, true),
508 AccountMeta::new(payer, true),
509 AccountMeta::new_readonly(update_authority, false),
510 AccountMeta::new_readonly(SYSTEM_PROGRAM_ID, false),
511 ],
512 data: ix_data,
513 }
514 }
515
516 pub fn update_metadata_v2(
526 metadata: [u8; 32],
527 update_authority: [u8; 32],
528 new_data: Option<&DataV2>,
529 new_update_authority: Option<&[u8; 32]>,
530 primary_sale_happened: Option<bool>,
531 is_mutable: Option<bool>,
532 ) -> Instruction {
533 let mut ix_data = vec![15];
535
536 match new_data {
538 None => ix_data.push(0),
539 Some(d) => {
540 ix_data.push(1);
541 ix_data.extend(serialize_data_v2(d));
542 }
543 }
544
545 match new_update_authority {
547 None => ix_data.push(0),
548 Some(auth) => {
549 ix_data.push(1);
550 ix_data.extend_from_slice(auth);
551 }
552 }
553
554 match primary_sale_happened {
556 None => ix_data.push(0),
557 Some(val) => {
558 ix_data.push(1);
559 ix_data.push(u8::from(val));
560 }
561 }
562
563 match is_mutable {
565 None => ix_data.push(0),
566 Some(val) => {
567 ix_data.push(1);
568 ix_data.push(u8::from(val));
569 }
570 }
571
572 Instruction {
573 program_id: ID,
574 accounts: vec![
575 AccountMeta::new(metadata, false),
576 AccountMeta::new_readonly(update_authority, true),
577 ],
578 data: ix_data,
579 }
580 }
581}
582
583#[cfg(test)]
588#[allow(clippy::unwrap_used, clippy::expect_used)]
589mod tests {
590 use super::*;
591
592 const WALLET: [u8; 32] = [1; 32];
593 const MINT: [u8; 32] = [2; 32];
594 const PAYER: [u8; 32] = [3; 32];
595
596 #[test]
599 fn test_derive_ata_deterministic() {
600 let ata1 = derive_ata_address(&WALLET, &MINT).unwrap();
601 let ata2 = derive_ata_address(&WALLET, &MINT).unwrap();
602 assert_eq!(ata1, ata2);
603 }
604
605 #[test]
606 fn test_derive_ata_different_mints() {
607 let mint2 = [3; 32];
608 let ata1 = derive_ata_address(&WALLET, &MINT).unwrap();
609 let ata2 = derive_ata_address(&WALLET, &mint2).unwrap();
610 assert_ne!(ata1, ata2);
611 }
612
613 #[test]
614 fn test_create_ata_instruction() {
615 let ix = create_ata(PAYER, WALLET, MINT).unwrap();
616 assert_eq!(ix.program_id, ATA_PROGRAM_ID);
617 assert_eq!(ix.accounts.len(), 6);
618 assert_eq!(ix.data, vec![0]); assert!(ix.accounts[0].is_signer); }
621
622 #[test]
623 fn test_create_ata_idempotent() {
624 let ix = create_ata_idempotent(PAYER, WALLET, MINT).unwrap();
625 assert_eq!(ix.data, vec![1]); }
627
628 #[test]
631 fn test_memo_basic() {
632 let ix = memo("Hello, Solana!", &[WALLET]);
633 assert_eq!(ix.program_id, MEMO_PROGRAM_ID);
634 assert_eq!(ix.data, b"Hello, Solana!");
635 assert_eq!(ix.accounts.len(), 1);
636 assert!(ix.accounts[0].is_signer);
637 }
638
639 #[test]
640 fn test_memo_unsigned() {
641 let ix = memo_unsigned("test memo");
642 assert!(ix.accounts.is_empty());
643 assert_eq!(ix.data, b"test memo");
644 }
645
646 #[test]
647 fn test_memo_multiple_signers() {
648 let signer2 = [4; 32];
649 let ix = memo("multi-signer memo", &[WALLET, signer2]);
650 assert_eq!(ix.accounts.len(), 2);
651 }
652
653 #[test]
656 fn test_stake_delegate() {
657 let vote = [5; 32];
658 let ix = stake_delegate(WALLET, vote, PAYER);
659 assert_eq!(ix.program_id, STAKE_PROGRAM_ID);
660 assert_eq!(ix.accounts.len(), 6);
661 assert_eq!(ix.data[0], 2); }
663
664 #[test]
665 fn test_stake_deactivate() {
666 let ix = stake_deactivate(WALLET, PAYER);
667 assert_eq!(ix.accounts.len(), 3);
668 assert_eq!(ix.data[0], 5); }
670
671 #[test]
672 fn test_stake_withdraw() {
673 let recipient = [6; 32];
674 let ix = stake_withdraw(WALLET, PAYER, recipient, 1_000_000_000);
675 assert_eq!(ix.data[0], 4); let lamports = u64::from_le_bytes(ix.data[4..12].try_into().unwrap());
677 assert_eq!(lamports, 1_000_000_000);
678 }
679
680 #[test]
683 fn test_advance_nonce() {
684 let nonce_account = [7; 32];
685 let ix = advance_nonce(nonce_account, PAYER);
686 assert_eq!(ix.program_id, SYSTEM_PROGRAM_ID);
687 assert_eq!(ix.accounts.len(), 3);
688 assert_eq!(ix.data[0], 4); }
690
691 #[test]
692 fn test_initialize_nonce() {
693 let nonce_account = [8; 32];
694 let ix = initialize_nonce(nonce_account, PAYER);
695 assert_eq!(ix.data[0], 6); assert_eq!(&ix.data[4..36], &PAYER); }
698
699 #[test]
702 fn test_alt_create() {
703 let table = [9; 32];
704 let ix = address_lookup_table::create(PAYER, PAYER, table, 12345);
705 assert_eq!(ix.program_id, address_lookup_table::ID);
706 assert_eq!(ix.accounts.len(), 4);
707 assert_eq!(ix.data[0], 0); let slot = u64::from_le_bytes(ix.data[4..12].try_into().unwrap());
709 assert_eq!(slot, 12345);
710 }
711
712 #[test]
713 fn test_alt_extend() {
714 let table = [9; 32];
715 let addrs = [[10u8; 32], [11u8; 32]];
716 let ix = address_lookup_table::extend(table, PAYER, PAYER, &addrs);
717 assert_eq!(ix.data[0], 2); let count = u32::from_le_bytes(ix.data[4..8].try_into().unwrap());
719 assert_eq!(count, 2);
720 assert_eq!(&ix.data[8..40], &addrs[0]);
721 assert_eq!(&ix.data[40..72], &addrs[1]);
722 }
723
724 #[test]
725 fn test_alt_deactivate() {
726 let table = [9; 32];
727 let ix = address_lookup_table::deactivate(table, PAYER);
728 assert_eq!(ix.data[0], 3);
729 assert_eq!(ix.accounts.len(), 2);
730 }
731
732 #[test]
733 fn test_alt_close() {
734 let table = [9; 32];
735 let recipient = [10; 32];
736 let ix = address_lookup_table::close(table, PAYER, recipient);
737 assert_eq!(ix.data[0], 4);
738 assert_eq!(ix.accounts.len(), 3);
739 }
740
741 #[test]
744 fn test_derive_metadata_deterministic() {
745 let addr1 = token_metadata::derive_metadata_address(&MINT).unwrap();
746 let addr2 = token_metadata::derive_metadata_address(&MINT).unwrap();
747 assert_eq!(addr1, addr2);
748 }
749
750 #[test]
751 fn test_create_metadata_v3() {
752 let metadata = [12; 32];
753 let update_auth = [13; 32];
754 let data = token_metadata::DataV2 {
755 name: "My NFT".to_string(),
756 symbol: "MNFT".to_string(),
757 uri: "https://example.com/meta.json".to_string(),
758 seller_fee_basis_points: 500,
759 creators: Some(vec![token_metadata::Creator {
760 address: PAYER,
761 verified: true,
762 share: 100,
763 }]),
764 };
765 let ix = token_metadata::create_metadata_v3(
766 metadata,
767 MINT,
768 PAYER,
769 PAYER,
770 update_auth,
771 &data,
772 true,
773 );
774 assert_eq!(ix.program_id, token_metadata::ID);
775 assert_eq!(ix.accounts.len(), 6);
776 assert_eq!(ix.data[0], 33); }
778
779 #[test]
780 fn test_update_metadata_v2() {
781 let metadata = [12; 32];
782 let ix = token_metadata::update_metadata_v2(metadata, PAYER, None, None, Some(true), None);
783 assert_eq!(ix.data[0], 15); assert_eq!(ix.accounts.len(), 2);
785 }
786
787 #[test]
788 fn test_metadata_without_creators() {
789 let metadata = [12; 32];
790 let update_auth = [13; 32];
791 let data = token_metadata::DataV2 {
792 name: "Token".to_string(),
793 symbol: "TKN".to_string(),
794 uri: "https://example.com".to_string(),
795 seller_fee_basis_points: 0,
796 creators: None,
797 };
798 let ix = token_metadata::create_metadata_v3(
799 metadata,
800 MINT,
801 PAYER,
802 PAYER,
803 update_auth,
804 &data,
805 false,
806 );
807 assert_eq!(ix.data[0], 33);
808 let last_data = ix.data.last().unwrap();
810 assert_eq!(*last_data, 0);
812 }
813}