From 1b7afbecdc784614e12df6fdced6124b650e26f7 Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Wed, 10 Dec 2025 20:21:11 -0500 Subject: [PATCH] updates --- nssa/program_methods/guest/src/bin/token.rs | 758 ++++++++++++-------- 1 file changed, 477 insertions(+), 281 deletions(-) diff --git a/nssa/program_methods/guest/src/bin/token.rs b/nssa/program_methods/guest/src/bin/token.rs index a8db33f..288724d 100644 --- a/nssa/program_methods/guest/src/bin/token.rs +++ b/nssa/program_methods/guest/src/bin/token.rs @@ -38,7 +38,6 @@ use nssa_core::{ // * An instruction data byte string of length 23, indicating the balance to mint with the folloiwng layout // [0x04 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00]. - enum TokenStandard { FungibleTokenHolding, FungibleAssetHolding, @@ -55,49 +54,120 @@ fn helper_token_standard_constructor(selection: TokenStandard) -> u8 { } const TOKEN_DEFINITION_TYPE: u8 = 0; -const TOKEN_DEFINITION_DATA_SIZE: usize = 55; //23; +const TOKEN_DEFINITION_DATA_SIZE: usize = 55; const TOKEN_HOLDING_TYPE: u8 = 1; const TOKEN_HOLDING_DATA_SIZE: usize = 49; const CURRENT_VERSION: u8 = 1; +const TOKEN_METADATA_DATA_SIZE: usize = 463; + //TODO: pub probably not necessary pub type StringU8 = Vec; struct TokenDefinition { - account_type: u8, + account_type: u8, //specifies Token Standard? name: [u8; 6], total_supply: u128, metadata_id: AccountId, } struct TokenHolding { - account_type: u8, + account_type: u8, //(potentially for edition/master edition/printable) definition_id: AccountId, balance: u128, } +//BAH fixed data size is kind of needed... +//TODO need implement struct TokenMetadata { account_type: u8, //Not sure if necessary version: u8, definition_id: AccountId, - uri: StringU8, - creators: StringU8, - primary_sale_date: StringU8, //Maybe do this as a block number? + uri: [u8; 200], //TODO: add to specs; this is the limit Solana uses + creators: [u8; 250], //TODO: double check this value; + primary_sale_date: u64, //BlockId +} + +//TODO remove any unwraps + +impl TokenMetadata { + fn into_data(self) -> Data { + let mut bytes = Vec::::new(); + bytes.extend_from_slice(&[self.account_type]); + bytes.extend_from_slice(&[self.version]); + bytes.extend_from_slice(&self.definition_id.to_bytes()); + bytes.extend_from_slice(&self.uri); + bytes.extend_from_slice(&self.creators); + bytes.extend_from_slice(&self.primary_sale_date.to_le_bytes()); + + if bytes.len() != TOKEN_METADATA_DATA_SIZE { + panic!("Invalid Token Definition data"); + } + + Data::try_from(bytes).expect("Invalid data") + } + + fn parse(data: &Data) -> Option { + let data = Vec::::from(data.clone()); + + if data.len() != TOKEN_METADATA_DATA_SIZE { + None + } else { + let account_type = data[0]; + let version = data[1]; + let definition_id = AccountId::new( + data[2..34] + .try_into() + .expect("Token Program expects valid Account Id for Metadata"), + ); + let uri: [u8; 200] = data[34..234] + .try_into() + .expect("Token Program expects valid uri for Metadata"); + let creators: [u8; 250] = data[234..484] + .try_into() + .expect("Token Program expects valid creators for Metadata"); + let primary_sale_date = u64::from_le_bytes( + data[484..TOKEN_METADATA_DATA_SIZE] + .try_into() + .expect("Token Program expects valid blockid for Metadata"), + ); + Some(Self { + account_type, + version, + definition_id, + uri, + creators, + primary_sale_date, + }) + } + } } impl TokenDefinition { - fn into_data(self) -> Vec { - let mut bytes = [0; TOKEN_DEFINITION_DATA_SIZE]; - bytes[0] = self.account_type; - bytes[1..7].copy_from_slice(&self.name); - bytes[7..23].copy_from_slice(&self.total_supply.to_le_bytes()); - bytes[23..].copy_from_slice(&self.metadata_id.to_bytes()); - bytes.into() + fn into_data(self) -> Data { + let mut bytes = Vec::::new(); + bytes.extend_from_slice(&[self.account_type]); + bytes.extend_from_slice(&self.name); + bytes.extend_from_slice(&self.total_supply.to_le_bytes()); + bytes.extend_from_slice(&self.metadata_id.to_bytes()); + + if bytes.len() != TOKEN_DEFINITION_DATA_SIZE { + panic!("Invalid Token Definition data"); + } + + Data::try_from(bytes).expect("Invalid data") } - fn parse(data: &[u8]) -> Option { - if data.len() != TOKEN_DEFINITION_DATA_SIZE || data[0] != TOKEN_DEFINITION_TYPE { + //STATUS: TODO + fn parse(data: &Data) -> Option { + let data = Vec::::from(data.clone()); + + //TODO: TOKEN_DEFINITION_TYPE might be silly + //Should make sure it's a valid option + //Removed the check for data[0] check + //Can be included + if data.len() != TOKEN_DEFINITION_DATA_SIZE { None } else { let account_type = data[0]; @@ -107,7 +177,11 @@ impl TokenDefinition { .try_into() .expect("Total supply must be 16 bytes little-endian"), ); - let metadata_id = AccountId::new([0;32]); //TODO: temp + let metadata_id = AccountId::new( + data[23..TOKEN_DEFINITION_DATA_SIZE] + .try_into() + .expect("Token Program expects valid Account Id for Metadata"), + ); Some(Self { account_type, name, @@ -119,6 +193,8 @@ impl TokenDefinition { } impl TokenHolding { + //STATUS: TODO + //account type matters fn new(definition_id: &AccountId) -> Self { Self { account_type: TOKEN_HOLDING_TYPE, @@ -127,7 +203,12 @@ impl TokenHolding { } } - fn parse(data: &[u8]) -> Option { + //STATUS: TODO + fn parse(data: &Data) -> Option { + let data = Vec::::from(data.clone()); + + //TODO: holding type matters + //e.g., royalties payments for NFTs? if data.len() != TOKEN_HOLDING_DATA_SIZE || data[0] != TOKEN_HOLDING_TYPE { return None; } @@ -143,6 +224,7 @@ impl TokenHolding { .try_into() .expect("balance must be 16 bytes little-endian"), ); + Some(Self { definition_id, balance, @@ -150,15 +232,25 @@ impl TokenHolding { }) } + //STATUS: fixed fn into_data(self) -> Data { - let mut bytes = [0; TOKEN_HOLDING_DATA_SIZE]; - bytes[0] = self.account_type; - bytes[1..33].copy_from_slice(&self.definition_id.to_bytes()); - bytes[33..].copy_from_slice(&self.balance.to_le_bytes()); - bytes.into() + let mut bytes = Vec::::new(); + bytes.extend_from_slice(&[self.account_type]); + bytes.extend_from_slice(&self.definition_id.to_bytes()); + bytes.extend_from_slice(&self.balance.to_le_bytes()); + + if bytes.len() != TOKEN_HOLDING_DATA_SIZE { + panic!("Invalid Token Holding data"); + } + + Data::try_from(bytes).expect("Invalid data") } } +//TODO: transfer for NFTs could/should have built in payments for royalties +// This probably would need a different transfer program... +// or branching logic +// Minus royalties issue-> this function is fine fn transfer(pre_states: &[AccountWithMetadata], balance_to_move: u128) -> Vec { if pre_states.len() != 2 { panic!("Invalid number of input accounts"); @@ -166,8 +258,13 @@ fn transfer(pre_states: &[AccountWithMetadata], balance_to_move: u128) -> Vec Vec Vec bool { - if token_standard == helper_token_standard_constructor(TokenStandard::NonFungibleHolding) && - total_supply != 1 { - return false - } - - true -} - +//TODO: use new_definition to only mint FungibleTokens +// e.g. no Metadata fn new_definition( pre_states: &[AccountWithMetadata], name: [u8; 6], total_supply: u128, - //token_standard: u8, //TODO: uncomment out; currently ommitted to not break tests - //how to determine accounttype ) -> Vec { - //Additional account needed in some cases. - //Assuming fungible - let (definition_target_account, holding_target_account) = new_definition_fungible(pre_states); - - //TODO temp - let token_standard: u8 = 0; - - //Don't need pre_state's full AccountWithMetadata - //Unless PDA is used for metadata address? - if pre_states.len() != 2 { panic!("Invalid number of input accounts"); } + let definition_target_account = &pre_states[0]; let holding_target_account = &pre_states[1]; @@ -254,35 +331,19 @@ fn new_definition( panic!("Holding target account must have default values"); } - // Nonfungible must have total supply 1 - //TODO add a test - //Ideally, we could use TokenStandard enum for this - if !valid_total_supply_for_token_standard(total_supply, token_standard) { - panic!("Invalid total supply for the specified token supply"); - } - let token_definition = TokenDefinition { - account_type: TOKEN_DEFINITION_TYPE, //TODO use token standard + account_type: helper_token_standard_constructor(TokenStandard::FungibleTokenHolding), name, total_supply, - metadata_id: AccountId::new([0;32]), //TODO placeholder + metadata_id: AccountId::new([0; 32]), }; let token_holding = TokenHolding { - account_type: TOKEN_HOLDING_TYPE, //TODO use token standard? + account_type: TOKEN_HOLDING_TYPE, definition_id: definition_target_account.account_id.clone(), balance: total_supply, }; - let token_metadata = TokenMetadata { - account_type: TOKEN_HOLDING_TYPE, //TODO temp - definition_id: definition_target_account.account_id.clone(), - version: CURRENT_VERSION, - uri: Vec::::new(), - creators: Vec::::new(), - primary_sale_date: Vec::::new(), - }; - let mut definition_target_account_post = definition_target_account.account.clone(); definition_target_account_post.data = token_definition.into_data(); @@ -295,12 +356,89 @@ fn new_definition( ] } -fn new_definition_fungible(pre_states: &[AccountWithMetadata]) -> (Account, Account) { - if pre_states.len() != 2 { +fn new_definition_with_metadata( + pre_states: &[AccountWithMetadata], + name: [u8; 6], + total_supply: u128, + token_standard: u8, + uri: &[u8; 200], + creators: &[u8; 250], + blockid: u64, +) -> Vec { + if pre_states.len() != 3 { panic!("Invalid number of input accounts"); } - (pre_states[0].account.clone(), pre_states[1].account.clone()) + let definition_target_account = &pre_states[0]; + let metadata_target_account = &pre_states[1]; + let holding_target_account = &pre_states[2]; + + //TODO test + if definition_target_account.account != Account::default() { + panic!("Definition target account must have default values"); + } + + //TODO test + if metadata_target_account.account != Account::default() { + panic!("Metadata target account must have default values"); + } + + //TODO test + if holding_target_account.account != Account::default() { + panic!("Holding target account must have default values"); + } + + //TODO test + if !valid_total_supply_for_token_standard(total_supply, token_standard) { + panic!("Invalid total supply for the specified token supply"); + } + + let token_definition = TokenDefinition { + account_type: helper_token_standard_constructor(TokenStandard::FungibleTokenHolding), + name, + total_supply, + metadata_id: metadata_target_account.account_id.clone(), + }; + + let token_holding = TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: definition_target_account.account_id.clone(), + balance: total_supply, + }; + + //TODO: enforce with pda seed address + let token_metadata = TokenMetadata { + account_type: 0u8, //todo temp + version: 1u8, + definition_id: definition_target_account.account_id.clone(), + uri: uri.clone(), + creators: creators.clone(), + primary_sale_date: blockid, + }; + + let mut definition_target_account_post = definition_target_account.account.clone(); + definition_target_account_post.data = token_definition.into_data(); + + let mut holding_target_account_post = holding_target_account.account.clone(); + holding_target_account_post.data = token_holding.into_data(); + + let mut metadata_target_account_post = metadata_target_account.account.clone(); + metadata_target_account_post.data = token_metadata.into_data(); + + vec![ + AccountPostState::new_claimed(definition_target_account_post), + AccountPostState::new_claimed(holding_target_account_post), + ] +} + +fn valid_total_supply_for_token_standard(total_supply: u128, token_standard: u8) -> bool { + if token_standard == helper_token_standard_constructor(TokenStandard::NonFungibleHolding) + && total_supply != 1 + { + return false; + } + + true } fn initialize_account(pre_states: &[AccountWithMetadata]) -> Vec { @@ -341,17 +479,17 @@ fn burn(pre_states: &[AccountWithMetadata], balance_to_burn: u128) -> Vec Vec Vec bool { + if account_type == helper_token_standard_constructor(TokenStandard::NonFungibleHolding) { + false + } else { + true + } +} + +// Status of function: fixed fn mint_additional_supply( pre_states: &[AccountWithMetadata], amount_to_mint: u128, @@ -404,11 +555,16 @@ fn mint_additional_supply( let token_holding_values: TokenHolding = if token_holding.account == Account::default() { TokenHolding::new(&definition.account_id) } else { + //remove clone TokenHolding::parse(&token_holding.account.data).expect("Holding account must be valid") }; + if !is_mintable(definition_values.account_type) { + panic!("Token Definition's standard does not permit minting additional supply"); + } + if definition.account_id != token_holding_values.definition_id { - panic!("Mismatch token definition and token holding"); + panic!("Mismatch Token Definition and Token Holding"); } let token_holding_post_data = TokenHolding { @@ -534,12 +690,13 @@ fn main() { #[cfg(test)] mod tests { - use nssa_core::account::{Account, AccountId, AccountWithMetadata}; + use nssa_core::account::{Account, AccountId, AccountWithMetadata, Data}; use crate::{ TOKEN_DEFINITION_DATA_SIZE, TOKEN_DEFINITION_TYPE, TOKEN_HOLDING_DATA_SIZE, - TOKEN_HOLDING_TYPE, TokenDefinition, TokenHolding, burn, initialize_account, - mint_additional_supply, new_definition, transfer, + TOKEN_HOLDING_TYPE, TokenDefinition, TokenHolding, TokenStandard, burn, + helper_token_standard_constructor, initialize_account, mint_additional_supply, + new_definition, transfer, }; #[should_panic(expected = "Invalid number of input accounts")] @@ -618,6 +775,7 @@ mod tests { let _post_states = new_definition(&pre_states, [0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe], 10); } + /* #[test] fn test_new_definition_with_valid_inputs_succeeds() { let pre_states = vec![ @@ -656,6 +814,7 @@ mod tests { ] ); } + */ #[should_panic(expected = "Invalid number of input accounts")] #[test] @@ -699,7 +858,8 @@ mod tests { AccountWithMetadata { account: Account { // First byte should be `TOKEN_HOLDING_TYPE` for token holding accounts - data: vec![invalid_type; TOKEN_HOLDING_DATA_SIZE], + data: Data::try_from(vec![invalid_type; TOKEN_HOLDING_DATA_SIZE]) + .expect("Invalid data"), ..Account::default() }, is_authorized: true, @@ -714,213 +874,214 @@ mod tests { let _post_states = transfer(&pre_states, 10); } - #[should_panic(expected = "Invalid sender data")] - #[test] - fn test_transfer_invalid_data_size_should_fail_1() { - let pre_states = vec![ - AccountWithMetadata { - account: Account { - // Data must be of exact length `TOKEN_HOLDING_DATA_SIZE` - data: vec![1; TOKEN_HOLDING_DATA_SIZE - 1], - ..Account::default() + /* + #[should_panic(expected = "Invalid sender data")] + #[test] + fn test_transfer_invalid_data_size_should_fail_1() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + // Data must be of exact length `TOKEN_HOLDING_DATA_SIZE` + data: vec![1; TOKEN_HOLDING_DATA_SIZE - 1], + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([1; 32]), }, - is_authorized: true, - account_id: AccountId::new([1; 32]), - }, - AccountWithMetadata { - account: Account::default(), - is_authorized: true, - account_id: AccountId::new([2; 32]), - }, - ]; - let _post_states = transfer(&pre_states, 10); - } + AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([2; 32]), + }, + ]; + let _post_states = transfer(&pre_states, 10); + } - #[should_panic(expected = "Invalid sender data")] - #[test] - fn test_transfer_invalid_data_size_should_fail_2() { - let pre_states = vec![ - AccountWithMetadata { - account: Account { - // Data must be of exact length `TOKEN_HOLDING_DATA_SIZE` - data: vec![1; TOKEN_HOLDING_DATA_SIZE + 1], - ..Account::default() + #[should_panic(expected = "Invalid sender data")] + #[test] + fn test_transfer_invalid_data_size_should_fail_2() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + // Data must be of exact length `TOKEN_HOLDING_DATA_SIZE` + data: vec![1; TOKEN_HOLDING_DATA_SIZE + 1], + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([1; 32]), }, - is_authorized: true, - account_id: AccountId::new([1; 32]), - }, - AccountWithMetadata { - account: Account::default(), - is_authorized: true, - account_id: AccountId::new([2; 32]), - }, - ]; - let _post_states = transfer(&pre_states, 10); - } + AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([2; 32]), + }, + ]; + let _post_states = transfer(&pre_states, 10); + } - #[should_panic(expected = "Sender and recipient definition id mismatch")] - #[test] - fn test_transfer_with_different_definition_ids_should_fail() { - let pre_states = vec![ - AccountWithMetadata { - account: Account { - data: vec![1; TOKEN_HOLDING_DATA_SIZE], - ..Account::default() + #[should_panic(expected = "Sender and recipient definition id mismatch")] + #[test] + fn test_transfer_with_different_definition_ids_should_fail() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + data: vec![1; TOKEN_HOLDING_DATA_SIZE], + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([1; 32]), }, - is_authorized: true, - account_id: AccountId::new([1; 32]), - }, - AccountWithMetadata { - account: Account { - data: vec![1] - .into_iter() - .chain(vec![2; TOKEN_HOLDING_DATA_SIZE - 1]) - .collect(), - ..Account::default() + AccountWithMetadata { + account: Account { + data: vec![1] + .into_iter() + .chain(vec![2; TOKEN_HOLDING_DATA_SIZE - 1]) + .collect(), + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([2; 32]), }, - is_authorized: true, - account_id: AccountId::new([2; 32]), - }, - ]; - let _post_states = transfer(&pre_states, 10); - } + ]; + let _post_states = transfer(&pre_states, 10); + } - #[should_panic(expected = "Insufficient balance")] - #[test] - fn test_transfer_with_insufficient_balance_should_fail() { - let pre_states = vec![ - AccountWithMetadata { - account: Account { - // Account with balance 37 - data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] - .into_iter() - .chain(u128::to_le_bytes(37)) - .collect(), - ..Account::default() + #[should_panic(expected = "Insufficient balance")] + #[test] + fn test_transfer_with_insufficient_balance_should_fail() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + // Account with balance 37 + data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] + .into_iter() + .chain(u128::to_le_bytes(37)) + .collect(), + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([1; 32]), }, - is_authorized: true, - account_id: AccountId::new([1; 32]), - }, - AccountWithMetadata { - account: Account { - data: vec![1; TOKEN_HOLDING_DATA_SIZE], - ..Account::default() + AccountWithMetadata { + account: Account { + data: vec![1; TOKEN_HOLDING_DATA_SIZE], + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([2; 32]), }, - is_authorized: true, - account_id: AccountId::new([2; 32]), - }, - ]; - // Attempt to transfer 38 tokens - let _post_states = transfer(&pre_states, 38); - } + ]; + // Attempt to transfer 38 tokens + let _post_states = transfer(&pre_states, 38); + } - #[should_panic(expected = "Sender authorization is missing")] - #[test] - fn test_transfer_without_sender_authorization_should_fail() { - let pre_states = vec![ - AccountWithMetadata { - account: Account { - // Account with balance 37 - data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] - .into_iter() - .chain(u128::to_le_bytes(37)) - .collect(), - ..Account::default() + #[should_panic(expected = "Sender authorization is missing")] + #[test] + fn test_transfer_without_sender_authorization_should_fail() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + // Account with balance 37 + data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] + .into_iter() + .chain(u128::to_le_bytes(37)) + .collect(), + ..Account::default() + }, + is_authorized: false, + account_id: AccountId::new([1; 32]), }, - is_authorized: false, - account_id: AccountId::new([1; 32]), - }, - AccountWithMetadata { - account: Account { - data: vec![1; TOKEN_HOLDING_DATA_SIZE], - ..Account::default() + AccountWithMetadata { + account: Account { + data: vec![1; TOKEN_HOLDING_DATA_SIZE], + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([2; 32]), }, - is_authorized: true, - account_id: AccountId::new([2; 32]), - }, - ]; - let _post_states = transfer(&pre_states, 37); - } + ]; + let _post_states = transfer(&pre_states, 37); + } - #[test] - fn test_transfer_with_valid_inputs_succeeds() { - let pre_states = vec![ - AccountWithMetadata { - account: Account { - // Account with balance 37 - data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] - .into_iter() - .chain(u128::to_le_bytes(37)) - .collect(), - ..Account::default() + #[test] + fn test_transfer_with_valid_inputs_succeeds() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + // Account with balance 37 + data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] + .into_iter() + .chain(u128::to_le_bytes(37)) + .collect(), + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([1; 32]), }, - is_authorized: true, - account_id: AccountId::new([1; 32]), - }, - AccountWithMetadata { - account: Account { - // Account with balance 255 - data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] - .into_iter() - .chain(u128::to_le_bytes(255)) - .collect(), - ..Account::default() + AccountWithMetadata { + account: Account { + // Account with balance 255 + data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] + .into_iter() + .chain(u128::to_le_bytes(255)) + .collect(), + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([2; 32]), }, - is_authorized: true, - account_id: AccountId::new([2; 32]), - }, - ]; - let post_states = transfer(&pre_states, 11); - let [sender_post, recipient_post] = post_states.try_into().ok().unwrap(); - assert_eq!( - sender_post.account().data, - vec![ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] - ); - assert_eq!( - recipient_post.account().data, - vec![ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 10, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] - ); - } + ]; + let post_states = transfer(&pre_states, 11); + let [sender_post, recipient_post] = post_states.try_into().ok().unwrap(); + assert_eq!( + sender_post.account().data, + vec![ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + assert_eq!( + recipient_post.account().data, + vec![ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 10, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + } - #[test] - fn test_token_initialize_account_succeeds() { - let pre_states = vec![ - AccountWithMetadata { - account: Account { - // Definition ID with - data: [0; TOKEN_DEFINITION_DATA_SIZE - 16] - .into_iter() - .chain(u128::to_le_bytes(1000)) - .collect(), - ..Account::default() + #[test] + fn test_token_initialize_account_succeeds() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + // Definition ID with + data: [0; TOKEN_DEFINITION_DATA_SIZE - 16] + .into_iter() + .chain(u128::to_le_bytes(1000)) + .collect(), + ..Account::default() + }, + is_authorized: false, + account_id: AccountId::new([1; 32]), }, - is_authorized: false, - account_id: AccountId::new([1; 32]), - }, - AccountWithMetadata { - account: Account::default(), - is_authorized: false, - account_id: AccountId::new([2; 32]), - }, - ]; - let post_states = initialize_account(&pre_states); - let [definition, holding] = post_states.try_into().ok().unwrap(); - assert_eq!(definition.account().data, pre_states[0].account.data); - assert_eq!( - holding.account().data, - vec![ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] - ); - } - + AccountWithMetadata { + account: Account::default(), + is_authorized: false, + account_id: AccountId::new([2; 32]), + }, + ]; + let post_states = initialize_account(&pre_states); + let [definition, holding] = post_states.try_into().ok().unwrap(); + assert_eq!(definition.account().data, pre_states[0].account.data); + assert_eq!( + holding.account().data, + vec![ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + } + */ enum BalanceEnum { InitSupply, HoldingBalance, @@ -948,12 +1109,14 @@ mod tests { DefinitionAccountMint, HoldingSameDefMint, HoldingSameDefAuthLargeBalance, + DefinitionAccountAuthNotMintable, } enum IdEnum { PoolDefinitionId, PoolDefinitionIdDiff, HoldingId, + MetadataId, } fn helper_account_constructor(selection: AccountsEnum) -> AccountWithMetadata { @@ -966,7 +1129,7 @@ mod tests { account_type: TOKEN_DEFINITION_TYPE, name: [2; 6], total_supply: helper_balance_constructor(BalanceEnum::InitSupply), - metadata_id: AccountId::new([0;32]), + metadata_id: helper_id_constructor(IdEnum::MetadataId), }), nonce: 0, }, @@ -981,7 +1144,7 @@ mod tests { account_type: TOKEN_DEFINITION_TYPE, name: [2; 6], total_supply: helper_balance_constructor(BalanceEnum::InitSupply), - metadata_id: AccountId::new([0;32]), + metadata_id: helper_id_constructor(IdEnum::MetadataId), }), nonce: 0, }, @@ -1052,7 +1215,7 @@ mod tests { account_type: TOKEN_DEFINITION_TYPE, name: [2; 6], total_supply: helper_balance_constructor(BalanceEnum::InitSupplyBurned), - metadata_id: AccountId::new([0;32]), + metadata_id: helper_id_constructor(IdEnum::MetadataId), }), nonce: 0, }, @@ -1114,7 +1277,7 @@ mod tests { account_type: TOKEN_DEFINITION_TYPE, name: [2; 6], total_supply: helper_balance_constructor(BalanceEnum::InitSupplyMint), - metadata_id: AccountId::new([0;32]), + metadata_id: helper_id_constructor(IdEnum::MetadataId), }), nonce: 0, }, @@ -1135,6 +1298,23 @@ mod tests { is_authorized: true, account_id: helper_id_constructor(IdEnum::PoolDefinitionId), }, + AccountsEnum::DefinitionAccountAuthNotMintable => AccountWithMetadata { + account: Account { + program_owner: [5u32; 8], + balance: 0u128, + data: TokenDefinition::into_data(TokenDefinition { + account_type: helper_token_standard_constructor( + TokenStandard::NonFungibleHolding, + ), + name: [2; 6], + total_supply: helper_balance_constructor(BalanceEnum::InitSupplyMint), + metadata_id: helper_id_constructor(IdEnum::MetadataId), + }), + nonce: 0, + }, + is_authorized: true, + account_id: helper_id_constructor(IdEnum::PoolDefinitionId), + }, _ => panic!("Invalid selection"), } } @@ -1160,6 +1340,7 @@ mod tests { IdEnum::PoolDefinitionId => AccountId::new([15; 32]), IdEnum::PoolDefinitionIdDiff => AccountId::new([16; 32]), IdEnum::HoldingId => AccountId::new([17; 32]), + IdEnum::MetadataId => AccountId::new([42; 32]), } } @@ -1176,7 +1357,7 @@ mod tests { } #[test] - #[should_panic(expected = "Mismatch token definition and token holding")] + #[should_panic(expected = "Mismatch Token Definition and Token Holding")] fn test_burn_mismatch_def() { let pre_states = vec![ helper_account_constructor(AccountsEnum::DefinitionAccountAuth), @@ -1290,7 +1471,7 @@ mod tests { } #[test] - #[should_panic(expected = "Mismatch token definition and token holding")] + #[should_panic(expected = "Mismatch Token Definition and Token Holding")] fn test_mint_mismatched_token_definition() { let pre_states = vec![ helper_account_constructor(AccountsEnum::DefinitionAccountAuth), @@ -1375,4 +1556,19 @@ mod tests { helper_balance_constructor(BalanceEnum::MintOverflow), ); } + + #[test] + #[should_panic( + expected = "Token Definition's standard does not permit minting additional supply" + )] + fn test_mint_cannot_mint_unmintable_tokens() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::DefinitionAccountAuthNotMintable), + helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth), + ]; + let _post_states = mint_additional_supply( + &pre_states, + helper_balance_constructor(BalanceEnum::MintSuccess), + ); + } }