From 52ec10dd0322e92c62aee0e410d28ce1c8898c3e Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Tue, 24 Sep 2024 14:17:42 +0700 Subject: [PATCH] refactor: tests --- pop-api/examples/fungibles/lib.rs | 32 +--- pop-api/examples/fungibles/tests.rs | 249 +++++++++++++--------------- pop-api/examples/fungibles/utils.rs | 5 +- 3 files changed, 127 insertions(+), 159 deletions(-) diff --git a/pop-api/examples/fungibles/lib.rs b/pop-api/examples/fungibles/lib.rs index 1de56772..87aebdb4 100644 --- a/pop-api/examples/fungibles/lib.rs +++ b/pop-api/examples/fungibles/lib.rs @@ -1,3 +1,9 @@ +// TODO: If `admin` in `create` is different than the contract address, `NoPermission` thrown for +// mint, burn +// TODO: `Fungibles::decrease_allowance()` saturating_sub the value, it should +// `checked_sub` and throw an error instead. Hence, we don't have to handle `InsufficientAllowance` +// on the contract side. + #![cfg_attr(not(feature = "std"), no_std, no_main)] use ink::prelude::{string::String, vec::Vec}; @@ -48,12 +54,7 @@ mod fungibles { /// * - `admin` - The account that will administer the token. /// * - `min_balance` - The minimum balance required for accounts holding this token. #[ink(constructor, payable)] - pub fn new( - id: TokenId, - // TODO: If admin is different than the contract address, `NoPermission` thrown for mint, burn - // _admin: AccountId, - min_balance: Balance, - ) -> Result { + pub fn new(id: TokenId, min_balance: Balance) -> Result { let mut contract = Self { id }; let contract_id = contract.env().account_id(); api::create(id, contract_id, min_balance).map_err(PSP22Error::from)?; @@ -99,11 +100,6 @@ mod fungibles { return Ok(()); } - // Reverts with `InsufficientBalance` if the `value` exceeds the caller's balance. - if value > self.balance_of(caller) { - return Err(PSP22Error::InsufficientBalance); - } - api::transfer(self.id, to, value).map_err(PSP22Error::from)?; self.env().emit_event(Transfer { from: Some(caller), to: Some(to), value }); Ok(()) @@ -126,20 +122,6 @@ mod fungibles { return Ok(()); } - // Reverts with `InsufficientBalance` if the `value` exceeds the balance of the account - // `from`. - let allowance = self.allowance(from, caller); - if allowance < value { - return Err(PSP22Error::InsufficientAllowance); - } - - // Reverts with `InsufficientAllowance` if `from` and the caller are different addresses - // and the `value` exceeds the allowance granted by `from` to the caller. - let from_balance = self.balance_of(from); - if from_balance < value { - return Err(PSP22Error::InsufficientBalance); - } - // If `from` and the caller are different addresses, a successful transfer results // in decreased allowance by `from` to the caller and an `Approval` event with // the new allowance amount is emitted. diff --git a/pop-api/examples/fungibles/tests.rs b/pop-api/examples/fungibles/tests.rs index 57e93005..32b1989c 100644 --- a/pop-api/examples/fungibles/tests.rs +++ b/pop-api/examples/fungibles/tests.rs @@ -31,14 +31,13 @@ fn new_constructor_works(mut session: Session) -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box> { TOKEN_ID, TOKEN_MIN_BALANCE, )?; - // Mint tokens. + const AMOUNT: Balance = TOKEN_MIN_BALANCE * 4; + + // Successfully mint tokens. + assert_ok!(mint(&mut session, ALICE, 0)); + assert_eq!(last_contract_event(&session), None); // No event emitted. + assert_eq!(session.sandbox().total_supply(&TOKEN_ID), 0); + assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &ALICE), 0); + + // Mint tokens. assert_ok!(mint(&mut session, ALICE, AMOUNT)); - // Successfully emit event. let expected = Transfer { from: None, to: Some(account_id_from_slice(ALICE.as_ref())), value: AMOUNT } .encode(); - assert_eq!(last_contract_event(&session).unwrap(), expected.as_slice()); - // Tokens were minted with the right amount. + assert_eq!(last_contract_event(&session).unwrap(), expected.as_slice()); // Successfully emit event. + assert_eq!(session.sandbox().total_supply(&TOKEN_ID), AMOUNT); assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &ALICE), AMOUNT); Ok(()) } -#[drink::test(sandbox = Sandbox)] -fn mint_zero_value_works(mut session: Session) -> Result<(), Box> { - let _ = env_logger::try_init(); - // Deploy a new contract. - deploy_with_new_constructor( - &mut session, - BundleProvider::local()?, - TOKEN_ID, - TOKEN_MIN_BALANCE, - )?; - // Mint tokens. - assert_ok!(mint(&mut session, ALICE, 0)); - // No event emitted. - assert_eq!(last_contract_event(&session), None); - // Tokens were minted with the right amount. - assert_eq!(session.sandbox().total_supply(&TOKEN_ID), 0); - assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &ALICE), 0); - Ok(()) -} - #[drink::test(sandbox = Sandbox)] fn burn_works(mut session: Session) -> Result<(), Box> { let _ = env_logger::try_init(); @@ -142,56 +131,15 @@ fn burn_works(mut session: Session) -> Result<(), Box> { TOKEN_ID, TOKEN_MIN_BALANCE, )?; - // Mint tokens. + const AMOUNT: Balance = TOKEN_MIN_BALANCE * 4; + // Mint tokens. assert_ok!(session.sandbox().mint_into(&TOKEN_ID, &ALICE, AMOUNT)); - // Burn tokens. - assert_ok!(burn(&mut session, ALICE, 1)); - // Successfully emit event. - let expected = - Transfer { from: Some(account_id_from_slice(ALICE.as_ref())), to: None, value: 1 }.encode(); - assert_eq!(last_contract_event(&session).unwrap(), expected.as_slice()); - - assert_eq!(session.sandbox().total_supply(&TOKEN_ID), AMOUNT - 1); - assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &ALICE), AMOUNT - 1); - Ok(()) -} -#[drink::test(sandbox = Sandbox)] -fn burn_zero_value_works(mut session: Session) -> Result<(), Box> { - let _ = env_logger::try_init(); - // Deploy a new contract. - deploy_with_new_constructor( - &mut session, - BundleProvider::local()?, - TOKEN_ID, - TOKEN_MIN_BALANCE, - )?; - // Burn tokens. + // No-op. assert_ok!(burn(&mut session, ALICE, 0)); - // No event emitted. - assert_eq!(last_contract_event(&session), None); - // Tokens were minted with the right amount. - assert_eq!(total_supply(&mut session), 0); - assert_eq!(balance_of(&mut session, ALICE), 0); - Ok(()) -} + assert_eq!(last_contract_event(&session), None); // No event emitted. -#[drink::test(sandbox = Sandbox)] -fn burn_fails_with_insufficient_balance( - mut session: Session, -) -> Result<(), Box> { - let _ = env_logger::try_init(); - // Deploy a new contract. - deploy_with_new_constructor( - &mut session, - BundleProvider::local()?, - TOKEN_ID, - TOKEN_MIN_BALANCE, - )?; - // Mint tokens. - const AMOUNT: Balance = TOKEN_MIN_BALANCE * 4; - assert_ok!(session.sandbox().mint_into(&TOKEN_ID, &ALICE, AMOUNT)); // Failed with `InsufficientBalance`. expect_call_reverted( &mut session, @@ -199,6 +147,15 @@ fn burn_fails_with_insufficient_balance( vec![ALICE.to_string(), (AMOUNT + 1).to_string()], PSP22Error::InsufficientBalance, ); + + // Successfully burn tokens. + assert_ok!(burn(&mut session, ALICE, 1)); + let expected = + Transfer { from: Some(account_id_from_slice(ALICE.as_ref())), to: None, value: 1 }.encode(); + assert_eq!(last_contract_event(&session).unwrap(), expected.as_slice()); // Successfully emit event. + + assert_eq!(session.sandbox().total_supply(&TOKEN_ID), AMOUNT - 1); + assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &ALICE), AMOUNT - 1); Ok(()) } @@ -212,50 +169,49 @@ fn transfer_works(mut session: Session) -> Result<(), Box TOKEN_ID, TOKEN_MIN_BALANCE, )?; - // Mint tokens. + const AMOUNT: Balance = TOKEN_MIN_BALANCE * 4; const TRANSFERRED: Balance = TOKEN_MIN_BALANCE; + // Mint tokens. assert_ok!(session.sandbox().mint_into(&TOKEN_ID, &contract.clone(), AMOUNT)); assert_ok!(session.sandbox().mint_into(&TOKEN_ID, &BOB, AMOUNT)); // Check balance of accounts. assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &contract.clone()), AMOUNT); assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &BOB), AMOUNT); - // Transfer tokens from `contract` to `account`. + + // No-op. + assert_ok!(transfer(&mut session, ALICE, 0)); + assert_eq!(last_contract_event(&session), None); // No event emitted. + + // Failed with `InsufficientBalance`. + let data = serde_json::to_string::<[u8; 0]>(&[]).unwrap(); + expect_call_reverted( + &mut session, + TRANSFER, + vec![BOB.to_string(), (AMOUNT + 1).to_string(), data], + PSP22Error::InsufficientBalance, + ); + // Make sure balance is not changed. + assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &contract), AMOUNT); + + // Successfully transfer tokens from `contract` to `account`. session.set_actor(contract.clone()); assert_ok!(transfer(&mut session, BOB, TRANSFERRED)); - // Successfully emit event. let expected = Transfer { from: Some(account_id_from_slice(contract.clone().as_ref())), to: Some(account_id_from_slice(BOB.as_ref())), value: TRANSFERRED, } .encode(); - assert_eq!(last_contract_event(&session).unwrap(), expected.as_slice()); + assert_eq!(last_contract_event(&session).unwrap(), expected.as_slice()); // Successfully emit event. + assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &contract), AMOUNT - TRANSFERRED); assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &BOB), AMOUNT + TRANSFERRED); Ok(()) } #[drink::test(sandbox = Sandbox)] -fn transfer_zero_value_works(mut session: Session) -> Result<(), Box> { - let _ = env_logger::try_init(); - // Deploy a new contract. - deploy_with_new_constructor( - &mut session, - BundleProvider::local()?, - TOKEN_ID, - TOKEN_MIN_BALANCE, - )?; - assert_ok!(transfer(&mut session, ALICE, 0)); - // No event emitted. - assert_eq!(last_contract_event(&session), None); - Ok(()) -} - -#[drink::test(sandbox = Sandbox)] -fn transfer_fails_with_insufficient_balance( - mut session: Session, -) -> Result<(), Box> { +fn allowance_works(mut session: Session) -> Result<(), Box> { let _ = env_logger::try_init(); // Deploy a new contract. let contract = deploy_with_new_constructor( @@ -264,28 +220,14 @@ fn transfer_fails_with_insufficient_balance( TOKEN_ID, TOKEN_MIN_BALANCE, )?; - // Mint tokens. + const AMOUNT: Balance = TOKEN_MIN_BALANCE * 4; + // Mint tokens and approve. assert_ok!(session.sandbox().mint_into(&TOKEN_ID, &contract.clone(), AMOUNT)); - assert_ok!(session.sandbox().mint_into(&TOKEN_ID, &BOB, AMOUNT)); - assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &contract.clone()), AMOUNT); - - session.set_actor(contract.clone()); - // Failed with `InsufficientBalance`. - let data = serde_json::to_string::<[u8; 0]>(&[]).unwrap(); - expect_call_reverted( - &mut session, - TRANSFER, - vec![BOB.to_string(), (AMOUNT + 1).to_string(), data], - PSP22Error::InsufficientBalance, - ); - // Check that balance of account is not changed. - assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &contract), AMOUNT); - Ok(()) -} + assert_ok!(session.sandbox().approve(&TOKEN_ID, &contract.clone(), &ALICE, AMOUNT / 2)); -#[drink::test(sandbox = Sandbox)] -fn allowance_works(mut session: Session) -> Result<(), Box> { + // Successfully return a correct allowance. + assert_eq!(allowance(&mut session, contract, ALICE), AMOUNT / 2); Ok(()) } @@ -303,17 +245,17 @@ fn approve_works(mut session: Session) -> Result<(), Box> const AMOUNT: Balance = TOKEN_MIN_BALANCE * 4; // Mint tokens. assert_ok!(session.sandbox().mint_into(&TOKEN_ID, &contract.clone(), AMOUNT)); - // Successfully apporve. + + // Successfully approve. session.set_actor(contract.clone()); assert_ok!(approve(&mut session, ALICE, AMOUNT / 2)); - // Successfully emit event. let expected = Approval { owner: account_id_from_slice(contract.clone().as_ref()), spender: account_id_from_slice(ALICE.as_ref()), value: AMOUNT / 2, } .encode(); - assert_eq!(last_contract_event(&session).unwrap(), expected.as_slice()); + assert_eq!(last_contract_event(&session).unwrap(), expected.as_slice()); // Successfully emit event. assert_eq!(session.sandbox().allowance(&TOKEN_ID, &contract, &ALICE), AMOUNT / 2); Ok(()) } @@ -330,20 +272,20 @@ fn increase_allowance_works(mut session: Session) -> Result<(), Box Result<(), Box Result<(), Box(&[]).unwrap(); + expect_call_reverted( + &mut session, + TRANSFER_FROM, + vec![ALICE.to_string(), contract.clone().to_string(), (AMOUNT * 2 + 1).to_string(), data], + PSP22Error::InsufficientAllowance, + ); + // Make sure balances are unchaged. + assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &contract), 0); + assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &ALICE), AMOUNT); + assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &BOB), 0); + + // Failed with `InsufficientBalance`. + session.set_actor(contract.clone()); + let data = serde_json::to_string::<[u8; 0]>(&[]).unwrap(); + expect_call_reverted( + &mut session, + TRANSFER_FROM, + vec![ALICE.to_string(), contract.clone().to_string(), (AMOUNT + 1).to_string(), data], + PSP22Error::InsufficientBalance, + ); + // Make sure balances are unchaged. + assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &contract), 0); + assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &ALICE), AMOUNT); + assert_eq!(session.sandbox().balance_of(&TOKEN_ID, &BOB), 0); + // Successfully transfer from `owner`. session.set_actor(contract.clone()); assert_ok!(transfer_from(&mut session, ALICE, BOB, AMOUNT / 2)); @@ -406,11 +388,14 @@ fn transfer_from_works(mut session: Session) -> Result<(), Box AccountId { AccountId::decode(&mut &s[..]).expect("Should be decoded to AccountId") } @@ -47,6 +47,7 @@ pub(super) fn expect_call_reverted( err: PSP22Error, ) { let call = session.call::(function, ¶ms, None); + println!("call: {:?}", call); match call { Err(SessionError::CallReverted(error)) => { assert_eq!(error[1..], Err::<(), PSP22Error>(err).encode())