Skip to main content

solana_nft_token_metadata/
state.rs

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};
7/// prefix used for PDAs to avoid certain collision attacks (https://en.wikipedia.org/wiki/Collision_attack#Chosen-prefix_collision_attack)
8pub const PREFIX: &str = "metadata";
9
10/// Used in seeds to make Edition model pda address
11pub 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 //key 
29+ 32 // update auth pubkey
30+ 32 // mint pubkey
31+ MAX_DATA_SIZE 
32+ 1 // primary sale
33+ 1 // mutable
34+ 9 // nonce (pretty sure this only needs to be 2)
35+ 34 // collection
36+ 18 // uses
37+ 2 // token standard
38+ 118; // Padding
39
40pub 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
53// Large buffer because the older master editions have two pubkeys in them,
54// need to keep two versions same size because the conversion process actually changes the same account
55// by rewriting it.
56pub 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
64// can hold up to 200 keys per reservation, note: the extra 8 is for number of elements in the vec
65pub const MAX_RESERVATION_LIST_V1_SIZE: usize = 1 + 32 + 8 + 8 + MAX_RESERVATIONS * 34 + 100;
66
67// can hold up to 200 keys per reservation, note: the extra 8 is for number of elements in the vec
68pub 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; //10 byte padding
75
76pub const COLLECTION_AUTHORITY_RECORD_SIZE: usize = 11; //10 byte padding
77
78
79#[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    /// The name of the asset
97    pub name: String,
98    /// The symbol for the asset
99    pub symbol: String,
100    /// URI pointing to JSON representing the asset
101    pub uri: String,
102    /// Royalty basis points that goes to creators in secondary sales (0-10000)
103    pub seller_fee_basis_points: u16,
104    /// Array of creators, optional
105    pub creators: Option<Vec<Creator>>,
106}
107
108#[repr(C)]
109#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
110pub struct DataV2 {
111    /// The name of the asset
112    pub name: String,
113    /// The symbol for the asset
114    pub symbol: String,
115    /// URI pointing to JSON representing the asset
116    pub uri: String,
117    /// Royalty basis points that goes to creators in secondary sales (0-10000)
118    pub seller_fee_basis_points: u16,
119    /// Array of creators, optional
120    pub creators: Option<Vec<Creator>>,
121    /// Collection
122    pub collection: Option<Collection>,
123    /// Uses
124    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 { // 17 bytes + Option byte
151    pub use_method: UseMethod, //1
152    pub remaining: u64, //8
153    pub total: u64, //8
154}
155
156#[repr(C)]
157#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
158pub enum TokenStandard {
159    NonFungible,  // This is a master edition
160    FungibleAsset, // A token with metadata that can also have attrributes
161    Fungible,     // A token with simple metadata
162    NonFungibleEdition,      // This is a limited edition
163}
164
165#[repr(C)]
166#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
167pub struct UseAuthorityRecord {
168    pub key: Key, //1
169    pub allowed_uses: u64, //8
170}
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 //1
186}
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    // Immutable, once flipped, all sales of this metadata are considered secondary.
212    pub primary_sale_happened: bool,
213    // Whether or not the data struct is mutable, default is not
214    pub is_mutable: bool,
215    /// nonce for easy calculation of editions, if present
216    pub edition_nonce: Option<u8>,
217    /// Since we cannot easily change Metadata, we add the new DataV2 fields here at the end.
218    pub token_standard: Option<TokenStandard>,
219    /// Collection
220    pub collection: Option<Collection>,
221    /// Uses
222    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    // For some reason when converting Key to u8 here, it becomes unreachable. Use direct constant instead.
246    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    /// Can be used to mint tokens that give one-time permission to mint a single limited edition.
308    pub printing_mint: Pubkey,
309
310    /// If you don't know how many printing tokens you are going to need, but you do know
311    /// you are going to need some amount in the future, you can use a token from this mint.
312    /// Coming back to token metadata with one of these tokens allows you to mint (one time)
313    /// any number of printing tokens you want. This is used for instance by Auction Manager
314    /// with participation NFTs, where we dont know how many people will bid and need participation
315    /// printing tokens to redeem, so we give it ONE of these tokens to use after the auction is over,
316    /// because when the auction begins we just dont know how many printing tokens we will need,
317    /// but at the end we will. At the end it then burns this token with token-metadata to
318    /// get the printing tokens it needs to give to bidders. Each bidder then redeems a printing token
319    /// to get their limited editions.
320    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)]
360/// All Editions should never have a supply greater than 1.
361/// To enforce this, a transfer mint authority instruction will happen when
362/// a normal token is turned into an Edition, and in order for a Metadata update authority
363/// to do this transaction they will also need to sign the transaction as the Mint authority.
364pub struct Edition {
365    pub key: Key,
366
367    /// Points at MasterEdition struct
368    pub parent: Pubkey,
369
370    /// Starting at 0 for master record, this is incremented for each edition minted.
371    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    // In percentages, NOT basis points ;) Watch out!
389    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    // For some reason when converting Key to u8 here, it becomes unreachable. Use direct constant instead.
418    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    /// Present for reverse lookups
430    pub master_edition: Pubkey,
431
432    /// What supply counter was on master_edition when this reservation was created.
433    pub supply_snapshot: Option<u64>,
434    pub reservations: Vec<Reservation>,
435    /// How many reservations there are going to be, given on first set_reservation call
436    pub total_reservation_spots: u64,
437    /// Cached count of reservation spots in the reservation vec to save on CPU.
438    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                // Since we will have incremented, decrease in advance so we dont blow the spot check.
482                // Super hacky but this code is to be deprecated.
483                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            // This becomes an anchor then for calculations... put total spot offset in here.
500            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// Legacy Reservation List with u8s
555#[repr(C)]
556#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
557pub struct ReservationListV1 {
558    pub key: Key,
559    /// Present for reverse lookups
560    pub master_edition: Pubkey,
561
562    /// What supply counter was on master_edition when this reservation was created.
563    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        // With only EDITION_MARKER_BIT_SIZE bits, or 31 bytes, we have a max constraint here.
685        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        // We're saying the left hand side of a u8 is the 0th index so to get a 1 in that 0th index
694        // you need to shift a 1 over 8 spots from the right hand side. To do that you actually
695        // need not 00000001 but 10000000 which you can get by simply multiplying 1 by 2^7, 128 and then ORing
696        // it with the current value.
697        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        // How many editions off we are from edition at 0th index
704        let offset_from_start = EditionMarker::get_edition_offset_from_starting_index(edition)?;
705
706        // How many whole u8s we are from the u8 at the 0th index, which basically dividing by 8
707        let index = EditionMarker::get_index(offset_from_start)?;
708
709        // what position in the given u8 bitset are we (remainder math)
710        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        // apply mask with bitwise and with a 1 to determine if it is set or not
723        let applied_mask = self.ledger[index] & mask;
724
725        // What remains should not equal 0.
726        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        // bitwise or a 1 into our position in that position
732        self.ledger[index] = self.ledger[index] | mask;
733        Ok(())
734    }
735}