1use crate::{deser::meta_deser, error::MetadataError, utils::try_from_slice_checked};
2use borsh::{BorshDeserialize, BorshSerialize};
3use shank::ShankAccount;
4use solana_program::{
5 account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError,
6 pubkey::Pubkey,
7};
8pub const PREFIX: &str = "metadata";
10
11pub const EDITION: &str = "edition";
13
14pub const RESERVATION: &str = "reservation";
15
16pub const USER: &str = "user";
17
18pub const BURN: &str = "burn";
19
20pub const COLLECTION_AUTHORITY: &str = "collection_authority";
21
22pub const MAX_NAME_LENGTH: usize = 32;
23
24pub const MAX_SYMBOL_LENGTH: usize = 10;
25
26pub const MAX_URI_LENGTH: usize = 5000;
27
28pub const MAX_METADATA_LEN: usize = 1 + 32 + 32 + MAX_DATA_SIZE
32+ 1 + 1 + 9 + 2 + 34 + 18 + 118; pub const MAX_DATA_SIZE: usize = 4
41 + MAX_NAME_LENGTH
42 + 4
43 + MAX_SYMBOL_LENGTH
44 + 4
45 + MAX_URI_LENGTH
46 + 2
47 + 1
48 + 4
49 + MAX_CREATOR_LIMIT * MAX_CREATOR_LEN;
50
51pub const MAX_EDITION_LEN: usize = 1 + 32 + 8 + 200;
52
53pub const MAX_MASTER_EDITION_LEN: usize = 1 + 9 + 8 + 264;
57
58pub const MAX_CREATOR_LIMIT: usize = 5;
59
60pub const MAX_CREATOR_LEN: usize = 32 + 1 + 1;
61
62pub const MAX_RESERVATIONS: usize = 200;
63
64pub const MAX_RESERVATION_LIST_V1_SIZE: usize = 1 + 32 + 8 + 8 + MAX_RESERVATIONS * 34 + 100;
66
67pub const MAX_RESERVATION_LIST_SIZE: usize = 1 + 32 + 8 + 8 + MAX_RESERVATIONS * 48 + 8 + 8 + 84;
69
70pub const MAX_EDITION_MARKER_SIZE: usize = 32;
71
72pub const EDITION_MARKER_BIT_SIZE: u64 = 248;
73
74pub const USE_AUTHORITY_RECORD_SIZE: usize = 18; pub const COLLECTION_AUTHORITY_RECORD_SIZE: usize = 11; #[repr(C)]
79#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone, Copy)]
80pub enum Key {
81 Uninitialized,
82 EditionV1,
83 MasterEditionV1,
84 ReservationListV1,
85 MetadataV1,
86 ReservationListV2,
87 MasterEditionV2,
88 EditionMarker,
89 UseAuthorityRecord,
90 CollectionAuthorityRecord,
91}
92#[repr(C)]
93#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
94pub struct Data {
95 pub name: String,
97 pub symbol: String,
99 pub uri: String,
101 pub seller_fee_basis_points: u16,
103 pub creators: Option<Vec<Creator>>,
105}
106
107#[repr(C)]
108#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
109pub struct DataV2 {
110 pub name: String,
112 pub symbol: String,
114 pub uri: String,
116 pub seller_fee_basis_points: u16,
118 pub creators: Option<Vec<Creator>>,
120 pub collection: Option<Collection>,
122 pub uses: Option<Uses>,
124}
125
126impl DataV2 {
127 pub fn to_v1(&self) -> Data {
128 let ns = self.clone();
129 Data {
130 name: ns.name,
131 symbol: ns.symbol,
132 uri: ns.uri,
133 seller_fee_basis_points: ns.seller_fee_basis_points,
134 creators: ns.creators,
135 }
136 }
137}
138
139#[repr(C)]
140#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
141pub enum UseMethod {
142 Burn,
143 Multiple,
144 Single,
145}
146
147#[repr(C)]
148#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
149pub struct Uses {
150 pub use_method: UseMethod, pub remaining: u64, pub total: u64, }
155
156#[repr(C)]
157#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
158pub enum TokenStandard {
159 NonFungible, FungibleAsset, Fungible, NonFungibleEdition, }
164
165#[repr(C)]
166#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone, ShankAccount)]
167pub struct UseAuthorityRecord {
168 pub key: Key, pub allowed_uses: u64, pub bump: u8,
171}
172
173impl UseAuthorityRecord {
174 pub fn from_account_info(a: &AccountInfo) -> Result<UseAuthorityRecord, ProgramError> {
175 let ua: UseAuthorityRecord = try_from_slice_checked(
176 &a.data.borrow_mut(),
177 Key::UseAuthorityRecord,
178 USE_AUTHORITY_RECORD_SIZE,
179 )?;
180 Ok(ua)
181 }
182
183 pub fn from_bytes(b: &[u8]) -> Result<UseAuthorityRecord, ProgramError> {
184 let ua: UseAuthorityRecord =
185 try_from_slice_checked(b, Key::UseAuthorityRecord, USE_AUTHORITY_RECORD_SIZE)?;
186 Ok(ua)
187 }
188
189 pub fn bump_empty(&self) -> bool {
190 self.bump == 0 && self.key == Key::UseAuthorityRecord
191 }
192}
193
194#[repr(C)]
195#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone, ShankAccount)]
196pub struct CollectionAuthorityRecord {
197 pub key: Key, pub bump: u8, }
200
201impl CollectionAuthorityRecord {
202 pub fn from_account_info(a: &AccountInfo) -> Result<CollectionAuthorityRecord, ProgramError> {
203 let ua: CollectionAuthorityRecord = try_from_slice_checked(
204 &a.data.borrow_mut(),
205 Key::CollectionAuthorityRecord,
206 COLLECTION_AUTHORITY_RECORD_SIZE,
207 )?;
208
209 Ok(ua)
210 }
211
212 pub fn from_bytes(b: &[u8]) -> Result<CollectionAuthorityRecord, ProgramError> {
213 let ca: CollectionAuthorityRecord = try_from_slice_checked(
214 b,
215 Key::CollectionAuthorityRecord,
216 COLLECTION_AUTHORITY_RECORD_SIZE,
217 )?;
218 Ok(ca)
219 }
220}
221
222#[repr(C)]
223#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
224pub struct Collection {
225 pub verified: bool,
226 pub key: Pubkey,
227}
228
229#[repr(C)]
230#[derive(Clone, BorshSerialize, Debug, PartialEq, ShankAccount)]
231pub struct Metadata {
232 pub key: Key,
233 pub update_authority: Pubkey,
234 pub mint: Pubkey,
235 pub data: Data,
236 pub primary_sale_happened: bool,
238 pub is_mutable: bool,
240 pub edition_nonce: Option<u8>,
242 pub token_standard: Option<TokenStandard>,
244 pub collection: Option<Collection>,
246 pub uses: Option<Uses>,
248}
249
250impl Metadata {
251 pub fn from_account_info(a: &AccountInfo) -> Result<Metadata, ProgramError> {
252 let md: Metadata = meta_deser(&mut a.data.borrow_mut().as_ref())?;
253
254 Ok(md)
255 }
256}
257
258impl borsh::de::BorshDeserialize for Metadata {
259 fn deserialize(buf: &mut &[u8]) -> ::core::result::Result<Self, borsh::maybestd::io::Error> {
260 let md = meta_deser(buf)?;
261 Ok(md)
262 }
263}
264
265pub trait MasterEdition {
266 fn key(&self) -> Key;
267 fn supply(&self) -> u64;
268 fn set_supply(&mut self, supply: u64);
269 fn max_supply(&self) -> Option<u64>;
270 fn save(&self, account: &AccountInfo) -> ProgramResult;
271}
272
273pub fn get_master_edition(account: &AccountInfo) -> Result<Box<dyn MasterEdition>, ProgramError> {
274 let version = account.data.borrow()[0];
275
276 let master_edition_result: Result<Box<dyn MasterEdition>, ProgramError> = match version {
278 2 => Ok(Box::new(MasterEditionV1::from_account_info(account)?)),
279 6 => Ok(Box::new(MasterEditionV2::from_account_info(account)?)),
280 _ => Err(MetadataError::DataTypeMismatch.into()),
281 };
282
283 master_edition_result
284}
285
286#[repr(C)]
287#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, ShankAccount)]
288pub struct MasterEditionV2 {
289 pub key: Key,
290
291 pub supply: u64,
292
293 pub max_supply: Option<u64>,
294}
295
296impl MasterEdition for MasterEditionV2 {
297 fn key(&self) -> Key {
298 self.key
299 }
300
301 fn supply(&self) -> u64 {
302 self.supply
303 }
304
305 fn set_supply(&mut self, supply: u64) {
306 self.supply = supply;
307 }
308
309 fn max_supply(&self) -> Option<u64> {
310 self.max_supply
311 }
312
313 fn save(&self, account: &AccountInfo) -> ProgramResult {
314 self.serialize(&mut *account.data.borrow_mut())?;
315 Ok(())
316 }
317}
318
319impl MasterEditionV2 {
320 pub fn from_account_info(a: &AccountInfo) -> Result<MasterEditionV2, ProgramError> {
321 let me: MasterEditionV2 = try_from_slice_checked(
322 &a.data.borrow_mut(),
323 Key::MasterEditionV2,
324 MAX_MASTER_EDITION_LEN,
325 )?;
326
327 Ok(me)
328 }
329}
330
331#[repr(C)]
332#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, ShankAccount)]
333pub struct MasterEditionV1 {
334 pub key: Key,
335
336 pub supply: u64,
337
338 pub max_supply: Option<u64>,
339
340 pub printing_mint: Pubkey,
342
343 pub one_time_printing_authorization_mint: Pubkey,
354}
355
356impl MasterEdition for MasterEditionV1 {
357 fn key(&self) -> Key {
358 self.key
359 }
360
361 fn supply(&self) -> u64 {
362 self.supply
363 }
364
365 fn max_supply(&self) -> Option<u64> {
366 self.max_supply
367 }
368
369 fn set_supply(&mut self, supply: u64) {
370 self.supply = supply;
371 }
372
373 fn save(&self, account: &AccountInfo) -> ProgramResult {
374 self.serialize(&mut *account.data.borrow_mut())?;
375 Ok(())
376 }
377}
378
379impl MasterEditionV1 {
380 pub fn from_account_info(a: &AccountInfo) -> Result<MasterEditionV1, ProgramError> {
381 let me: MasterEditionV1 = try_from_slice_checked(
382 &a.data.borrow_mut(),
383 Key::MasterEditionV1,
384 MAX_MASTER_EDITION_LEN,
385 )?;
386
387 Ok(me)
388 }
389}
390
391#[repr(C)]
392#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, ShankAccount)]
393pub struct Edition {
398 pub key: Key,
399
400 pub parent: Pubkey,
402
403 pub edition: u64,
405}
406
407impl Edition {
408 pub fn from_account_info(a: &AccountInfo) -> Result<Edition, ProgramError> {
409 let ed: Edition =
410 try_from_slice_checked(&a.data.borrow_mut(), Key::EditionV1, MAX_EDITION_LEN)?;
411
412 Ok(ed)
413 }
414}
415
416#[repr(C)]
417#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
418pub struct Creator {
419 pub address: Pubkey,
420 pub verified: bool,
421 pub share: u8,
423}
424
425pub trait ReservationList {
426 fn master_edition(&self) -> Pubkey;
427 fn supply_snapshot(&self) -> Option<u64>;
428 fn reservations(&self) -> Vec<Reservation>;
429 fn total_reservation_spots(&self) -> u64;
430 fn current_reservation_spots(&self) -> u64;
431 fn set_master_edition(&mut self, key: Pubkey);
432 fn set_supply_snapshot(&mut self, supply: Option<u64>);
433 fn set_reservations(&mut self, reservations: Vec<Reservation>) -> ProgramResult;
434 fn add_reservation(
435 &mut self,
436 reservation: Reservation,
437 offset: u64,
438 total_spot_offset: u64,
439 ) -> ProgramResult;
440 fn set_total_reservation_spots(&mut self, total_reservation_spots: u64);
441 fn set_current_reservation_spots(&mut self, current_reservation_spots: u64);
442 fn save(&self, account: &AccountInfo) -> ProgramResult;
443}
444
445pub fn get_reservation_list(
446 account: &AccountInfo,
447) -> Result<Box<dyn ReservationList>, ProgramError> {
448 let version = account.data.borrow()[0];
449
450 let reservation_list_result: Result<Box<dyn ReservationList>, ProgramError> = match version {
452 3 => Ok(Box::new(ReservationListV1::from_account_info(account)?)),
453 5 => Ok(Box::new(ReservationListV2::from_account_info(account)?)),
454 _ => Err(MetadataError::DataTypeMismatch.into()),
455 };
456
457 reservation_list_result
458}
459
460#[repr(C)]
461#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone, ShankAccount)]
462pub struct ReservationListV2 {
463 pub key: Key,
464 pub master_edition: Pubkey,
466
467 pub supply_snapshot: Option<u64>,
469 pub reservations: Vec<Reservation>,
470 pub total_reservation_spots: u64,
472 pub current_reservation_spots: u64,
474}
475
476impl ReservationList for ReservationListV2 {
477 fn master_edition(&self) -> Pubkey {
478 self.master_edition
479 }
480
481 fn supply_snapshot(&self) -> Option<u64> {
482 self.supply_snapshot
483 }
484
485 fn reservations(&self) -> Vec<Reservation> {
486 self.reservations.clone()
487 }
488
489 fn set_master_edition(&mut self, key: Pubkey) {
490 self.master_edition = key
491 }
492
493 fn set_supply_snapshot(&mut self, supply: Option<u64>) {
494 self.supply_snapshot = supply;
495 }
496
497 fn add_reservation(
498 &mut self,
499 reservation: Reservation,
500 offset: u64,
501 total_spot_offset: u64,
502 ) -> ProgramResult {
503 let usize_offset = offset as usize;
504 while self.reservations.len() < usize_offset {
505 self.reservations.push(Reservation {
506 address: solana_program::system_program::id(),
507 spots_remaining: 0,
508 total_spots: 0,
509 })
510 }
511 if self.reservations.len() > usize_offset {
512 let replaced_addr = self.reservations[usize_offset].address;
513 let replaced_spots = self.reservations[usize_offset].total_spots;
514
515 if replaced_addr == reservation.address {
516 self.set_current_reservation_spots(
519 self.current_reservation_spots()
520 .checked_sub(replaced_spots)
521 .ok_or(MetadataError::NumericalOverflowError)?,
522 );
523 } else if replaced_addr != solana_program::system_program::id() {
524 return Err(MetadataError::TriedToReplaceAnExistingReservation.into());
525 }
526 self.reservations[usize_offset] = reservation;
527 } else {
528 self.reservations.push(reservation)
529 }
530
531 if usize_offset != 0
532 && self.reservations[usize_offset - 1].address == solana_program::system_program::id()
533 {
534 self.reservations[usize_offset - 1].spots_remaining = total_spot_offset;
536 self.reservations[usize_offset - 1].total_spots = total_spot_offset;
537 }
538
539 Ok(())
540 }
541
542 fn set_reservations(&mut self, reservations: Vec<Reservation>) -> ProgramResult {
543 self.reservations = reservations;
544 Ok(())
545 }
546
547 fn save(&self, account: &AccountInfo) -> ProgramResult {
548 self.serialize(&mut *account.data.borrow_mut())?;
549 Ok(())
550 }
551
552 fn total_reservation_spots(&self) -> u64 {
553 self.total_reservation_spots
554 }
555
556 fn set_total_reservation_spots(&mut self, total_reservation_spots: u64) {
557 self.total_reservation_spots = total_reservation_spots;
558 }
559
560 fn current_reservation_spots(&self) -> u64 {
561 self.current_reservation_spots
562 }
563
564 fn set_current_reservation_spots(&mut self, current_reservation_spots: u64) {
565 self.current_reservation_spots = current_reservation_spots;
566 }
567}
568
569impl ReservationListV2 {
570 pub fn from_account_info(a: &AccountInfo) -> Result<ReservationListV2, ProgramError> {
571 let res: ReservationListV2 = try_from_slice_checked(
572 &a.data.borrow_mut(),
573 Key::ReservationListV2,
574 MAX_RESERVATION_LIST_SIZE,
575 )?;
576
577 Ok(res)
578 }
579}
580
581#[repr(C)]
582#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
583pub struct Reservation {
584 pub address: Pubkey,
585 pub spots_remaining: u64,
586 pub total_spots: u64,
587}
588
589#[repr(C)]
591#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone, ShankAccount)]
592pub struct ReservationListV1 {
593 pub key: Key,
594 pub master_edition: Pubkey,
596
597 pub supply_snapshot: Option<u64>,
599 pub reservations: Vec<ReservationV1>,
600}
601
602impl ReservationList for ReservationListV1 {
603 fn master_edition(&self) -> Pubkey {
604 self.master_edition
605 }
606
607 fn supply_snapshot(&self) -> Option<u64> {
608 self.supply_snapshot
609 }
610
611 fn reservations(&self) -> Vec<Reservation> {
612 self.reservations
613 .iter()
614 .map(|r| Reservation {
615 address: r.address,
616 spots_remaining: r.spots_remaining as u64,
617 total_spots: r.total_spots as u64,
618 })
619 .collect()
620 }
621
622 fn set_master_edition(&mut self, key: Pubkey) {
623 self.master_edition = key
624 }
625
626 fn set_supply_snapshot(&mut self, supply: Option<u64>) {
627 self.supply_snapshot = supply;
628 }
629
630 fn add_reservation(&mut self, reservation: Reservation, _: u64, _: u64) -> ProgramResult {
631 self.reservations = vec![ReservationV1 {
632 address: reservation.address,
633 spots_remaining: reservation.spots_remaining as u8,
634 total_spots: reservation.total_spots as u8,
635 }];
636
637 Ok(())
638 }
639
640 fn set_reservations(&mut self, reservations: Vec<Reservation>) -> ProgramResult {
641 self.reservations = reservations
642 .iter()
643 .map(|r| ReservationV1 {
644 address: r.address,
645 spots_remaining: r.spots_remaining as u8,
646 total_spots: r.total_spots as u8,
647 })
648 .collect();
649 Ok(())
650 }
651
652 fn save(&self, account: &AccountInfo) -> ProgramResult {
653 self.serialize(&mut *account.data.borrow_mut())?;
654 Ok(())
655 }
656
657 fn total_reservation_spots(&self) -> u64 {
658 self.reservations.iter().map(|r| r.total_spots as u64).sum()
659 }
660
661 fn set_total_reservation_spots(&mut self, _: u64) {}
662
663 fn current_reservation_spots(&self) -> u64 {
664 self.reservations.iter().map(|r| r.total_spots as u64).sum()
665 }
666
667 fn set_current_reservation_spots(&mut self, _: u64) {}
668}
669
670impl ReservationListV1 {
671 pub fn from_account_info(a: &AccountInfo) -> Result<ReservationListV1, ProgramError> {
672 let res: ReservationListV1 = try_from_slice_checked(
673 &a.data.borrow_mut(),
674 Key::ReservationListV1,
675 MAX_RESERVATION_LIST_V1_SIZE,
676 )?;
677
678 Ok(res)
679 }
680}
681
682#[repr(C)]
683#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
684pub struct ReservationV1 {
685 pub address: Pubkey,
686 pub spots_remaining: u8,
687 pub total_spots: u8,
688}
689
690#[repr(C)]
691#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone, ShankAccount)]
692pub struct EditionMarker {
693 pub key: Key,
694 pub ledger: [u8; 31],
695}
696
697impl EditionMarker {
698 pub fn from_account_info(a: &AccountInfo) -> Result<EditionMarker, ProgramError> {
699 let res: EditionMarker = try_from_slice_checked(
700 &a.data.borrow_mut(),
701 Key::EditionMarker,
702 MAX_EDITION_MARKER_SIZE,
703 )?;
704
705 Ok(res)
706 }
707
708 fn get_edition_offset_from_starting_index(edition: u64) -> Result<usize, ProgramError> {
709 Ok(edition
710 .checked_rem(EDITION_MARKER_BIT_SIZE)
711 .ok_or(MetadataError::NumericalOverflowError)? as usize)
712 }
713
714 fn get_index(offset_from_start: usize) -> Result<usize, ProgramError> {
715 let index = offset_from_start
716 .checked_div(8)
717 .ok_or(MetadataError::NumericalOverflowError)?;
718
719 if index > 30 {
721 return Err(MetadataError::InvalidEditionIndex.into());
722 }
723
724 Ok(index)
725 }
726
727 fn get_offset_from_right(offset_from_start: usize) -> Result<u32, ProgramError> {
728 Ok(7 - offset_from_start
733 .checked_rem(8)
734 .ok_or(MetadataError::NumericalOverflowError)? as u32)
735 }
736
737 fn get_index_and_mask(edition: u64) -> Result<(usize, u8), ProgramError> {
738 let offset_from_start = EditionMarker::get_edition_offset_from_starting_index(edition)?;
740
741 let index = EditionMarker::get_index(offset_from_start)?;
743
744 let my_position_in_index_starting_from_right =
746 EditionMarker::get_offset_from_right(offset_from_start)?;
747
748 Ok((
749 index,
750 u8::pow(2, my_position_in_index_starting_from_right as u32),
751 ))
752 }
753
754 pub fn edition_taken(&self, edition: u64) -> Result<bool, ProgramError> {
755 let (index, mask) = EditionMarker::get_index_and_mask(edition)?;
756
757 let applied_mask = self.ledger[index] & mask;
759
760 Ok(applied_mask != 0)
762 }
763
764 pub fn insert_edition(&mut self, edition: u64) -> ProgramResult {
765 let (index, mask) = EditionMarker::get_index_and_mask(edition)?;
766 self.ledger[index] |= mask;
768 Ok(())
769 }
770}