Skip to content

Commit

Permalink
test(pallet-market): add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
th7nder committed Jun 20, 2024
1 parent ff557aa commit 0ff469f
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 44 deletions.
4 changes: 2 additions & 2 deletions pallets/market/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ frame-support = { workspace = true, default-features = false }
frame-system = { workspace = true, default-features = false }

[dev-dependencies]
pallet-balances = { workspace = true, default-features = false }
sp-core = { workspace = true, default-features = false }
sp-io = { workspace = true }
sp-runtime = { workspace = true, default-features = false }
pallet-balances = { workspace = true, default-features = false }

[features]
default = ["std"]
Expand All @@ -41,9 +41,9 @@ runtime-benchmarks = [
std = [
"codec/std",
"frame-benchmarking?/std",
"pallet-balances/std",
"frame-support/std",
"frame-system/std",
"pallet-balances/std",
"scale-info/std",
"sp-core/std",
"sp-io/std",
Expand Down
81 changes: 48 additions & 33 deletions pallets/market/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,33 @@ pub use pallet::*;
#[cfg(test)]
mod mock;

#[cfg(test)]
mod test;

// TODO(@th7nder,#77,14/06/2024): take the pallet out of dev mode
#[frame_support::pallet(dev_mode)]
pub mod pallet {
use codec::{Decode, Encode};
use frame_support::{
dispatch::DispatchResult,
ensure,
pallet_prelude::*,
sp_runtime::{RuntimeDebug,traits::{AccountIdConversion,CheckedAdd},ArithmeticError},
traits::{Currency, ReservableCurrency,ExistenceRequirement::KeepAlive,ExistenceRequirement::AllowDeath},
sp_runtime::{
traits::{AccountIdConversion, CheckedAdd, CheckedSub},
ArithmeticError, RuntimeDebug,
},
traits::{
Currency,
ExistenceRequirement::{AllowDeath, KeepAlive},
ReservableCurrency,
},
PalletId,
};
use frame_system::{pallet_prelude::*, Config as SystemConfig};
use scale_info::TypeInfo;

// Allows to extract Balance of an account via the Config::Currency associated type.
// BalanceOf is a sophisticated way of getting an u128.
/// Allows to extract Balance of an account via the Config::Currency associated type.
/// BalanceOf is a sophisticated way of getting an u128.
type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;

Expand All @@ -45,7 +56,7 @@ pub mod pallet {
}

/// Stores balances info for both Storage Providers and Storage Users
/// We do not use the ReservableCurrency::reserve mechanism,
/// We do not use the ReservableCurrency::reserve mechanism,
/// as the Market works as a liaison between Storage Providers and Storage Clients.
/// Market has its own account on which funds of all parties are stored.
/// It's Market reposibility to manage deposited funds, lock/unlock and pay them out when necessary.
Expand All @@ -55,10 +66,10 @@ pub mod pallet {
pub struct BalanceEntry<Balance> {
/// Amount of Balance that has been deposited for future deals/earned from deals.
/// It can be withdrawn at any time.
free: Balance,
pub(crate) free: Balance,
/// Amount of Balance that has been staked as Deal Collateral
/// It's locked to a deal and cannot be withdrawn until the deal ends.
locked: Balance,
pub(crate) locked: Balance,
}

#[pallet::pallet]
Expand All @@ -81,39 +92,40 @@ pub mod pallet {
BalanceWithdrawn {
who: T::AccountId,
amount: BalanceOf<T>,
}
},
}

#[pallet::error]
pub enum Error<T> {
/// When a Market Participant tries to withdraw more
/// funds than they have available on the Market, because:
/// - they never deposited the amount they want to withdraw
// - the funds they deposited were locked as part of a deal
/// - the funds they deposited were locked as part of a deal
InsufficientFreeFunds,
}

/// Extrinsics exposed by the pallet
#[pallet::call]
impl<T: Config> Pallet<T> {

/// Transfers `amount` of Balance from the `origin` to the Market Pallet account.
/// It is marked as _free_ in the Market bookkeeping.
/// Free balance can be withdrawn at any moment from the Market.
pub fn add_balance(origin: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {
let caller = ensure_signed(origin)?;

BalanceTable::<T>::try_mutate(
&caller,
|balance| -> DispatchResult {
T::Currency::transfer(&caller, &Self::account_id(), amount, KeepAlive)?;
balance.free = balance.free.checked_add(&amount)
.ok_or(ArithmeticError::Overflow)?;
BalanceTable::<T>::try_mutate(&caller, |balance| -> DispatchResult {
balance.free = balance
.free
.checked_add(&amount)
.ok_or(ArithmeticError::Overflow)?;
T::Currency::transfer(&caller, &Self::account_id(), amount, KeepAlive)?;

Self::deposit_event(Event::<T>::BalanceAdded { who: caller.clone(), amount });
Ok(())
}
)?;
Self::deposit_event(Event::<T>::BalanceAdded {
who: caller.clone(),
amount,
});
Ok(())
})?;

Ok(())
}
Expand All @@ -123,18 +135,21 @@ pub mod pallet {
pub fn withdraw_balance(origin: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {
let caller = ensure_signed(origin)?;

BalanceTable::<T>::try_mutate(
&caller,
|balance| -> DispatchResult {
ensure!(balance.free >= amount, Error::<T>::InsufficientFreeFunds);
balance.free -= amount;
// The Market Pallet account will be reaped if no one is participating in the market.
T::Currency::transfer(&Self::account_id(), &caller, amount, AllowDeath)?;

Self::deposit_event(Event::<T>::BalanceWithdrawn { who: caller.clone(), amount });
Ok(())
}
)?;
BalanceTable::<T>::try_mutate(&caller, |balance| -> DispatchResult {
ensure!(balance.free >= amount, Error::<T>::InsufficientFreeFunds);
balance.free = balance
.free
.checked_sub(&amount)
.ok_or(ArithmeticError::Underflow)?;
// The Market Pallet account will be reaped if no one is participating in the market.
T::Currency::transfer(&Self::account_id(), &caller, amount, AllowDeath)?;

Self::deposit_event(Event::<T>::BalanceWithdrawn {
who: caller.clone(),
amount,
});
Ok(())
})?;

Ok(())
}
Expand All @@ -144,7 +159,7 @@ pub mod pallet {
impl<T: Config> Pallet<T> {
/// Account Id of the Market
///
/// This actually does computation.
/// This actually does computation.
/// If you need to keep using it, make sure you cache it and call it once.
pub fn account_id() -> T::AccountId {
T::PalletId::get().into_account_truncating()
Expand Down
44 changes: 35 additions & 9 deletions pallets/market/src/mock.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
use crate as pallet_market;
use frame_support::{derive_impl, parameter_types, PalletId};
use frame_system as system;
use sp_runtime::BuildStorage;

use crate as pallet_market;

type Block = frame_system::mocking::MockBlock<Test>;

// Configure a mock runtime to test the pallet.
frame_support::construct_runtime!(
pub enum Test
{
System: frame_system,
pub enum Test
{
System: frame_system,
Balances: pallet_balances,
Market: pallet_market,
}
}
);

#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl system::Config for Test {
type Block = Block;
type Block = Block;
type AccountData = pallet_balances::AccountData<u64>;
}

Expand All @@ -31,12 +32,37 @@ parameter_types! {
}

impl crate::Config for Test {
type RuntimeEvent = RuntimeEvent;
type RuntimeEvent = RuntimeEvent;
type PalletId = MarketPalletId;
type Currency = Balances;
}

pub const ALICE: u64 = 0;
pub const BOB: u64 = 1;
pub const INITIAL_FUNDS: u64 = 100;

/// Build genesis storage according to the mock runtime.
pub fn new_test_ext() -> sp_io::TestExternalities {
system::GenesisConfig::<Test>::default().build_storage().unwrap().into()
}
let mut t = system::GenesisConfig::<Test>::default()
.build_storage()
.unwrap()
.into();
pallet_balances::GenesisConfig::<Test> {
balances: vec![(ALICE, INITIAL_FUNDS), (BOB, INITIAL_FUNDS)],
}
.assimilate_storage(&mut t)
.unwrap();

let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
ext
}

pub fn events() -> Vec<RuntimeEvent> {
let evt = System::events()
.into_iter()
.map(|evt| evt.event)
.collect::<Vec<_>>();
System::reset_events();
evt
}
Loading

0 comments on commit 0ff469f

Please sign in to comment.