use helpers::C; use sov_bank::{get_token_address, Bank, BankConfig, CallMessage, Coins, TotalSupplyResponse}; use sov_modules_api::default_context::DefaultContext; use sov_modules_api::utils::generate_address; use sov_modules_api::{Address, Context, Error, Module, WorkingSet}; use sov_state::{DefaultStorageSpec, ProverStorage}; mod helpers; pub type Storage = ProverStorage; #[test] fn mint_token() { let bank = Bank::::default(); let tmpdir = tempfile::tempdir().unwrap(); let mut working_set = WorkingSet::new(ProverStorage::with_path(tmpdir.path()).unwrap()); let empty_bank_config = BankConfig:: { tokens: vec![] }; bank.genesis(&empty_bank_config, &mut working_set).unwrap(); let minter_address = generate_address::("minter"); let minter_context = C::new(minter_address); let salt = 0; let token_name = "Token1".to_owned(); let initial_balance = 100; let token_address = get_token_address::(&token_name, minter_address.as_ref(), salt); // --- // Deploying token let mint_message = CallMessage::CreateToken { salt, token_name: token_name.clone(), initial_balance, minter_address, authorized_minters: vec![minter_address], }; let _minted = bank .call(mint_message, &minter_context, &mut working_set) .expect("Failed to mint token"); // No events at the moment. If there are, needs to be checked assert!(working_set.events().is_empty()); let query_total_supply = |token_address: Address, working_set: &mut WorkingSet| -> Option { let total_supply: TotalSupplyResponse = bank.supply_of(token_address, working_set).unwrap(); total_supply.amount }; let query_user_balance = |user_address: Address, working_set: &mut WorkingSet| -> Option { bank.get_balance_of(user_address, token_address, working_set) }; let previous_total_supply = query_total_supply(token_address, &mut working_set); assert_eq!(Some(initial_balance), previous_total_supply); // ----- // Mint Additional let mint_amount = 10; let new_holder = generate_address::("new_holder"); let mint_message = CallMessage::Mint { coins: Coins { amount: mint_amount, token_address, }, minter_address: new_holder, }; let _minted = bank .call(mint_message.clone(), &minter_context, &mut working_set) .expect("Failed to mint token"); assert!(working_set.events().is_empty()); let total_supply = query_total_supply(token_address, &mut working_set); assert_eq!(Some(initial_balance + mint_amount), total_supply); // check user balance after minting let balance = query_user_balance(new_holder, &mut working_set); assert_eq!(Some(10), balance); // check original token creation balance let bal = query_user_balance(minter_address, &mut working_set); assert_eq!(Some(100), bal); // Mint with an un-authorized user let unauthorized_address = generate_address::("unauthorized_address"); let unauthorized_context = C::new(unauthorized_address); let unauthorized_mint = bank.call(mint_message, &unauthorized_context, &mut working_set); assert!(unauthorized_mint.is_err()); let Error::ModuleError(err) = unauthorized_mint.err().unwrap(); let mut chain = err.chain(); let message_1 = chain.next().unwrap().to_string(); let message_2 = chain.next().unwrap().to_string(); assert!(chain.next().is_none()); assert_eq!( format!( "Failed mint coins(token_address={} amount={}) to {} by authorizer {}", token_address, mint_amount, new_holder, unauthorized_address ), message_1 ); assert_eq!( format!( "Sender {} is not an authorized minter of token {}", unauthorized_address, token_name, ), message_2 ); // Authorized minter test let salt = 0; let token_name = "Token_New".to_owned(); let initial_balance = 100; let token_address = get_token_address::(&token_name, minter_address.as_ref(), salt); let authorized_minter_address_1 = generate_address::("authorized_minter_1"); let authorized_minter_address_2 = generate_address::("authorized_minter_2"); // --- // Deploying token let mint_message = CallMessage::CreateToken { salt, token_name: token_name.clone(), initial_balance, minter_address, authorized_minters: vec![authorized_minter_address_1, authorized_minter_address_2], }; let _minted = bank .call(mint_message, &minter_context, &mut working_set) .expect("Failed to mint token"); // No events at the moment. If there are, needs to be checked assert!(working_set.events().is_empty()); // Try to mint new token with original token creator, in this case minter_context let mint_amount = 10; let new_holder = generate_address::("new_holder_2"); let mint_message = CallMessage::Mint { coins: Coins { amount: mint_amount, token_address, }, minter_address: new_holder, }; let minted = bank.call(mint_message, &minter_context, &mut working_set); assert!(minted.is_err()); let Error::ModuleError(err) = minted.err().unwrap(); let mut chain = err.chain(); let message_1 = chain.next().unwrap().to_string(); let message_2 = chain.next().unwrap().to_string(); assert!(chain.next().is_none()); assert_eq!( format!( "Failed mint coins(token_address={} amount={}) to {} by authorizer {}", token_address, mint_amount, new_holder, minter_address, ), message_1 ); assert_eq!( format!( "Sender {} is not an authorized minter of token {}", minter_address, token_name ), message_2 ); // Try to mint new token with authorized sender 2 let authorized_minter_2_context = C::new(authorized_minter_address_2); let mint_message = CallMessage::Mint { coins: Coins { amount: mint_amount, token_address, }, minter_address: new_holder, }; let _minted = bank .call(mint_message, &authorized_minter_2_context, &mut working_set) .expect("Failed to mint token"); let supply = query_total_supply(token_address, &mut working_set); assert!(working_set.events().is_empty()); assert_eq!(Some(110), supply); // Try to mint new token with authorized sender 1 let authorized_minter_1_context = C::new(authorized_minter_address_1); let mint_message = CallMessage::Mint { coins: Coins { amount: mint_amount, token_address, }, minter_address: new_holder, }; let _minted = bank .call(mint_message, &authorized_minter_1_context, &mut working_set) .expect("Failed to mint token"); let supply = query_total_supply(token_address, &mut working_set); assert!(working_set.events().is_empty()); assert_eq!(Some(120), supply); // Overflow test - account balance let overflow_mint_message = CallMessage::Mint { coins: Coins { amount: u64::MAX, token_address, }, minter_address: new_holder, }; let minted = bank.call( overflow_mint_message, &authorized_minter_1_context, &mut working_set, ); assert!(minted.is_err()); let Error::ModuleError(err) = minted.err().unwrap(); let mut chain = err.chain(); let message_1 = chain.next().unwrap().to_string(); let message_2 = chain.next().unwrap().to_string(); assert!(chain.next().is_none()); assert_eq!( format!( "Failed mint coins(token_address={} amount={}) to {} by authorizer {}", token_address, u64::MAX, new_holder, authorized_minter_address_1, ), message_1 ); assert_eq!( "Account balance overflow in the mint method of bank module", message_2, ); // assert that the supply is unchanged after the overflow mint let supply = query_total_supply(token_address, &mut working_set); assert_eq!(Some(120), supply); // Overflow test 2 - total supply let new_holder = generate_address::("new_holder_3"); let overflow_mint_message = CallMessage::Mint { coins: Coins { amount: u64::MAX - 1, token_address, }, minter_address: new_holder, }; let minted = bank.call( overflow_mint_message, &authorized_minter_1_context, &mut working_set, ); assert!(minted.is_err()); let Error::ModuleError(err) = minted.err().unwrap(); let mut chain = err.chain(); let message_1 = chain.next().unwrap().to_string(); let message_2 = chain.next().unwrap().to_string(); assert!(chain.next().is_none()); assert_eq!( format!( "Failed mint coins(token_address={} amount={}) to {} by authorizer {}", token_address, u64::MAX - 1, new_holder, authorized_minter_address_1, ), message_1 ); assert_eq!( "Total Supply overflow in the mint method of bank module", message_2, ); // assert that the supply is unchanged after the overflow mint let supply = query_total_supply(token_address, &mut working_set); assert_eq!(Some(120), supply); }