use super::*;
pub const MAX_MASTER_EDITION_LEN: usize = 1 + 9 + 8 + 264;
pub const TOKEN_STANDARD_INDEX: usize = MAX_MASTER_EDITION_LEN - 1;
pub trait MasterEdition {
fn key(&self) -> Key;
fn supply(&self) -> u64;
fn set_supply(&mut self, supply: u64);
fn max_supply(&self) -> Option<u64>;
fn save(&self, account: &AccountInfo) -> ProgramResult;
}
pub fn get_master_edition(account: &AccountInfo) -> Result<Box<dyn MasterEdition>, ProgramError> {
let version = account.data.borrow()[0];
let master_edition_result: Result<Box<dyn MasterEdition>, ProgramError> = match version {
2 => {
let me = MasterEditionV1::from_account_info(account)?;
Ok(Box::new(me))
}
6 => {
let me = MasterEditionV2::from_account_info(account)?;
Ok(Box::new(me))
}
_ => Err(MetadataError::DataTypeMismatch.into()),
};
master_edition_result
}
#[macro_export]
macro_rules! edition_seeds {
($mint:expr) => {{
let path = vec![
"metadata".as_bytes(),
$crate::ID.as_ref(),
$mint.as_ref(),
"edition".as_bytes(),
];
let (_, bump) = Pubkey::find_program_address(&path, &$crate::ID);
&[
"metadata".as_bytes(),
$crate::ID.as_ref(),
$mint.as_ref(),
"edition".as_bytes(),
&[bump],
]
}};
}
#[repr(C)]
#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, ShankAccount)]
pub struct MasterEditionV2 {
pub key: Key,
pub supply: u64,
pub max_supply: Option<u64>,
}
impl Default for MasterEditionV2 {
fn default() -> Self {
MasterEditionV2 {
key: Key::MasterEditionV2,
supply: 0,
max_supply: Some(0),
}
}
}
impl TokenMetadataAccount for MasterEditionV2 {
fn key() -> Key {
Key::MasterEditionV2
}
fn size() -> usize {
MAX_MASTER_EDITION_LEN
}
}
impl MasterEdition for MasterEditionV2 {
fn key(&self) -> Key {
self.key
}
fn supply(&self) -> u64 {
self.supply
}
fn set_supply(&mut self, supply: u64) {
self.supply = supply;
}
fn max_supply(&self) -> Option<u64> {
self.max_supply
}
fn save(&self, account: &AccountInfo) -> ProgramResult {
let mut storage = &mut account.data.borrow_mut()[..TOKEN_STANDARD_INDEX];
BorshSerialize::serialize(self, &mut storage)?;
Ok(())
}
}
#[repr(C)]
#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, ShankAccount)]
pub struct MasterEditionV1 {
pub key: Key,
pub supply: u64,
pub max_supply: Option<u64>,
pub printing_mint: Pubkey,
pub one_time_printing_authorization_mint: Pubkey,
}
impl TokenMetadataAccount for MasterEditionV1 {
fn key() -> Key {
Key::MasterEditionV1
}
fn size() -> usize {
MAX_MASTER_EDITION_LEN
}
}
impl MasterEdition for MasterEditionV1 {
fn key(&self) -> Key {
self.key
}
fn supply(&self) -> u64 {
self.supply
}
fn max_supply(&self) -> Option<u64> {
self.max_supply
}
fn set_supply(&mut self, supply: u64) {
self.supply = supply;
}
fn save(&self, account: &AccountInfo) -> ProgramResult {
let mut storage = &mut account.data.borrow_mut()[..TOKEN_STANDARD_INDEX];
BorshSerialize::serialize(self, &mut storage)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use borsh::BorshSerialize;
use solana_program::account_info::AccountInfo;
use solana_sdk::{signature::Keypair, signer::Signer};
use crate::{
error::MetadataError,
state::{Key, MasterEditionV2, Metadata, TokenMetadataAccount},
ID,
};
#[test]
fn successfully_deserialize() {
let expected_data = MasterEditionV2::default();
let mut buf = Vec::new();
expected_data.serialize(&mut buf).unwrap();
MasterEditionV2::pad_length(&mut buf).unwrap();
let pubkey = Keypair::new().pubkey();
let owner = &ID;
let mut lamports = 1_000_000_000;
let mut data = buf.clone();
let account_info = AccountInfo::new(
&pubkey,
false,
true,
&mut lamports,
&mut data,
owner,
false,
1_000_000_000,
);
let data = MasterEditionV2::from_account_info(&account_info).unwrap();
assert_eq!(data.key, Key::MasterEditionV2);
assert_eq!(data, expected_data);
}
#[test]
fn deserializing_wrong_account_type_fails() {
let wrong_type = Metadata::default();
let mut buf = Vec::new();
wrong_type.serialize(&mut buf).unwrap();
Metadata::pad_length(&mut buf).unwrap();
let pubkey = Keypair::new().pubkey();
let owner = &ID;
let mut lamports = 1_000_000_000;
let mut data = buf.clone();
let account_info = AccountInfo::new(
&pubkey,
false,
true,
&mut lamports,
&mut data,
owner,
false,
1_000_000_000,
);
let error = MasterEditionV2::from_account_info(&account_info).unwrap_err();
assert_eq!(error, MetadataError::DataTypeMismatch.into());
}
}