1use crate::{error::MetadataError, utils::try_from_slice_checked};
2use borsh::{BorshDeserialize, BorshSerialize};
3use solana_program::{
4 account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError,
5 pubkey::Pubkey,
6};
7pub const PREFIX: &str = "metadata";
9
10pub const EDITION: &str = "edition";
12
13pub const RESERVATION: &str = "reservation";
14
15pub const USER: &str = "user";
16
17pub const BURN: &str = "burn";
18
19pub const COLLECTION_AUTHORITY: &str = "collection_authority";
20
21pub const MAX_NAME_LENGTH: usize = 32;
22
23pub const MAX_SYMBOL_LENGTH: usize = 10;
24
25pub const MAX_URI_LENGTH: usize = 200;
26
27pub const MAX_METADATA_LEN: usize =
281 + 32 + 32 + MAX_DATA_SIZE
32+ 1 + 1 + 9 + 34 + 18 + 2 + 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)]
80#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone, Copy)]
81pub enum Key {
82 Uninitialized,
83 EditionV1,
84 MasterEditionV1,
85 ReservationListV1,
86 MetadataV1,
87 ReservationListV2,
88 MasterEditionV2,
89 EditionMarker,
90 UseAuthorityRecord,
91 CollectionAuthorityRecord
92}
93#[repr(C)]
94#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
95pub struct Data {
96 pub name: String,
98 pub symbol: String,
100 pub uri: String,
102 pub seller_fee_basis_points: u16,
104 pub creators: Option<Vec<Creator>>,
106}
107
108#[repr(C)]
109#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
110pub struct DataV2 {
111 pub name: String,
113 pub symbol: String,
115 pub uri: String,
117 pub seller_fee_basis_points: u16,
119 pub creators: Option<Vec<Creator>>,
121 pub collection: Option<Collection>,
123 pub uses: Option<Uses>,
125}
126
127impl DataV2 {
128 pub fn to_v1(&self) -> Data {
129 let ns = self.clone();
130 Data {
131 name: ns.name,
132 symbol: ns.symbol,
133 uri: ns.uri,
134 seller_fee_basis_points: ns.seller_fee_basis_points,
135 creators: ns.creators,
136 }
137 }
138}
139
140#[repr(C)]
141#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
142pub enum UseMethod {
143 Burn,
144 Multiple,
145 Single,
146}
147
148#[repr(C)]
149#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
150pub struct Uses { 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)]
167pub struct UseAuthorityRecord {
168 pub key: Key, pub allowed_uses: u64, }
171
172impl UseAuthorityRecord {
173 pub fn from_account_info(a: &AccountInfo) -> Result<UseAuthorityRecord, ProgramError> {
174 let ua: UseAuthorityRecord =
175 try_from_slice_checked(&a.data.borrow_mut(), Key::UseAuthorityRecord, USE_AUTHORITY_RECORD_SIZE)?;
176
177 Ok(ua)
178 }
179}
180
181
182#[repr(C)]
183#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
184pub struct CollectionAuthorityRecord {
185 pub key: Key }
187
188impl CollectionAuthorityRecord {
189 pub fn from_account_info(a: &AccountInfo) -> Result<CollectionAuthorityRecord, ProgramError> {
190 let ua: CollectionAuthorityRecord =
191 try_from_slice_checked(&a.data.borrow_mut(), Key::CollectionAuthorityRecord, COLLECTION_AUTHORITY_RECORD_SIZE)?;
192
193 Ok(ua)
194 }
195}
196
197#[repr(C)]
198#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
199pub struct Collection {
200 pub verified: bool,
201 pub key: Pubkey,
202}
203
204#[repr(C)]
205#[derive(Clone, BorshSerialize, BorshDeserialize, Debug)]
206pub struct Metadata {
207 pub key: Key,
208 pub update_authority: Pubkey,
209 pub mint: Pubkey,
210 pub data: Data,
211 pub primary_sale_happened: bool,
213 pub is_mutable: bool,
215 pub edition_nonce: Option<u8>,
217 pub token_standard: Option<TokenStandard>,
219 pub collection: Option<Collection>,
221 pub uses: Option<Uses>,
223}
224
225impl Metadata {
226 pub fn from_account_info(a: &AccountInfo) -> Result<Metadata, ProgramError> {
227 let md: Metadata =
228 try_from_slice_checked(&a.data.borrow_mut(), Key::MetadataV1, MAX_METADATA_LEN)?;
229
230 Ok(md)
231 }
232}
233
234pub trait MasterEdition {
235 fn key(&self) -> Key;
236 fn supply(&self) -> u64;
237 fn set_supply(&mut self, supply: u64);
238 fn max_supply(&self) -> Option<u64>;
239 fn save(&self, account: &AccountInfo) -> ProgramResult;
240}
241
242pub fn get_master_edition(account: &AccountInfo) -> Result<Box<dyn MasterEdition>, ProgramError> {
243 let version = account.data.borrow()[0];
244
245 match version {
247 2 => return Ok(Box::new(MasterEditionV1::from_account_info(account)?)),
248 6 => return Ok(Box::new(MasterEditionV2::from_account_info(account)?)),
249 _ => return Err(MetadataError::DataTypeMismatch.into()),
250 };
251}
252
253#[repr(C)]
254#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
255pub struct MasterEditionV2 {
256 pub key: Key,
257
258 pub supply: u64,
259
260 pub max_supply: Option<u64>,
261}
262
263impl MasterEdition for MasterEditionV2 {
264 fn key(&self) -> Key {
265 self.key
266 }
267
268 fn supply(&self) -> u64 {
269 self.supply
270 }
271
272 fn set_supply(&mut self, supply: u64) {
273 self.supply = supply;
274 }
275
276 fn max_supply(&self) -> Option<u64> {
277 self.max_supply
278 }
279
280 fn save(&self, account: &AccountInfo) -> ProgramResult {
281 self.serialize(&mut *account.data.borrow_mut())?;
282 Ok(())
283 }
284}
285
286impl MasterEditionV2 {
287 pub fn from_account_info(a: &AccountInfo) -> Result<MasterEditionV2, ProgramError> {
288 let me: MasterEditionV2 = try_from_slice_checked(
289 &a.data.borrow_mut(),
290 Key::MasterEditionV2,
291 MAX_MASTER_EDITION_LEN,
292 )?;
293
294 Ok(me)
295 }
296}
297
298#[repr(C)]
299#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
300pub struct MasterEditionV1 {
301 pub key: Key,
302
303 pub supply: u64,
304
305 pub max_supply: Option<u64>,
306
307 pub printing_mint: Pubkey,
309
310 pub one_time_printing_authorization_mint: Pubkey,
321}
322
323impl MasterEdition for MasterEditionV1 {
324 fn key(&self) -> Key {
325 self.key
326 }
327
328 fn supply(&self) -> u64 {
329 self.supply
330 }
331
332 fn max_supply(&self) -> Option<u64> {
333 self.max_supply
334 }
335
336 fn set_supply(&mut self, supply: u64) {
337 self.supply = supply;
338 }
339
340 fn save(&self, account: &AccountInfo) -> ProgramResult {
341 self.serialize(&mut *account.data.borrow_mut())?;
342 Ok(())
343 }
344}
345
346impl MasterEditionV1 {
347 pub fn from_account_info(a: &AccountInfo) -> Result<MasterEditionV1, ProgramError> {
348 let me: MasterEditionV1 = try_from_slice_checked(
349 &a.data.borrow_mut(),
350 Key::MasterEditionV1,
351 MAX_MASTER_EDITION_LEN,
352 )?;
353
354 Ok(me)
355 }
356}
357
358#[repr(C)]
359#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
360pub struct Edition {
365 pub key: Key,
366
367 pub parent: Pubkey,
369
370 pub edition: u64,
372}
373
374impl Edition {
375 pub fn from_account_info(a: &AccountInfo) -> Result<Edition, ProgramError> {
376 let ed: Edition =
377 try_from_slice_checked(&a.data.borrow_mut(), Key::EditionV1, MAX_EDITION_LEN)?;
378
379 Ok(ed)
380 }
381}
382
383#[repr(C)]
384#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
385pub struct Creator {
386 pub address: Pubkey,
387 pub verified: bool,
388 pub share: u8,
390}
391
392pub trait ReservationList {
393 fn master_edition(&self) -> Pubkey;
394 fn supply_snapshot(&self) -> Option<u64>;
395 fn reservations(&self) -> Vec<Reservation>;
396 fn total_reservation_spots(&self) -> u64;
397 fn current_reservation_spots(&self) -> u64;
398 fn set_master_edition(&mut self, key: Pubkey);
399 fn set_supply_snapshot(&mut self, supply: Option<u64>);
400 fn set_reservations(&mut self, reservations: Vec<Reservation>) -> ProgramResult;
401 fn add_reservation(
402 &mut self,
403 reservation: Reservation,
404 offset: u64,
405 total_spot_offset: u64,
406 ) -> ProgramResult;
407 fn set_total_reservation_spots(&mut self, total_reservation_spots: u64);
408 fn set_current_reservation_spots(&mut self, current_reservation_spots: u64);
409 fn save(&self, account: &AccountInfo) -> ProgramResult;
410}
411
412pub fn get_reservation_list(
413 account: &AccountInfo,
414) -> Result<Box<dyn ReservationList>, ProgramError> {
415 let version = account.data.borrow()[0];
416
417 match version {
419 3 => return Ok(Box::new(ReservationListV1::from_account_info(account)?)),
420 5 => return Ok(Box::new(ReservationListV2::from_account_info(account)?)),
421 _ => return Err(MetadataError::DataTypeMismatch.into()),
422 };
423}
424
425#[repr(C)]
426#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
427pub struct ReservationListV2 {
428 pub key: Key,
429 pub master_edition: Pubkey,
431
432 pub supply_snapshot: Option<u64>,
434 pub reservations: Vec<Reservation>,
435 pub total_reservation_spots: u64,
437 pub current_reservation_spots: u64,
439}
440
441impl ReservationList for ReservationListV2 {
442 fn master_edition(&self) -> Pubkey {
443 self.master_edition
444 }
445
446 fn supply_snapshot(&self) -> Option<u64> {
447 self.supply_snapshot
448 }
449
450 fn reservations(&self) -> Vec<Reservation> {
451 self.reservations.clone()
452 }
453
454 fn set_master_edition(&mut self, key: Pubkey) {
455 self.master_edition = key
456 }
457
458 fn set_supply_snapshot(&mut self, supply: Option<u64>) {
459 self.supply_snapshot = supply;
460 }
461
462 fn add_reservation(
463 &mut self,
464 reservation: Reservation,
465 offset: u64,
466 total_spot_offset: u64,
467 ) -> ProgramResult {
468 let usize_offset = offset as usize;
469 while self.reservations.len() < usize_offset {
470 self.reservations.push(Reservation {
471 address: solana_program::system_program::id(),
472 spots_remaining: 0,
473 total_spots: 0,
474 })
475 }
476 if self.reservations.len() > usize_offset {
477 let replaced_addr = self.reservations[usize_offset].address;
478 let replaced_spots = self.reservations[usize_offset].total_spots;
479
480 if replaced_addr == reservation.address {
481 self.set_current_reservation_spots(
484 self.current_reservation_spots()
485 .checked_sub(replaced_spots)
486 .ok_or(MetadataError::NumericalOverflowError)?,
487 );
488 } else if replaced_addr != solana_program::system_program::id() {
489 return Err(MetadataError::TriedToReplaceAnExistingReservation.into());
490 }
491 self.reservations[usize_offset] = reservation;
492 } else {
493 self.reservations.push(reservation)
494 }
495
496 if usize_offset != 0
497 && self.reservations[usize_offset - 1].address == solana_program::system_program::id()
498 {
499 self.reservations[usize_offset - 1].spots_remaining = total_spot_offset;
501 self.reservations[usize_offset - 1].total_spots = total_spot_offset;
502 }
503
504 Ok(())
505 }
506
507 fn set_reservations(&mut self, reservations: Vec<Reservation>) -> ProgramResult {
508 self.reservations = reservations;
509 Ok(())
510 }
511
512 fn save(&self, account: &AccountInfo) -> ProgramResult {
513 self.serialize(&mut *account.data.borrow_mut())?;
514 Ok(())
515 }
516
517 fn total_reservation_spots(&self) -> u64 {
518 self.total_reservation_spots
519 }
520
521 fn set_total_reservation_spots(&mut self, total_reservation_spots: u64) {
522 self.total_reservation_spots = total_reservation_spots;
523 }
524
525 fn current_reservation_spots(&self) -> u64 {
526 self.current_reservation_spots
527 }
528
529 fn set_current_reservation_spots(&mut self, current_reservation_spots: u64) {
530 self.current_reservation_spots = current_reservation_spots;
531 }
532}
533
534impl ReservationListV2 {
535 pub fn from_account_info(a: &AccountInfo) -> Result<ReservationListV2, ProgramError> {
536 let res: ReservationListV2 = try_from_slice_checked(
537 &a.data.borrow_mut(),
538 Key::ReservationListV2,
539 MAX_RESERVATION_LIST_SIZE,
540 )?;
541
542 Ok(res)
543 }
544}
545
546#[repr(C)]
547#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
548pub struct Reservation {
549 pub address: Pubkey,
550 pub spots_remaining: u64,
551 pub total_spots: u64,
552}
553
554#[repr(C)]
556#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
557pub struct ReservationListV1 {
558 pub key: Key,
559 pub master_edition: Pubkey,
561
562 pub supply_snapshot: Option<u64>,
564 pub reservations: Vec<ReservationV1>,
565}
566
567impl ReservationList for ReservationListV1 {
568 fn master_edition(&self) -> Pubkey {
569 self.master_edition
570 }
571
572 fn supply_snapshot(&self) -> Option<u64> {
573 self.supply_snapshot
574 }
575
576 fn reservations(&self) -> Vec<Reservation> {
577 self.reservations
578 .iter()
579 .map(|r| Reservation {
580 address: r.address,
581 spots_remaining: r.spots_remaining as u64,
582 total_spots: r.total_spots as u64,
583 })
584 .collect()
585 }
586
587 fn set_master_edition(&mut self, key: Pubkey) {
588 self.master_edition = key
589 }
590
591 fn set_supply_snapshot(&mut self, supply: Option<u64>) {
592 self.supply_snapshot = supply;
593 }
594
595 fn add_reservation(&mut self, reservation: Reservation, _: u64, _: u64) -> ProgramResult {
596 self.reservations = vec![ReservationV1 {
597 address: reservation.address,
598 spots_remaining: reservation.spots_remaining as u8,
599 total_spots: reservation.total_spots as u8,
600 }];
601
602 Ok(())
603 }
604
605 fn set_reservations(&mut self, reservations: Vec<Reservation>) -> ProgramResult {
606 self.reservations = reservations
607 .iter()
608 .map(|r| ReservationV1 {
609 address: r.address,
610 spots_remaining: r.spots_remaining as u8,
611 total_spots: r.total_spots as u8,
612 })
613 .collect();
614 Ok(())
615 }
616
617 fn save(&self, account: &AccountInfo) -> ProgramResult {
618 self.serialize(&mut *account.data.borrow_mut())?;
619 Ok(())
620 }
621
622 fn total_reservation_spots(&self) -> u64 {
623 self.reservations.iter().map(|r| r.total_spots as u64).sum()
624 }
625
626 fn set_total_reservation_spots(&mut self, _: u64) {}
627
628 fn current_reservation_spots(&self) -> u64 {
629 self.reservations.iter().map(|r| r.total_spots as u64).sum()
630 }
631
632 fn set_current_reservation_spots(&mut self, _: u64) {}
633}
634
635impl ReservationListV1 {
636 pub fn from_account_info(a: &AccountInfo) -> Result<ReservationListV1, ProgramError> {
637 let res: ReservationListV1 = try_from_slice_checked(
638 &a.data.borrow_mut(),
639 Key::ReservationListV1,
640 MAX_RESERVATION_LIST_V1_SIZE,
641 )?;
642
643 Ok(res)
644 }
645}
646
647#[repr(C)]
648#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
649pub struct ReservationV1 {
650 pub address: Pubkey,
651 pub spots_remaining: u8,
652 pub total_spots: u8,
653}
654
655#[repr(C)]
656#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
657pub struct EditionMarker {
658 pub key: Key,
659 pub ledger: [u8; 31],
660}
661
662impl EditionMarker {
663 pub fn from_account_info(a: &AccountInfo) -> Result<EditionMarker, ProgramError> {
664 let res: EditionMarker = try_from_slice_checked(
665 &a.data.borrow_mut(),
666 Key::EditionMarker,
667 MAX_EDITION_MARKER_SIZE,
668 )?;
669
670 Ok(res)
671 }
672
673 fn get_edition_offset_from_starting_index(edition: u64) -> Result<usize, ProgramError> {
674 Ok(edition
675 .checked_rem(EDITION_MARKER_BIT_SIZE)
676 .ok_or(MetadataError::NumericalOverflowError)? as usize)
677 }
678
679 fn get_index(offset_from_start: usize) -> Result<usize, ProgramError> {
680 let index = offset_from_start
681 .checked_div(8)
682 .ok_or(MetadataError::NumericalOverflowError)?;
683
684 if index > 30 {
686 return Err(MetadataError::InvalidEditionIndex.into());
687 }
688
689 Ok(index)
690 }
691
692 fn get_offset_from_right(offset_from_start: usize) -> Result<u32, ProgramError> {
693 Ok(7 - offset_from_start
698 .checked_rem(8)
699 .ok_or(MetadataError::NumericalOverflowError)? as u32)
700 }
701
702 fn get_index_and_mask(edition: u64) -> Result<(usize, u8), ProgramError> {
703 let offset_from_start = EditionMarker::get_edition_offset_from_starting_index(edition)?;
705
706 let index = EditionMarker::get_index(offset_from_start)?;
708
709 let my_position_in_index_starting_from_right =
711 EditionMarker::get_offset_from_right(offset_from_start)?;
712
713 Ok((
714 index,
715 u8::pow(2, my_position_in_index_starting_from_right as u32),
716 ))
717 }
718
719 pub fn edition_taken(&self, edition: u64) -> Result<bool, ProgramError> {
720 let (index, mask) = EditionMarker::get_index_and_mask(edition)?;
721
722 let applied_mask = self.ledger[index] & mask;
724
725 Ok(applied_mask != 0)
727 }
728
729 pub fn insert_edition(&mut self, edition: u64) -> ProgramResult {
730 let (index, mask) = EditionMarker::get_index_and_mask(edition)?;
731 self.ledger[index] = self.ledger[index] | mask;
733 Ok(())
734 }
735}