From 97f2e93f8a96046cd3e0275ec1d7b939ee91a4fc Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:55:12 -0500 Subject: [PATCH 01/10] initialize branch --- nssa/program_methods/guest/src/bin/token.rs | 89 +++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/nssa/program_methods/guest/src/bin/token.rs b/nssa/program_methods/guest/src/bin/token.rs index 821438a..8050d34 100644 --- a/nssa/program_methods/guest/src/bin/token.rs +++ b/nssa/program_methods/guest/src/bin/token.rs @@ -226,6 +226,54 @@ fn initialize_account(pre_states: &[AccountWithMetadata]) -> Vec { vec![definition_post, account_to_initialize_post] } +fn burn(pre_states: &[AccountWithMetadata], balance_to_burn: u128) -> Vec { + + if pre_states.len() != 2 { + panic!("Invalid number of accounts"); + } + + let definition = &pre_states[0]; + let user_holding = &pre_states[1]; + + let definition_values = + TokenDefinition::parse(&definition.account.data).expect("Definition account must be valid"); + let user_values = + TokenHolding::parse(&user_holding.account.data).expect("Token Holding account must be valid"); + + if definition.account_id != user_values.definition_id { + panic!("Mismatch token definition and token holding"); + } + + if !user_holding.is_authorized { + panic!("Authorization is missing"); + } + + if user_values.balance < balance_to_burn { + panic!("Insufficient balance to burn"); + } + + let mut post_user_holding = user_holding.account.clone(); + let mut post_definition = definition.account.clone(); + + post_user_holding.data = TokenHolding::into_data( + TokenHolding { + account_type: user_values.account_type, + definition_id: user_values.definition_id, + balance: user_values.balance - balance_to_burn, + } + ); + + post_definition.data = TokenDefinition::into_data( + TokenDefinition { + account_type: definition_values.account_type, + name: definition_values.name, + total_supply: definition_values.total_supply - balance_to_burn, + } + ); + + vec![post_definition, post_user_holding] +} + type Instruction = [u8; 23]; fn main() { @@ -273,6 +321,21 @@ fn main() { let post_states = initialize_account(&pre_states); (pre_states, post_states) } + 3 => { + let balance_to_burn = u128::from_le_bytes( + instruction[1..17] + .try_into() + .expect("Balance to burn must be 16 bytes little-endian"), + ); + let name: [u8; 6] = instruction[17..] + .try_into() + .expect("Name must be 6 bytes long"); + assert_eq!(name, [0; 6]); + + // Execute + let post_states = burn(&pre_states, balance_to_burn); + (pre_states, post_states) + } _ => panic!("Invalid instruction"), }; @@ -666,4 +729,30 @@ mod tests { ] ); } + + // TODO: create a burn test + /* + #[test] + fn test_token_burn_mismatch() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + + }, + is_authorized: false, + account_id: AccountId::new([1;32]); + }, + AccountWithMetadata { + account: Account { + account_type: + }, + is_authorized: true, + account_id: AccountId::new([2;32]); + } + ]; + } + */ + // Mismatch token types + // Insufficient balance + // Successful } From c6cde352035f8b5915099df455a9b3df2e693182 Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Fri, 5 Dec 2025 21:01:36 -0500 Subject: [PATCH 02/10] added burn/mint and tests --- nssa/program_methods/guest/src/bin/token.rs | 459 +++++++++++++++++++- 1 file changed, 437 insertions(+), 22 deletions(-) diff --git a/nssa/program_methods/guest/src/bin/token.rs b/nssa/program_methods/guest/src/bin/token.rs index 8050d34..830c53d 100644 --- a/nssa/program_methods/guest/src/bin/token.rs +++ b/nssa/program_methods/guest/src/bin/token.rs @@ -23,6 +23,16 @@ use nssa_core::{ // * Two accounts: [definition_account, account_to_initialize]. // * An dummy byte string of length 23, with the following layout // [0x02 || 0x00 || 0x00 || 0x00 || ... || 0x00 || 0x00]. +// 4. Burn tokens from a Toking Holding account (thus lowering total supply) +// Arguments to this function are: +// * Two accounts: [definition_account, holding_account]. +// * An instruction data byte string of length 23, indicating the balance to burn with the folloiwng layout +// [0x03 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00]. +// 5. Mint additional supply of tokens tokens to a Toking Holding account (thus increasing total supply) +// Arguments to this function are: +// * Two accounts: [definition_account, holding_account]. +// * 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]. const TOKEN_DEFINITION_TYPE: u8 = 0; const TOKEN_DEFINITION_DATA_SIZE: usize = 23; @@ -274,6 +284,58 @@ fn burn(pre_states: &[AccountWithMetadata], balance_to_burn: u128) -> Vec Vec { + if pre_states.len() != 2 { + panic!("Invalid number of accounts"); + } + + let definition = &pre_states[0]; + let token_holding = &pre_states[1]; + + if !definition.is_authorized { + panic!("Definition authorization is missing"); + } + + let definition_values = + TokenDefinition::parse(&definition.account.data).expect("Definition account must be valid"); + + let mut token_holding_post = token_holding.account.clone(); + + //TODO: add overflow protection + // TokenDefinition.supply_limit + amount_to_mint + + let token_holding_values: TokenHolding = if token_holding.account == Account::default() { + TokenHolding::new(&definition.account_id) + } else { TokenHolding::parse(&token_holding.account.data).expect("Holding account must be valid") }; + + if definition.account_id != token_holding_values.definition_id { + panic!("Mismatch token definition and token holding"); + } + + let mut post_definition = definition.account.clone(); + + let mut token_holding_post = token_holding.account.clone(); + + token_holding_post.data = TokenHolding::into_data( + TokenHolding { + account_type: token_holding_values.account_type, + definition_id: token_holding_values.definition_id, + balance: token_holding_values.balance + amount_to_mint, + } + ); + + post_definition.data = TokenDefinition::into_data( + TokenDefinition { + account_type: definition_values.account_type, + name: definition_values.name, + total_supply: definition_values.total_supply + amount_to_mint, + } + ); + + vec![post_definition, token_holding_post] +} + + type Instruction = [u8; 23]; fn main() { @@ -336,6 +398,21 @@ fn main() { let post_states = burn(&pre_states, balance_to_burn); (pre_states, post_states) } + 4 => { + let balance_to_mint = u128::from_le_bytes( + instruction[1..17] + .try_into() + .expect("Balance to burn must be 16 bytes little-endian"), + ); + let name: [u8; 6] = instruction[17..] + .try_into() + .expect("Name must be 6 bytes long"); + assert_eq!(name, [0; 6]); + + // Execute + let post_states = mint_additional_supply(&pre_states, balance_to_mint); + (pre_states, post_states) + } _ => panic!("Invalid instruction"), }; @@ -348,7 +425,8 @@ mod tests { use crate::{ TOKEN_DEFINITION_DATA_SIZE, TOKEN_HOLDING_DATA_SIZE, TOKEN_HOLDING_TYPE, - initialize_account, new_definition, transfer, + TOKEN_DEFINITION_TYPE, TokenDefinition, TokenHolding, + initialize_account, new_definition, transfer, burn, mint_additional_supply, }; #[should_panic(expected = "Invalid number of input accounts")] @@ -730,29 +808,366 @@ mod tests { ); } - // TODO: create a burn test - /* - #[test] - fn test_token_burn_mismatch() { - let pre_states = vec![ - AccountWithMetadata { - account: Account { + enum BalanceEnum { + init_supply, + holding_balance, + init_supply_burned, + holding_balance_burned, + burn_success, + burn_insufficient, + mint_success, + init_supply_mint, + holding_balance_mint, + } - }, - is_authorized: false, - account_id: AccountId::new([1;32]); - }, - AccountWithMetadata { + enum AccountsEnum { + definition_account_auth, + definition_account_not_auth, + holding_diff_def, + holding_same_def_auth, + holding_same_def_not_auth, + definition_account_post_burn, + holding_account_post_burn, + uninit, + init_mint, + definition_account_mint, + holding_same_def_mint, + } + + enum IdEnum { + pool_definition_id, + pool_definition_id_diff, + holding_id, + } + + fn helper_account_constructor(selection: AccountsEnum) -> AccountWithMetadata{ + match selection { + AccountsEnum::definition_account_auth => AccountWithMetadata { account: Account { - account_type: + program_owner: [5u32;8], + balance: 0u128, + data: TokenDefinition::into_data( + TokenDefinition { + account_type: TOKEN_DEFINITION_TYPE, + name: [2; 6], + total_supply: helper_balance_constructor(BalanceEnum::init_supply), + }), + nonce: 0, }, is_authorized: true, - account_id: AccountId::new([2;32]); - } - ]; + account_id: helper_id_constructor(IdEnum::pool_definition_id), + }, + AccountsEnum::definition_account_not_auth => AccountWithMetadata { + account: Account { + program_owner: [5u32; 8], + balance: 0u128, + data: TokenDefinition::into_data( + TokenDefinition { + account_type: TOKEN_DEFINITION_TYPE, + name: [2; 6], + total_supply: helper_balance_constructor(BalanceEnum::init_supply), + }), + nonce: 0, + }, + is_authorized: false, + account_id: helper_id_constructor(IdEnum::pool_definition_id), + }, + AccountsEnum::holding_diff_def => AccountWithMetadata { + account: Account { + program_owner: [5u32;8], + balance: 0u128, + data: TokenHolding::into_data( + TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::pool_definition_id_diff), + balance: helper_balance_constructor(BalanceEnum::holding_balance), + } + ), + nonce: 0, + }, + is_authorized: true, + account_id: helper_id_constructor(IdEnum::holding_id), + }, + AccountsEnum::holding_same_def_auth => AccountWithMetadata { + account: Account { + program_owner: [5u32;8], + balance: 0u128, + data: TokenHolding::into_data( + TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::pool_definition_id), + balance: helper_balance_constructor(BalanceEnum::holding_balance), + } + ), + nonce: 0, + }, + is_authorized: true, + account_id: helper_id_constructor(IdEnum::holding_id), + }, + AccountsEnum::holding_same_def_not_auth => AccountWithMetadata { + account: Account { + program_owner: [5u32;8], + balance: 0u128, + data: TokenHolding::into_data( + TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::pool_definition_id), + balance: helper_balance_constructor(BalanceEnum::holding_balance), + } + ), + nonce: 0, + }, + is_authorized: false, + account_id: helper_id_constructor(IdEnum::holding_id), + }, + AccountsEnum::definition_account_post_burn => AccountWithMetadata { + account: Account { + program_owner: [5u32;8], + balance: 0u128, + data: TokenDefinition::into_data( + TokenDefinition { + account_type: TOKEN_DEFINITION_TYPE, + name: [2; 6], + total_supply: helper_balance_constructor(BalanceEnum::init_supply_burned), + }), + nonce: 0, + }, + is_authorized: true, + account_id: helper_id_constructor(IdEnum::pool_definition_id), + }, + AccountsEnum::holding_same_def_auth => AccountWithMetadata { + account: Account { + program_owner: [5u32;8], + balance: 0u128, + data: TokenHolding::into_data( + TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::pool_definition_id), + balance: helper_balance_constructor(BalanceEnum::holding_balance), + } + ), + nonce: 0, + }, + is_authorized: true, + account_id: helper_id_constructor(IdEnum::holding_id), + }, + AccountsEnum::holding_account_post_burn => AccountWithMetadata { + account: Account { + program_owner: [5u32;8], + balance: 0u128, + data: TokenHolding::into_data( + TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::pool_definition_id), + balance: helper_balance_constructor(BalanceEnum::holding_balance_burned), + } + ), + nonce: 0, + }, + is_authorized: false, + account_id: helper_id_constructor(IdEnum::holding_id), + }, + AccountsEnum::uninit => AccountWithMetadata { + account: Account::default(), + is_authorized: false, + account_id: helper_id_constructor(IdEnum::holding_id), + }, + AccountsEnum::init_mint => AccountWithMetadata { + account: Account { + program_owner: [0u32;8], + balance: 0u128, + data: TokenHolding::into_data( + TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::pool_definition_id), + balance: helper_balance_constructor(BalanceEnum::mint_success), + } + ), + nonce: 0, + }, + is_authorized: false, + account_id: helper_id_constructor(IdEnum::holding_id), + }, + AccountsEnum::holding_same_def_mint => AccountWithMetadata { + account: Account { + program_owner: [5u32;8], + balance: 0u128, + data: TokenHolding::into_data( + TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::pool_definition_id), + balance: helper_balance_constructor(BalanceEnum::holding_balance_mint), + } + ), + nonce: 0, + }, + is_authorized: true, + account_id: helper_id_constructor(IdEnum::pool_definition_id), + }, + AccountsEnum::definition_account_mint => AccountWithMetadata { + account: Account { + program_owner: [5u32;8], + balance: 0u128, + data: TokenDefinition::into_data( + TokenDefinition { + account_type: TOKEN_DEFINITION_TYPE, + name: [2; 6], + total_supply: helper_balance_constructor(BalanceEnum::init_supply_mint), + }), + nonce: 0, + }, + is_authorized: true, + account_id: helper_id_constructor(IdEnum::pool_definition_id), + }, + _ => panic!("Invalid selection") + } } - */ - // Mismatch token types - // Insufficient balance - // Successful -} + + fn helper_balance_constructor(selection: BalanceEnum) -> u128 { + match selection { + BalanceEnum::init_supply => 100_000, + BalanceEnum::holding_balance => 1_000, + BalanceEnum::init_supply_burned => 99_500, + BalanceEnum::holding_balance_burned => 500, + BalanceEnum::burn_success => 500, + BalanceEnum::burn_insufficient => 1_500, + BalanceEnum::mint_success => 50_000, + BalanceEnum::init_supply_mint => 150_000, + BalanceEnum::holding_balance_mint => 51_000, + _ => panic!("Invalid selection") + } + } + + fn helper_id_constructor(selection: IdEnum) -> AccountId { + match selection { + IdEnum::pool_definition_id => AccountId::new([15;32]), + IdEnum::pool_definition_id_diff => AccountId::new([16;32]), + IdEnum::holding_id => AccountId::new([17;32]), + } + } + + #[test] + #[should_panic(expected = "Invalid number of accounts")] + fn test_burn_invalid_number_of_accounts() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::definition_account_auth), + ]; + let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::burn_success)); + } + + #[test] + #[should_panic(expected = "Mismatch token definition and token holding")] + fn test_burn_mismatch_def() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::definition_account_auth), + helper_account_constructor(AccountsEnum::holding_diff_def), + ]; + let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::burn_success)); + } + + #[test] + #[should_panic(expected = "Authorization is missing")] + fn test_burn_missing_authorization() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::definition_account_auth), + helper_account_constructor(AccountsEnum::holding_same_def_not_auth), + ]; + let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::burn_success)); + } + + #[test] + #[should_panic(expected = "Insufficient balance to burn")] + fn test_burn_insufficient_balance() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::definition_account_auth), + helper_account_constructor(AccountsEnum::holding_same_def_auth), + ]; + let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::burn_insufficient)); + } + + #[test] + fn test_burn_success() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::definition_account_auth), + helper_account_constructor(AccountsEnum::holding_same_def_auth), + ]; + let post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::burn_success)); + + let def_post = post_states[0].clone(); + let holding_post = post_states[1].clone(); + + assert!(def_post == helper_account_constructor(AccountsEnum::definition_account_post_burn).account); + assert!(holding_post == helper_account_constructor(AccountsEnum::holding_account_post_burn).account); + } + + #[test] + #[should_panic(expected = "Invalid number of accounts")] + fn test_mint_invalid_number_of_accounts() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::definition_account_auth), + ]; + let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::mint_success)); + } + + #[test] + #[should_panic(expected = "Holding account must be valid")] + fn test_mint_not_valid_holding_account() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::definition_account_auth), + helper_account_constructor(AccountsEnum::definition_account_not_auth), + ]; + let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::mint_success)); + } + + #[test] + #[should_panic(expected = "Definition authorization is missing")] + fn test_mint_missing_authorization() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::definition_account_not_auth), + helper_account_constructor(AccountsEnum::holding_same_def_not_auth), + ]; + let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::mint_success)); + } + + #[test] + #[should_panic(expected = "Mismatch token definition and token holding")] + fn test_mint_mismatched_token_definition() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::definition_account_auth), + helper_account_constructor(AccountsEnum::holding_diff_def), + ]; + let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::mint_success)); + } + + #[test] + fn test_mint_success() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::definition_account_auth), + helper_account_constructor(AccountsEnum::holding_same_def_not_auth), + ]; + let post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::mint_success)); + + let def_post = post_states[0].clone(); + let holding_post = post_states[1].clone(); + + assert!(def_post == helper_account_constructor(AccountsEnum::definition_account_mint).account); + assert!(holding_post == helper_account_constructor(AccountsEnum::holding_same_def_mint).account); + } + + #[test] + fn test_mint_uninit_holding_success() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::definition_account_auth), + helper_account_constructor(AccountsEnum::uninit), + ]; + let post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::mint_success)); + + let def_post = post_states[0].clone(); + let holding_post = post_states[1].clone(); + + assert!(def_post == helper_account_constructor(AccountsEnum::definition_account_mint).account); + assert!(holding_post == helper_account_constructor(AccountsEnum::init_mint).account); + } + + +} \ No newline at end of file From 5a1d37d607bcfc6c1aa51babb908ed64da9426d9 Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Sun, 7 Dec 2025 20:29:25 -0500 Subject: [PATCH 03/10] update burn/mint to use AccountPostState --- nssa/program_methods/guest/src/bin/token.rs | 291 ++++++++++---------- 1 file changed, 142 insertions(+), 149 deletions(-) diff --git a/nssa/program_methods/guest/src/bin/token.rs b/nssa/program_methods/guest/src/bin/token.rs index 9884046..c229441 100644 --- a/nssa/program_methods/guest/src/bin/token.rs +++ b/nssa/program_methods/guest/src/bin/token.rs @@ -230,7 +230,7 @@ fn initialize_account(pre_states: &[AccountWithMetadata]) -> Vec Vec Vec { +fn burn(pre_states: &[AccountWithMetadata], balance_to_burn: u128) -> Vec { if pre_states.len() != 2 { panic!("Invalid number of accounts"); @@ -296,10 +296,14 @@ fn burn(pre_states: &[AccountWithMetadata], balance_to_burn: u128) -> Vec Vec { +fn mint_additional_supply(pre_states: &[AccountWithMetadata], amount_to_mint: u128) -> Vec { if pre_states.len() != 2 { panic!("Invalid number of accounts"); } @@ -314,8 +318,6 @@ fn mint_additional_supply(pre_states: &[AccountWithMetadata], amount_to_mint: u1 let definition_values = TokenDefinition::parse(&definition.account.data).expect("Definition account must be valid"); - let mut token_holding_post = token_holding.account.clone(); - //TODO: add overflow protection // TokenDefinition.supply_limit + amount_to_mint @@ -327,26 +329,35 @@ fn mint_additional_supply(pre_states: &[AccountWithMetadata], amount_to_mint: u1 panic!("Mismatch token definition and token holding"); } - let mut post_definition = definition.account.clone(); - - let mut token_holding_post = token_holding.account.clone(); - - token_holding_post.data = TokenHolding::into_data( - TokenHolding { + let token_holding_post_data = TokenHolding { account_type: token_holding_values.account_type, definition_id: token_holding_values.definition_id, balance: token_holding_values.balance + amount_to_mint, - } - ); + }; - post_definition.data = TokenDefinition::into_data( - TokenDefinition { + let post_definition_data = TokenDefinition { account_type: definition_values.account_type, name: definition_values.name, total_supply: definition_values.total_supply + amount_to_mint, - } - ); + }; + let post_definition = { + let mut this = definition.account.clone(); + this.data = post_definition_data.into_data(); + AccountPostState::new(this) + }; + + let token_holding_post = { + let mut this = token_holding.account.clone(); + this.data = token_holding_post_data.into_data(); + + // Claim the recipient account if it has default program owner + if this.program_owner == DEFAULT_PROGRAM_ID { + AccountPostState::new_claimed(this) + } else { + AccountPostState::new(this) + } + }; vec![post_definition, token_holding_post] } @@ -409,8 +420,7 @@ fn main() { assert_eq!(name, [0; 6]); // Execute - let post_states = burn(&pre_states, balance_to_burn); - (pre_states, post_states) + burn(&pre_states, balance_to_burn) } 4 => { let balance_to_mint = u128::from_le_bytes( @@ -424,8 +434,7 @@ fn main() { assert_eq!(name, [0; 6]); // Execute - let post_states = mint_additional_supply(&pre_states, balance_to_mint); - (pre_states, post_states) + mint_additional_supply(&pre_states, balance_to_mint) } _ => panic!("Invalid instruction"), }; @@ -823,40 +832,40 @@ mod tests { } enum BalanceEnum { - init_supply, - holding_balance, - init_supply_burned, - holding_balance_burned, - burn_success, - burn_insufficient, - mint_success, - init_supply_mint, - holding_balance_mint, + InitSupply, + HoldingBalance, + InitSupplyBurned, + HoldingBalanceBurned, + BurnSuccess, + BurnInsufficient, + MintSuccess, + InitSupplyMint, + HoldingBalanceMint, } enum AccountsEnum { - definition_account_auth, - definition_account_not_auth, - holding_diff_def, - holding_same_def_auth, - holding_same_def_not_auth, - definition_account_post_burn, - holding_account_post_burn, - uninit, - init_mint, - definition_account_mint, - holding_same_def_mint, + DefinitionAccountAuth, + DefinitionAccountNotAuth, + HoldingDiffDef, + HoldingSameDefAuth, + HoldingSameDefNotAuth, + DefinitionAccountPostBurn, + HoldingAccountPostBurn, + Uninit, + InitMint, + DefinitionAccountMint, + HoldingSameDefMint, } enum IdEnum { - pool_definition_id, - pool_definition_id_diff, - holding_id, + PoolDefinitionId, + PoolDefinitionIdDiff, + HoldingId, } fn helper_account_constructor(selection: AccountsEnum) -> AccountWithMetadata{ match selection { - AccountsEnum::definition_account_auth => AccountWithMetadata { + AccountsEnum::DefinitionAccountAuth => AccountWithMetadata { account: Account { program_owner: [5u32;8], balance: 0u128, @@ -864,14 +873,14 @@ mod tests { TokenDefinition { account_type: TOKEN_DEFINITION_TYPE, name: [2; 6], - total_supply: helper_balance_constructor(BalanceEnum::init_supply), + total_supply: helper_balance_constructor(BalanceEnum::InitSupply), }), nonce: 0, }, is_authorized: true, - account_id: helper_id_constructor(IdEnum::pool_definition_id), + account_id: helper_id_constructor(IdEnum::PoolDefinitionId), }, - AccountsEnum::definition_account_not_auth => AccountWithMetadata { + AccountsEnum::DefinitionAccountNotAuth => AccountWithMetadata { account: Account { program_owner: [5u32; 8], balance: 0u128, @@ -879,62 +888,62 @@ mod tests { TokenDefinition { account_type: TOKEN_DEFINITION_TYPE, name: [2; 6], - total_supply: helper_balance_constructor(BalanceEnum::init_supply), + total_supply: helper_balance_constructor(BalanceEnum::InitSupply), }), nonce: 0, }, is_authorized: false, - account_id: helper_id_constructor(IdEnum::pool_definition_id), + account_id: helper_id_constructor(IdEnum::PoolDefinitionId), }, - AccountsEnum::holding_diff_def => AccountWithMetadata { + AccountsEnum::HoldingDiffDef => AccountWithMetadata { account: Account { program_owner: [5u32;8], balance: 0u128, data: TokenHolding::into_data( TokenHolding { account_type: TOKEN_HOLDING_TYPE, - definition_id: helper_id_constructor(IdEnum::pool_definition_id_diff), - balance: helper_balance_constructor(BalanceEnum::holding_balance), + definition_id: helper_id_constructor(IdEnum::PoolDefinitionIdDiff), + balance: helper_balance_constructor(BalanceEnum::HoldingBalance), } ), nonce: 0, }, is_authorized: true, - account_id: helper_id_constructor(IdEnum::holding_id), + account_id: helper_id_constructor(IdEnum::HoldingId), }, - AccountsEnum::holding_same_def_auth => AccountWithMetadata { + AccountsEnum::HoldingSameDefAuth => AccountWithMetadata { account: Account { program_owner: [5u32;8], balance: 0u128, data: TokenHolding::into_data( TokenHolding { account_type: TOKEN_HOLDING_TYPE, - definition_id: helper_id_constructor(IdEnum::pool_definition_id), - balance: helper_balance_constructor(BalanceEnum::holding_balance), + definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), + balance: helper_balance_constructor(BalanceEnum::HoldingBalance), } ), nonce: 0, }, is_authorized: true, - account_id: helper_id_constructor(IdEnum::holding_id), + account_id: helper_id_constructor(IdEnum::HoldingId), }, - AccountsEnum::holding_same_def_not_auth => AccountWithMetadata { + AccountsEnum::HoldingSameDefNotAuth => AccountWithMetadata { account: Account { program_owner: [5u32;8], balance: 0u128, data: TokenHolding::into_data( TokenHolding { account_type: TOKEN_HOLDING_TYPE, - definition_id: helper_id_constructor(IdEnum::pool_definition_id), - balance: helper_balance_constructor(BalanceEnum::holding_balance), + definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), + balance: helper_balance_constructor(BalanceEnum::HoldingBalance), } ), nonce: 0, }, is_authorized: false, - account_id: helper_id_constructor(IdEnum::holding_id), + account_id: helper_id_constructor(IdEnum::HoldingId), }, - AccountsEnum::definition_account_post_burn => AccountWithMetadata { + AccountsEnum::DefinitionAccountPostBurn => AccountWithMetadata { account: Account { program_owner: [5u32;8], balance: 0u128, @@ -942,83 +951,67 @@ mod tests { TokenDefinition { account_type: TOKEN_DEFINITION_TYPE, name: [2; 6], - total_supply: helper_balance_constructor(BalanceEnum::init_supply_burned), + total_supply: helper_balance_constructor(BalanceEnum::InitSupplyBurned), }), nonce: 0, }, is_authorized: true, - account_id: helper_id_constructor(IdEnum::pool_definition_id), + account_id: helper_id_constructor(IdEnum::PoolDefinitionId), }, - AccountsEnum::holding_same_def_auth => AccountWithMetadata { + AccountsEnum::HoldingAccountPostBurn => AccountWithMetadata { account: Account { program_owner: [5u32;8], balance: 0u128, data: TokenHolding::into_data( TokenHolding { account_type: TOKEN_HOLDING_TYPE, - definition_id: helper_id_constructor(IdEnum::pool_definition_id), - balance: helper_balance_constructor(BalanceEnum::holding_balance), - } - ), - nonce: 0, - }, - is_authorized: true, - account_id: helper_id_constructor(IdEnum::holding_id), - }, - AccountsEnum::holding_account_post_burn => AccountWithMetadata { - account: Account { - program_owner: [5u32;8], - balance: 0u128, - data: TokenHolding::into_data( - TokenHolding { - account_type: TOKEN_HOLDING_TYPE, - definition_id: helper_id_constructor(IdEnum::pool_definition_id), - balance: helper_balance_constructor(BalanceEnum::holding_balance_burned), + definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), + balance: helper_balance_constructor(BalanceEnum::HoldingBalanceBurned), } ), nonce: 0, }, is_authorized: false, - account_id: helper_id_constructor(IdEnum::holding_id), + account_id: helper_id_constructor(IdEnum::HoldingId), }, - AccountsEnum::uninit => AccountWithMetadata { + AccountsEnum::Uninit => AccountWithMetadata { account: Account::default(), is_authorized: false, - account_id: helper_id_constructor(IdEnum::holding_id), + account_id: helper_id_constructor(IdEnum::HoldingId), }, - AccountsEnum::init_mint => AccountWithMetadata { + AccountsEnum::InitMint => AccountWithMetadata { account: Account { program_owner: [0u32;8], balance: 0u128, data: TokenHolding::into_data( TokenHolding { account_type: TOKEN_HOLDING_TYPE, - definition_id: helper_id_constructor(IdEnum::pool_definition_id), - balance: helper_balance_constructor(BalanceEnum::mint_success), + definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), + balance: helper_balance_constructor(BalanceEnum::MintSuccess), } ), nonce: 0, }, is_authorized: false, - account_id: helper_id_constructor(IdEnum::holding_id), + account_id: helper_id_constructor(IdEnum::HoldingId), }, - AccountsEnum::holding_same_def_mint => AccountWithMetadata { + AccountsEnum::HoldingSameDefMint => AccountWithMetadata { account: Account { program_owner: [5u32;8], balance: 0u128, data: TokenHolding::into_data( TokenHolding { account_type: TOKEN_HOLDING_TYPE, - definition_id: helper_id_constructor(IdEnum::pool_definition_id), - balance: helper_balance_constructor(BalanceEnum::holding_balance_mint), + definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), + balance: helper_balance_constructor(BalanceEnum::HoldingBalanceMint), } ), nonce: 0, }, is_authorized: true, - account_id: helper_id_constructor(IdEnum::pool_definition_id), + account_id: helper_id_constructor(IdEnum::PoolDefinitionId), }, - AccountsEnum::definition_account_mint => AccountWithMetadata { + AccountsEnum::DefinitionAccountMint => AccountWithMetadata { account: Account { program_owner: [5u32;8], balance: 0u128, @@ -1026,12 +1019,12 @@ mod tests { TokenDefinition { account_type: TOKEN_DEFINITION_TYPE, name: [2; 6], - total_supply: helper_balance_constructor(BalanceEnum::init_supply_mint), + total_supply: helper_balance_constructor(BalanceEnum::InitSupplyMint), }), nonce: 0, }, is_authorized: true, - account_id: helper_id_constructor(IdEnum::pool_definition_id), + account_id: helper_id_constructor(IdEnum::PoolDefinitionId), }, _ => panic!("Invalid selection") } @@ -1039,24 +1032,24 @@ mod tests { fn helper_balance_constructor(selection: BalanceEnum) -> u128 { match selection { - BalanceEnum::init_supply => 100_000, - BalanceEnum::holding_balance => 1_000, - BalanceEnum::init_supply_burned => 99_500, - BalanceEnum::holding_balance_burned => 500, - BalanceEnum::burn_success => 500, - BalanceEnum::burn_insufficient => 1_500, - BalanceEnum::mint_success => 50_000, - BalanceEnum::init_supply_mint => 150_000, - BalanceEnum::holding_balance_mint => 51_000, + BalanceEnum::InitSupply => 100_000, + BalanceEnum::HoldingBalance => 1_000, + BalanceEnum::InitSupplyBurned => 99_500, + BalanceEnum::HoldingBalanceBurned => 500, + BalanceEnum::BurnSuccess => 500, + BalanceEnum::BurnInsufficient => 1_500, + BalanceEnum::MintSuccess => 50_000, + BalanceEnum::InitSupplyMint => 150_000, + BalanceEnum::HoldingBalanceMint => 51_000, _ => panic!("Invalid selection") } } fn helper_id_constructor(selection: IdEnum) -> AccountId { match selection { - IdEnum::pool_definition_id => AccountId::new([15;32]), - IdEnum::pool_definition_id_diff => AccountId::new([16;32]), - IdEnum::holding_id => AccountId::new([17;32]), + IdEnum::PoolDefinitionId => AccountId::new([15;32]), + IdEnum::PoolDefinitionIdDiff => AccountId::new([16;32]), + IdEnum::HoldingId => AccountId::new([17;32]), } } @@ -1064,124 +1057,124 @@ mod tests { #[should_panic(expected = "Invalid number of accounts")] fn test_burn_invalid_number_of_accounts() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::definition_account_auth), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), ]; - let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::burn_success)); + let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::BurnSuccess)); } #[test] #[should_panic(expected = "Mismatch token definition and token holding")] fn test_burn_mismatch_def() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::definition_account_auth), - helper_account_constructor(AccountsEnum::holding_diff_def), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingDiffDef), ]; - let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::burn_success)); + let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::BurnSuccess)); } #[test] #[should_panic(expected = "Authorization is missing")] fn test_burn_missing_authorization() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::definition_account_auth), - helper_account_constructor(AccountsEnum::holding_same_def_not_auth), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth), ]; - let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::burn_success)); + let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::BurnSuccess)); } #[test] #[should_panic(expected = "Insufficient balance to burn")] fn test_burn_insufficient_balance() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::definition_account_auth), - helper_account_constructor(AccountsEnum::holding_same_def_auth), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefAuth), ]; - let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::burn_insufficient)); + let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::BurnInsufficient)); } #[test] fn test_burn_success() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::definition_account_auth), - helper_account_constructor(AccountsEnum::holding_same_def_auth), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefAuth), ]; - let post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::burn_success)); + let post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::BurnSuccess)); let def_post = post_states[0].clone(); let holding_post = post_states[1].clone(); - assert!(def_post == helper_account_constructor(AccountsEnum::definition_account_post_burn).account); - assert!(holding_post == helper_account_constructor(AccountsEnum::holding_account_post_burn).account); + assert!(*def_post.account() == helper_account_constructor(AccountsEnum::DefinitionAccountPostBurn).account); + assert!(*holding_post.account() == helper_account_constructor(AccountsEnum::HoldingAccountPostBurn).account); } #[test] #[should_panic(expected = "Invalid number of accounts")] fn test_mint_invalid_number_of_accounts() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::definition_account_auth), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), ]; - let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::mint_success)); + let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::MintSuccess)); } #[test] #[should_panic(expected = "Holding account must be valid")] fn test_mint_not_valid_holding_account() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::definition_account_auth), - helper_account_constructor(AccountsEnum::definition_account_not_auth), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::DefinitionAccountNotAuth), ]; - let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::mint_success)); + let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::MintSuccess)); } #[test] #[should_panic(expected = "Definition authorization is missing")] fn test_mint_missing_authorization() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::definition_account_not_auth), - helper_account_constructor(AccountsEnum::holding_same_def_not_auth), + helper_account_constructor(AccountsEnum::DefinitionAccountNotAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth), ]; - let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::mint_success)); + let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::MintSuccess)); } #[test] #[should_panic(expected = "Mismatch token definition and token holding")] fn test_mint_mismatched_token_definition() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::definition_account_auth), - helper_account_constructor(AccountsEnum::holding_diff_def), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingDiffDef), ]; - let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::mint_success)); + let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::MintSuccess)); } #[test] fn test_mint_success() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::definition_account_auth), - helper_account_constructor(AccountsEnum::holding_same_def_not_auth), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth), ]; - let post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::mint_success)); + let post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::MintSuccess)); let def_post = post_states[0].clone(); let holding_post = post_states[1].clone(); - assert!(def_post == helper_account_constructor(AccountsEnum::definition_account_mint).account); - assert!(holding_post == helper_account_constructor(AccountsEnum::holding_same_def_mint).account); + assert!(*def_post.account() == helper_account_constructor(AccountsEnum::DefinitionAccountMint).account); + assert!(*holding_post.account() == helper_account_constructor(AccountsEnum::HoldingSameDefMint).account); } #[test] fn test_mint_uninit_holding_success() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::definition_account_auth), - helper_account_constructor(AccountsEnum::uninit), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::Uninit), ]; - let post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::mint_success)); + let post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::MintSuccess)); let def_post = post_states[0].clone(); let holding_post = post_states[1].clone(); - assert!(def_post == helper_account_constructor(AccountsEnum::definition_account_mint).account); - assert!(holding_post == helper_account_constructor(AccountsEnum::init_mint).account); + assert!(*def_post.account() == helper_account_constructor(AccountsEnum::DefinitionAccountMint).account); + assert!(*holding_post.account() == helper_account_constructor(AccountsEnum::InitMint).account); + assert!(holding_post.requires_claim() == true); } - } \ No newline at end of file From e0c6baf1ffff1b9a84d4a25c78fbcb30e1c34540 Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Tue, 9 Dec 2025 08:11:15 -0500 Subject: [PATCH 04/10] added mint overflow protection --- nssa/program_methods/guest/src/bin/token.rs | 24 +++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/nssa/program_methods/guest/src/bin/token.rs b/nssa/program_methods/guest/src/bin/token.rs index c229441..a5aed8b 100644 --- a/nssa/program_methods/guest/src/bin/token.rs +++ b/nssa/program_methods/guest/src/bin/token.rs @@ -318,9 +318,6 @@ fn mint_additional_supply(pre_states: &[AccountWithMetadata], amount_to_mint: u1 let definition_values = TokenDefinition::parse(&definition.account.data).expect("Definition account must be valid"); - //TODO: add overflow protection - // TokenDefinition.supply_limit + amount_to_mint - let token_holding_values: TokenHolding = if token_holding.account == Account::default() { TokenHolding::new(&definition.account_id) } else { TokenHolding::parse(&token_holding.account.data).expect("Holding account must be valid") }; @@ -335,10 +332,15 @@ fn mint_additional_supply(pre_states: &[AccountWithMetadata], amount_to_mint: u1 balance: token_holding_values.balance + amount_to_mint, }; + let post_total_supply = definition_values + .total_supply + .checked_add(amount_to_mint) + .expect("Total supply overflow."); + let post_definition_data = TokenDefinition { account_type: definition_values.account_type, name: definition_values.name, - total_supply: definition_values.total_supply + amount_to_mint, + total_supply: post_total_supply, }; let post_definition = { @@ -841,6 +843,7 @@ mod tests { MintSuccess, InitSupplyMint, HoldingBalanceMint, + MintOverflow, } enum AccountsEnum { @@ -1041,6 +1044,7 @@ mod tests { BalanceEnum::MintSuccess => 50_000, BalanceEnum::InitSupplyMint => 150_000, BalanceEnum::HoldingBalanceMint => 51_000, + BalanceEnum::MintOverflow => (2 as u128).pow(128) - 40_000, _ => panic!("Invalid selection") } } @@ -1177,4 +1181,16 @@ mod tests { assert!(holding_post.requires_claim() == true); } + #[test] + #[should_panic(expected = "Total supply overflow.")] + fn test_mint_overflow() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth), + ]; + let _post_states = mint_additional_supply(&pre_states, + helper_balance_constructor(BalanceEnum::MintOverflow)); + + } + } \ No newline at end of file From 17f77f9ae788946edad66184e9ee82784a1bc566 Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Tue, 9 Dec 2025 13:31:02 -0500 Subject: [PATCH 05/10] Update nssa/program_methods/guest/src/bin/token.rs Co-authored-by: Daniil Polyakov --- nssa/program_methods/guest/src/bin/token.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nssa/program_methods/guest/src/bin/token.rs b/nssa/program_methods/guest/src/bin/token.rs index a5aed8b..bc985cf 100644 --- a/nssa/program_methods/guest/src/bin/token.rs +++ b/nssa/program_methods/guest/src/bin/token.rs @@ -25,7 +25,7 @@ use nssa_core::{ // * Two accounts: [definition_account, account_to_initialize]. // * An dummy byte string of length 23, with the following layout // [0x02 || 0x00 || 0x00 || 0x00 || ... || 0x00 || 0x00]. -// 4. Burn tokens from a Toking Holding account (thus lowering total supply) +// 4. Burn tokens from a Token Holding account (thus lowering total supply) // Arguments to this function are: // * Two accounts: [definition_account, holding_account]. // * An instruction data byte string of length 23, indicating the balance to burn with the folloiwng layout From cf4d7ba80b04b2e3e0a2210cbd68992e44286cb5 Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Tue, 9 Dec 2025 13:31:13 -0500 Subject: [PATCH 06/10] Update nssa/program_methods/guest/src/bin/token.rs Co-authored-by: Daniil Polyakov --- nssa/program_methods/guest/src/bin/token.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nssa/program_methods/guest/src/bin/token.rs b/nssa/program_methods/guest/src/bin/token.rs index bc985cf..50c5d99 100644 --- a/nssa/program_methods/guest/src/bin/token.rs +++ b/nssa/program_methods/guest/src/bin/token.rs @@ -30,7 +30,7 @@ use nssa_core::{ // * Two accounts: [definition_account, holding_account]. // * An instruction data byte string of length 23, indicating the balance to burn with the folloiwng layout // [0x03 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00]. -// 5. Mint additional supply of tokens tokens to a Toking Holding account (thus increasing total supply) +// 5. Mint additional supply of tokens tokens to a Token Holding account (thus increasing total supply) // Arguments to this function are: // * Two accounts: [definition_account, holding_account]. // * An instruction data byte string of length 23, indicating the balance to mint with the folloiwng layout From 8c493a015515bfe6177bcca99f8b5d20246ecb4d Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Tue, 9 Dec 2025 13:31:44 -0500 Subject: [PATCH 07/10] Update nssa/program_methods/guest/src/bin/token.rs Co-authored-by: Daniil Polyakov --- nssa/program_methods/guest/src/bin/token.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nssa/program_methods/guest/src/bin/token.rs b/nssa/program_methods/guest/src/bin/token.rs index 50c5d99..2127f65 100644 --- a/nssa/program_methods/guest/src/bin/token.rs +++ b/nssa/program_methods/guest/src/bin/token.rs @@ -335,7 +335,7 @@ fn mint_additional_supply(pre_states: &[AccountWithMetadata], amount_to_mint: u1 let post_total_supply = definition_values .total_supply .checked_add(amount_to_mint) - .expect("Total supply overflow."); + .expect("Total supply overflow"); let post_definition_data = TokenDefinition { account_type: definition_values.account_type, From 70c228dbecb9f9814eebff1b736ae28f7f213a2e Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Tue, 9 Dec 2025 15:28:32 -0500 Subject: [PATCH 08/10] fixed formatting and added overflow/underflow --- nssa/program_methods/guest/src/bin/token.rs | 448 ++++++++++++-------- 1 file changed, 272 insertions(+), 176 deletions(-) diff --git a/nssa/program_methods/guest/src/bin/token.rs b/nssa/program_methods/guest/src/bin/token.rs index 2127f65..4914b4a 100644 --- a/nssa/program_methods/guest/src/bin/token.rs +++ b/nssa/program_methods/guest/src/bin/token.rs @@ -28,11 +28,13 @@ use nssa_core::{ // 4. Burn tokens from a Token Holding account (thus lowering total supply) // Arguments to this function are: // * Two accounts: [definition_account, holding_account]. +// * Authorization required: holding_account // * An instruction data byte string of length 23, indicating the balance to burn with the folloiwng layout // [0x03 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00]. // 5. Mint additional supply of tokens tokens to a Token Holding account (thus increasing total supply) // Arguments to this function are: // * Two accounts: [definition_account, holding_account]. +// * Authorization required: definition_account // * 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]. @@ -155,7 +157,7 @@ fn transfer(pre_states: &[AccountWithMetadata], balance_to_move: u128) -> Vec Vec Vec { - if pre_states.len() != 2 { panic!("Invalid number of accounts"); } @@ -262,8 +263,8 @@ fn burn(pre_states: &[AccountWithMetadata], balance_to_burn: u128) -> Vec Vec Vec Vec { +fn mint_additional_supply( + pre_states: &[AccountWithMetadata], + amount_to_mint: u128, +) -> Vec { if pre_states.len() != 2 { panic!("Invalid number of accounts"); } - + let definition = &pre_states[0]; let token_holding = &pre_states[1]; @@ -320,27 +322,32 @@ fn mint_additional_supply(pre_states: &[AccountWithMetadata], amount_to_mint: u1 let token_holding_values: TokenHolding = if token_holding.account == Account::default() { TokenHolding::new(&definition.account_id) - } else { TokenHolding::parse(&token_holding.account.data).expect("Holding account must be valid") }; + } else { + TokenHolding::parse(&token_holding.account.data).expect("Holding account must be valid") + }; if definition.account_id != token_holding_values.definition_id { panic!("Mismatch token definition and token holding"); } let token_holding_post_data = TokenHolding { - account_type: token_holding_values.account_type, - definition_id: token_holding_values.definition_id, - balance: token_holding_values.balance + amount_to_mint, + account_type: token_holding_values.account_type, + definition_id: token_holding_values.definition_id, + balance: token_holding_values + .balance + .checked_add(amount_to_mint) + .expect("New balance overflow"), }; let post_total_supply = definition_values - .total_supply - .checked_add(amount_to_mint) - .expect("Total supply overflow"); + .total_supply + .checked_add(amount_to_mint) + .expect("Total supply overflow"); let post_definition_data = TokenDefinition { - account_type: definition_values.account_type, - name: definition_values.name, - total_supply: post_total_supply, + account_type: definition_values.account_type, + name: definition_values.name, + total_supply: post_total_supply, }; let post_definition = { @@ -363,7 +370,6 @@ fn mint_additional_supply(pre_states: &[AccountWithMetadata], amount_to_mint: u1 vec![post_definition, token_holding_post] } - type Instruction = [u8; 23]; fn main() { @@ -449,9 +455,9 @@ mod tests { use nssa_core::account::{Account, AccountId, AccountWithMetadata}; use crate::{ - TOKEN_DEFINITION_DATA_SIZE, TOKEN_HOLDING_DATA_SIZE, TOKEN_HOLDING_TYPE, - TOKEN_DEFINITION_TYPE, TokenDefinition, TokenHolding, - initialize_account, new_definition, transfer, burn, mint_additional_supply, + 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, }; #[should_panic(expected = "Invalid number of input accounts")] @@ -852,12 +858,14 @@ mod tests { HoldingDiffDef, HoldingSameDefAuth, HoldingSameDefNotAuth, + HoldingSameDefNotAuthOverflow, DefinitionAccountPostBurn, HoldingAccountPostBurn, Uninit, InitMint, DefinitionAccountMint, HoldingSameDefMint, + HoldingSameDefAuthLargeBalance, } enum IdEnum { @@ -866,49 +874,45 @@ mod tests { HoldingId, } - fn helper_account_constructor(selection: AccountsEnum) -> AccountWithMetadata{ + fn helper_account_constructor(selection: AccountsEnum) -> AccountWithMetadata { match selection { AccountsEnum::DefinitionAccountAuth => AccountWithMetadata { account: Account { - program_owner: [5u32;8], - balance: 0u128, - data: TokenDefinition::into_data( - TokenDefinition { - account_type: TOKEN_DEFINITION_TYPE, - name: [2; 6], - total_supply: helper_balance_constructor(BalanceEnum::InitSupply), - }), - nonce: 0, + program_owner: [5u32; 8], + balance: 0u128, + data: TokenDefinition::into_data(TokenDefinition { + account_type: TOKEN_DEFINITION_TYPE, + name: [2; 6], + total_supply: helper_balance_constructor(BalanceEnum::InitSupply), + }), + nonce: 0, }, is_authorized: true, account_id: helper_id_constructor(IdEnum::PoolDefinitionId), }, AccountsEnum::DefinitionAccountNotAuth => AccountWithMetadata { account: Account { - program_owner: [5u32; 8], - balance: 0u128, - data: TokenDefinition::into_data( - TokenDefinition { - account_type: TOKEN_DEFINITION_TYPE, - name: [2; 6], - total_supply: helper_balance_constructor(BalanceEnum::InitSupply), - }), - nonce: 0, + program_owner: [5u32; 8], + balance: 0u128, + data: TokenDefinition::into_data(TokenDefinition { + account_type: TOKEN_DEFINITION_TYPE, + name: [2; 6], + total_supply: helper_balance_constructor(BalanceEnum::InitSupply), + }), + nonce: 0, }, is_authorized: false, account_id: helper_id_constructor(IdEnum::PoolDefinitionId), }, AccountsEnum::HoldingDiffDef => AccountWithMetadata { account: Account { - program_owner: [5u32;8], + program_owner: [5u32; 8], balance: 0u128, - data: TokenHolding::into_data( - TokenHolding { - account_type: TOKEN_HOLDING_TYPE, - definition_id: helper_id_constructor(IdEnum::PoolDefinitionIdDiff), - balance: helper_balance_constructor(BalanceEnum::HoldingBalance), - } - ), + data: TokenHolding::into_data(TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::PoolDefinitionIdDiff), + balance: helper_balance_constructor(BalanceEnum::HoldingBalance), + }), nonce: 0, }, is_authorized: true, @@ -916,15 +920,13 @@ mod tests { }, AccountsEnum::HoldingSameDefAuth => AccountWithMetadata { account: Account { - program_owner: [5u32;8], + program_owner: [5u32; 8], balance: 0u128, - data: TokenHolding::into_data( - TokenHolding { - account_type: TOKEN_HOLDING_TYPE, - definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), - balance: helper_balance_constructor(BalanceEnum::HoldingBalance), - } - ), + data: TokenHolding::into_data(TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), + balance: helper_balance_constructor(BalanceEnum::HoldingBalance), + }), nonce: 0, }, is_authorized: true, @@ -932,15 +934,27 @@ mod tests { }, AccountsEnum::HoldingSameDefNotAuth => AccountWithMetadata { account: Account { - program_owner: [5u32;8], + program_owner: [5u32; 8], balance: 0u128, - data: TokenHolding::into_data( - TokenHolding { - account_type: TOKEN_HOLDING_TYPE, - definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), - balance: helper_balance_constructor(BalanceEnum::HoldingBalance), - } - ), + data: TokenHolding::into_data(TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), + balance: helper_balance_constructor(BalanceEnum::HoldingBalance), + }), + nonce: 0, + }, + is_authorized: false, + account_id: helper_id_constructor(IdEnum::HoldingId), + }, + AccountsEnum::HoldingSameDefNotAuthOverflow => AccountWithMetadata { + account: Account { + program_owner: [5u32; 8], + balance: 0u128, + data: TokenHolding::into_data(TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), + balance: helper_balance_constructor(BalanceEnum::InitSupply), + }), nonce: 0, }, is_authorized: false, @@ -948,30 +962,27 @@ mod tests { }, AccountsEnum::DefinitionAccountPostBurn => AccountWithMetadata { account: Account { - program_owner: [5u32;8], - balance: 0u128, - data: TokenDefinition::into_data( - TokenDefinition { - account_type: TOKEN_DEFINITION_TYPE, - name: [2; 6], - total_supply: helper_balance_constructor(BalanceEnum::InitSupplyBurned), - }), - nonce: 0, + program_owner: [5u32; 8], + balance: 0u128, + data: TokenDefinition::into_data(TokenDefinition { + account_type: TOKEN_DEFINITION_TYPE, + name: [2; 6], + total_supply: helper_balance_constructor(BalanceEnum::InitSupplyBurned), + }), + nonce: 0, }, is_authorized: true, account_id: helper_id_constructor(IdEnum::PoolDefinitionId), }, AccountsEnum::HoldingAccountPostBurn => AccountWithMetadata { account: Account { - program_owner: [5u32;8], + program_owner: [5u32; 8], balance: 0u128, - data: TokenHolding::into_data( - TokenHolding { - account_type: TOKEN_HOLDING_TYPE, - definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), - balance: helper_balance_constructor(BalanceEnum::HoldingBalanceBurned), - } - ), + data: TokenHolding::into_data(TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), + balance: helper_balance_constructor(BalanceEnum::HoldingBalanceBurned), + }), nonce: 0, }, is_authorized: false, @@ -984,15 +995,13 @@ mod tests { }, AccountsEnum::InitMint => AccountWithMetadata { account: Account { - program_owner: [0u32;8], + program_owner: [0u32; 8], balance: 0u128, - data: TokenHolding::into_data( - TokenHolding { - account_type: TOKEN_HOLDING_TYPE, - definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), - balance: helper_balance_constructor(BalanceEnum::MintSuccess), - } - ), + data: TokenHolding::into_data(TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), + balance: helper_balance_constructor(BalanceEnum::MintSuccess), + }), nonce: 0, }, is_authorized: false, @@ -1000,36 +1009,47 @@ mod tests { }, AccountsEnum::HoldingSameDefMint => AccountWithMetadata { account: Account { - program_owner: [5u32;8], - balance: 0u128, - data: TokenHolding::into_data( - TokenHolding { - account_type: TOKEN_HOLDING_TYPE, - definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), - balance: helper_balance_constructor(BalanceEnum::HoldingBalanceMint), - } - ), - nonce: 0, + program_owner: [5u32; 8], + balance: 0u128, + data: TokenHolding::into_data(TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), + balance: helper_balance_constructor(BalanceEnum::HoldingBalanceMint), + }), + nonce: 0, }, is_authorized: true, account_id: helper_id_constructor(IdEnum::PoolDefinitionId), }, AccountsEnum::DefinitionAccountMint => AccountWithMetadata { account: Account { - program_owner: [5u32;8], - balance: 0u128, - data: TokenDefinition::into_data( - TokenDefinition { - account_type: TOKEN_DEFINITION_TYPE, - name: [2; 6], - total_supply: helper_balance_constructor(BalanceEnum::InitSupplyMint), - }), - nonce: 0, + program_owner: [5u32; 8], + balance: 0u128, + data: TokenDefinition::into_data(TokenDefinition { + account_type: TOKEN_DEFINITION_TYPE, + name: [2; 6], + total_supply: helper_balance_constructor(BalanceEnum::InitSupplyMint), + }), + nonce: 0, }, is_authorized: true, account_id: helper_id_constructor(IdEnum::PoolDefinitionId), }, - _ => panic!("Invalid selection") + AccountsEnum::HoldingSameDefAuthLargeBalance => AccountWithMetadata { + account: Account { + program_owner: [5u32; 8], + balance: 0u128, + data: TokenHolding::into_data(TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: helper_id_constructor(IdEnum::PoolDefinitionId), + balance: helper_balance_constructor(BalanceEnum::MintOverflow), + }), + nonce: 0, + }, + is_authorized: true, + account_id: helper_id_constructor(IdEnum::PoolDefinitionId), + }, + _ => panic!("Invalid selection"), } } @@ -1045,152 +1065,228 @@ mod tests { BalanceEnum::InitSupplyMint => 150_000, BalanceEnum::HoldingBalanceMint => 51_000, BalanceEnum::MintOverflow => (2 as u128).pow(128) - 40_000, - _ => panic!("Invalid selection") + _ => panic!("Invalid selection"), } } fn helper_id_constructor(selection: IdEnum) -> AccountId { match selection { - IdEnum::PoolDefinitionId => AccountId::new([15;32]), - IdEnum::PoolDefinitionIdDiff => AccountId::new([16;32]), - IdEnum::HoldingId => AccountId::new([17;32]), + IdEnum::PoolDefinitionId => AccountId::new([15; 32]), + IdEnum::PoolDefinitionIdDiff => AccountId::new([16; 32]), + IdEnum::HoldingId => AccountId::new([17; 32]), } } #[test] #[should_panic(expected = "Invalid number of accounts")] fn test_burn_invalid_number_of_accounts() { - let pre_states = vec![ - helper_account_constructor(AccountsEnum::DefinitionAccountAuth), - ]; - let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::BurnSuccess)); + let pre_states = vec![helper_account_constructor( + AccountsEnum::DefinitionAccountAuth, + )]; + let _post_states = burn( + &pre_states, + helper_balance_constructor(BalanceEnum::BurnSuccess), + ); } #[test] #[should_panic(expected = "Mismatch token definition and token holding")] fn test_burn_mismatch_def() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::DefinitionAccountAuth), - helper_account_constructor(AccountsEnum::HoldingDiffDef), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingDiffDef), ]; - let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::BurnSuccess)); + let _post_states = burn( + &pre_states, + helper_balance_constructor(BalanceEnum::BurnSuccess), + ); } #[test] #[should_panic(expected = "Authorization is missing")] fn test_burn_missing_authorization() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::DefinitionAccountAuth), - helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth), ]; - let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::BurnSuccess)); + let _post_states = burn( + &pre_states, + helper_balance_constructor(BalanceEnum::BurnSuccess), + ); } #[test] #[should_panic(expected = "Insufficient balance to burn")] fn test_burn_insufficient_balance() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::DefinitionAccountAuth), - helper_account_constructor(AccountsEnum::HoldingSameDefAuth), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefAuth), ]; - let _post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::BurnInsufficient)); + let _post_states = burn( + &pre_states, + helper_balance_constructor(BalanceEnum::BurnInsufficient), + ); + } + + #[test] + #[should_panic(expected = "Total supply underflow")] + fn test_burn_total_supply_underflow() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefAuthLargeBalance), + ]; + let _post_states = burn( + &pre_states, + helper_balance_constructor(BalanceEnum::MintOverflow), + ); } #[test] fn test_burn_success() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::DefinitionAccountAuth), - helper_account_constructor(AccountsEnum::HoldingSameDefAuth), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefAuth), ]; - let post_states = burn(&pre_states, helper_balance_constructor(BalanceEnum::BurnSuccess)); + let post_states = burn( + &pre_states, + helper_balance_constructor(BalanceEnum::BurnSuccess), + ); let def_post = post_states[0].clone(); let holding_post = post_states[1].clone(); - assert!(*def_post.account() == helper_account_constructor(AccountsEnum::DefinitionAccountPostBurn).account); - assert!(*holding_post.account() == helper_account_constructor(AccountsEnum::HoldingAccountPostBurn).account); + assert!( + *def_post.account() + == helper_account_constructor(AccountsEnum::DefinitionAccountPostBurn).account + ); + assert!( + *holding_post.account() + == helper_account_constructor(AccountsEnum::HoldingAccountPostBurn).account + ); } #[test] #[should_panic(expected = "Invalid number of accounts")] fn test_mint_invalid_number_of_accounts() { - let pre_states = vec![ - helper_account_constructor(AccountsEnum::DefinitionAccountAuth), - ]; - let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::MintSuccess)); + let pre_states = vec![helper_account_constructor( + AccountsEnum::DefinitionAccountAuth, + )]; + let _post_states = mint_additional_supply( + &pre_states, + helper_balance_constructor(BalanceEnum::MintSuccess), + ); } #[test] #[should_panic(expected = "Holding account must be valid")] fn test_mint_not_valid_holding_account() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::DefinitionAccountAuth), - helper_account_constructor(AccountsEnum::DefinitionAccountNotAuth), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::DefinitionAccountNotAuth), ]; - let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::MintSuccess)); + let _post_states = mint_additional_supply( + &pre_states, + helper_balance_constructor(BalanceEnum::MintSuccess), + ); } #[test] #[should_panic(expected = "Definition authorization is missing")] fn test_mint_missing_authorization() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::DefinitionAccountNotAuth), - helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth), + helper_account_constructor(AccountsEnum::DefinitionAccountNotAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth), ]; - let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::MintSuccess)); + let _post_states = mint_additional_supply( + &pre_states, + helper_balance_constructor(BalanceEnum::MintSuccess), + ); } #[test] #[should_panic(expected = "Mismatch token definition and token holding")] fn test_mint_mismatched_token_definition() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::DefinitionAccountAuth), - helper_account_constructor(AccountsEnum::HoldingDiffDef), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingDiffDef), ]; - let _post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::MintSuccess)); + let _post_states = mint_additional_supply( + &pre_states, + helper_balance_constructor(BalanceEnum::MintSuccess), + ); } #[test] fn test_mint_success() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::DefinitionAccountAuth), - helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth), ]; - let post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::MintSuccess)); + let post_states = mint_additional_supply( + &pre_states, + helper_balance_constructor(BalanceEnum::MintSuccess), + ); let def_post = post_states[0].clone(); let holding_post = post_states[1].clone(); - assert!(*def_post.account() == helper_account_constructor(AccountsEnum::DefinitionAccountMint).account); - assert!(*holding_post.account() == helper_account_constructor(AccountsEnum::HoldingSameDefMint).account); + assert!( + *def_post.account() + == helper_account_constructor(AccountsEnum::DefinitionAccountMint).account + ); + assert!( + *holding_post.account() + == helper_account_constructor(AccountsEnum::HoldingSameDefMint).account + ); } #[test] fn test_mint_uninit_holding_success() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::DefinitionAccountAuth), - helper_account_constructor(AccountsEnum::Uninit), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::Uninit), ]; - let post_states = mint_additional_supply(&pre_states, helper_balance_constructor(BalanceEnum::MintSuccess)); + let post_states = mint_additional_supply( + &pre_states, + helper_balance_constructor(BalanceEnum::MintSuccess), + ); let def_post = post_states[0].clone(); let holding_post = post_states[1].clone(); - assert!(*def_post.account() == helper_account_constructor(AccountsEnum::DefinitionAccountMint).account); - assert!(*holding_post.account() == helper_account_constructor(AccountsEnum::InitMint).account); + assert!( + *def_post.account() + == helper_account_constructor(AccountsEnum::DefinitionAccountMint).account + ); + assert!( + *holding_post.account() == helper_account_constructor(AccountsEnum::InitMint).account + ); assert!(holding_post.requires_claim() == true); } #[test] - #[should_panic(expected = "Total supply overflow.")] - fn test_mint_overflow() { + #[should_panic(expected = "Total supply overflow")] + fn test_mint_total_supply_overflow() { let pre_states = vec![ - helper_account_constructor(AccountsEnum::DefinitionAccountAuth), - helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth), + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth), ]; - let _post_states = mint_additional_supply(&pre_states, - helper_balance_constructor(BalanceEnum::MintOverflow)); - + let _post_states = mint_additional_supply( + &pre_states, + helper_balance_constructor(BalanceEnum::MintOverflow), + ); } -} \ No newline at end of file + #[test] + #[should_panic(expected = "New balance overflow")] + fn test_mint_holding_account_overflow() { + let pre_states = vec![ + helper_account_constructor(AccountsEnum::DefinitionAccountAuth), + helper_account_constructor(AccountsEnum::HoldingSameDefNotAuthOverflow), + ]; + let _post_states = mint_additional_supply( + &pre_states, + helper_balance_constructor(BalanceEnum::MintOverflow), + ); + } +} From 340c9c4ce6509782d4d0341360a9ee0e6af70e43 Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Wed, 10 Dec 2025 08:12:17 -0500 Subject: [PATCH 09/10] redundant underflow check added --- nssa/program_methods/guest/src/bin/token.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nssa/program_methods/guest/src/bin/token.rs b/nssa/program_methods/guest/src/bin/token.rs index 4914b4a..8409d76 100644 --- a/nssa/program_methods/guest/src/bin/token.rs +++ b/nssa/program_methods/guest/src/bin/token.rs @@ -284,7 +284,10 @@ fn burn(pre_states: &[AccountWithMetadata], balance_to_burn: u128) -> Vec Date: Fri, 12 Dec 2025 16:27:27 -0500 Subject: [PATCH 10/10] fixed conflicts with token rs from main --- nssa/program_methods/guest/src/bin/token.rs | 98 +++++++++++++-------- 1 file changed, 62 insertions(+), 36 deletions(-) diff --git a/nssa/program_methods/guest/src/bin/token.rs b/nssa/program_methods/guest/src/bin/token.rs index 8409d76..739295b 100644 --- a/nssa/program_methods/guest/src/bin/token.rs +++ b/nssa/program_methods/guest/src/bin/token.rs @@ -57,12 +57,15 @@ struct TokenHolding { } impl TokenDefinition { - fn into_data(self) -> Vec { + fn into_data(self) -> Data { let mut bytes = [0; TOKEN_DEFINITION_DATA_SIZE]; bytes[0] = self.account_type; bytes[1..7].copy_from_slice(&self.name); bytes[7..].copy_from_slice(&self.total_supply.to_le_bytes()); - bytes.into() + bytes + .to_vec() + .try_into() + .expect("23 bytes should fit into Data") } fn parse(data: &[u8]) -> Option { @@ -122,7 +125,10 @@ impl TokenHolding { 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() + bytes + .to_vec() + .try_into() + .expect("33 bytes should fit into Data") } } @@ -376,10 +382,13 @@ fn mint_additional_supply( type Instruction = [u8; 23]; fn main() { - let ProgramInput { - pre_states, - instruction, - } = read_nssa_inputs::(); + let ( + ProgramInput { + pre_states, + instruction, + }, + instruction_words, + ) = read_nssa_inputs::(); let post_states = match instruction[0] { 0 => { @@ -450,7 +459,7 @@ fn main() { _ => panic!("Invalid instruction"), }; - write_nssa_outputs(pre_states, post_states); + write_nssa_outputs(instruction_words, pre_states, post_states); } #[cfg(test)] @@ -562,15 +571,15 @@ mod tests { let post_states = new_definition(&pre_states, [0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe], 10); let [definition_account, holding_account] = post_states.try_into().ok().unwrap(); assert_eq!( - definition_account.account().data, - vec![ + definition_account.account().data.as_ref(), + &[ 0, 0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ); assert_eq!( - holding_account.account().data, - vec![ + holding_account.account().data.as_ref(), + &[ 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 @@ -620,7 +629,9 @@ 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: vec![invalid_type; TOKEN_HOLDING_DATA_SIZE] + .try_into() + .unwrap(), ..Account::default() }, is_authorized: true, @@ -642,7 +653,7 @@ mod tests { AccountWithMetadata { account: Account { // Data must be of exact length `TOKEN_HOLDING_DATA_SIZE` - data: vec![1; TOKEN_HOLDING_DATA_SIZE - 1], + data: vec![1; TOKEN_HOLDING_DATA_SIZE - 1].try_into().unwrap(), ..Account::default() }, is_authorized: true, @@ -664,7 +675,7 @@ mod tests { AccountWithMetadata { account: Account { // Data must be of exact length `TOKEN_HOLDING_DATA_SIZE` - data: vec![1; TOKEN_HOLDING_DATA_SIZE + 1], + data: vec![1; TOKEN_HOLDING_DATA_SIZE + 1].try_into().unwrap(), ..Account::default() }, is_authorized: true, @@ -685,7 +696,7 @@ mod tests { let pre_states = vec![ AccountWithMetadata { account: Account { - data: vec![1; TOKEN_HOLDING_DATA_SIZE], + data: vec![1; TOKEN_HOLDING_DATA_SIZE].try_into().unwrap(), ..Account::default() }, is_authorized: true, @@ -693,10 +704,12 @@ mod tests { }, AccountWithMetadata { account: Account { - data: vec![1] + data: [1] .into_iter() .chain(vec![2; TOKEN_HOLDING_DATA_SIZE - 1]) - .collect(), + .collect::>() + .try_into() + .unwrap(), ..Account::default() }, is_authorized: true, @@ -713,10 +726,12 @@ mod tests { AccountWithMetadata { account: Account { // Account with balance 37 - data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] + data: [1; TOKEN_HOLDING_DATA_SIZE - 16] .into_iter() .chain(u128::to_le_bytes(37)) - .collect(), + .collect::>() + .try_into() + .unwrap(), ..Account::default() }, is_authorized: true, @@ -724,7 +739,7 @@ mod tests { }, AccountWithMetadata { account: Account { - data: vec![1; TOKEN_HOLDING_DATA_SIZE], + data: vec![1; TOKEN_HOLDING_DATA_SIZE].try_into().unwrap(), ..Account::default() }, is_authorized: true, @@ -742,10 +757,12 @@ mod tests { AccountWithMetadata { account: Account { // Account with balance 37 - data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] + data: [1; TOKEN_HOLDING_DATA_SIZE - 16] .into_iter() .chain(u128::to_le_bytes(37)) - .collect(), + .collect::>() + .try_into() + .unwrap(), ..Account::default() }, is_authorized: false, @@ -753,7 +770,7 @@ mod tests { }, AccountWithMetadata { account: Account { - data: vec![1; TOKEN_HOLDING_DATA_SIZE], + data: vec![1; TOKEN_HOLDING_DATA_SIZE].try_into().unwrap(), ..Account::default() }, is_authorized: true, @@ -769,10 +786,12 @@ mod tests { AccountWithMetadata { account: Account { // Account with balance 37 - data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] + data: [1; TOKEN_HOLDING_DATA_SIZE - 16] .into_iter() .chain(u128::to_le_bytes(37)) - .collect(), + .collect::>() + .try_into() + .unwrap(), ..Account::default() }, is_authorized: true, @@ -781,10 +800,12 @@ mod tests { AccountWithMetadata { account: Account { // Account with balance 255 - data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] + data: [1; TOKEN_HOLDING_DATA_SIZE - 16] .into_iter() .chain(u128::to_le_bytes(255)) - .collect(), + .collect::>() + .try_into() + .unwrap(), ..Account::default() }, is_authorized: true, @@ -794,15 +815,15 @@ mod tests { 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![ + sender_post.account().data.as_ref(), + [ 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![ + recipient_post.account().data.as_ref(), + [ 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 ] @@ -818,7 +839,9 @@ mod tests { data: [0; TOKEN_DEFINITION_DATA_SIZE - 16] .into_iter() .chain(u128::to_le_bytes(1000)) - .collect(), + .collect::>() + .try_into() + .unwrap(), ..Account::default() }, is_authorized: false, @@ -832,10 +855,13 @@ mod tests { ]; 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![ + definition.account().data.as_ref(), + pre_states[0].account.data.as_ref() + ); + assert_eq!( + holding.account().data.as_ref(), + [ 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 ]