Skip to content
Snippets Groups Projects
Unverified Commit 19cd1d2f authored by Liam Aharon's avatar Liam Aharon
Browse files

create_pool implementation, mock and test set up

parent 9eeebd57
Branches
No related merge requests found
......@@ -68,6 +68,11 @@ use sp_core::Get;
use sp_runtime::DispatchError;
use sp_std::boxed::Box;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
/// The type of the unique id for each pool.
pub type PoolId = u32;
......@@ -80,7 +85,7 @@ pub struct PoolStakerInfo<Balance> {
}
/// A staking pool.
#[derive(Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)]
#[derive(Debug, Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)]
pub struct PoolInfo<AccountId, AssetId, Balance, BlockNumber> {
/// The asset that is staked in this pool.
staking_asset_id: AssetId,
......@@ -103,7 +108,7 @@ pub mod pallet {
use super::*;
use frame_support::{pallet_prelude::*, traits::tokens::AssetId};
use frame_system::pallet_prelude::*;
use sp_runtime::traits::AccountIdConversion;
use sp_runtime::traits::{AccountIdConversion, Saturating};
#[pallet::pallet]
pub struct Pallet<T>(_);
......@@ -158,7 +163,7 @@ pub mod pallet {
///
/// Incremented when a new pool is created.
#[pallet::storage]
pub type NextPoolId<T: Config> = StorageValue<_, PoolId>;
pub type NextPoolId<T: Config> = StorageValue<_, PoolId, ValueQuery>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
......@@ -166,7 +171,7 @@ pub mod pallet {
/// An account staked some tokens in a pool.
Staked {
/// The account that staked assets.
staker: T::AccountId,
who: T::AccountId,
/// The pool.
pool_id: PoolId,
/// The staked asset amount.
......@@ -175,7 +180,7 @@ pub mod pallet {
/// An account unstaked some tokens from a pool.
Unstaked {
/// The account that unstaked assets.
staker: T::AccountId,
who: T::AccountId,
/// The pool.
pool_id: PoolId,
/// The unstaked asset amount.
......@@ -183,6 +188,8 @@ pub mod pallet {
},
/// An account harvested some rewards.
RewardsHarvested {
/// The extrinsic caller.
who: T::AccountId,
/// The staker whos rewards were harvested.
staker: T::AccountId,
/// The pool.
......@@ -192,6 +199,8 @@ pub mod pallet {
},
/// A new reward pool was created.
PoolCreated {
/// The account that created the pool.
creator: T::AccountId,
/// Unique ID for the new pool.
pool_id: PoolId,
/// The staking asset.
......@@ -200,26 +209,21 @@ pub mod pallet {
reward_asset_id: T::AssetId,
/// The initial reward rate per block.
reward_rate_per_block: T::Balance,
/// The account allowed to modify the pool.
admin: T::AccountId,
},
/// A reward pool was deleted.
/// A reward pool was deleted by the admin.
PoolDeleted {
/// The deleted pool id.
pool_id: PoolId,
},
/// A pool was modified.
/// A pool was modified by the admin.
PoolModifed {
/// The modified pool.
pool_id: PoolId,
/// The new reward rate.
new_reward_rate_per_block: T::Balance,
},
/// Reward assets were withdrawn from a pool.
RewardPoolWithdrawal {
/// The affected pool.
pool_id: PoolId,
/// The acount of reward asset withdrawn.
amount: T::Balance,
},
}
#[pallet::error]
......@@ -231,7 +235,7 @@ pub mod pallet {
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn integrity_test() {
todo!()
// TODO: Proper implementation
}
}
......@@ -243,12 +247,49 @@ pub mod pallet {
impl<T: Config> Pallet<T> {
/// Create a new reward pool.
pub fn create_pool(
_origin: OriginFor<T>,
_staked_asset_id: Box<T::AssetId>,
_reward_asset_id: Box<T::AssetId>,
_admin: Option<T::AccountId>,
origin: OriginFor<T>,
staked_asset_id: Box<T::AssetId>,
reward_asset_id: Box<T::AssetId>,
reward_rate_per_block: T::Balance,
admin: Option<T::AccountId>,
) -> DispatchResult {
todo!()
// Ensure Origin is allowed to create pools.
T::PermissionedPoolCreator::ensure_origin(origin.clone())?;
// Get the admin, or try to use the origin as admin.
let origin_acc_id = ensure_signed(origin)?;
let admin = match admin {
Some(admin) => admin,
None => origin_acc_id,
};
// Create the pool.
let pool = PoolInfo::<T::AccountId, T::AssetId, T::Balance, BlockNumberFor<T>> {
staking_asset_id: *staked_asset_id.clone(),
reward_asset_id: *reward_asset_id.clone(),
reward_rate_per_block,
total_tokens_staked: 0u32.into(),
accumulated_rewards_per_share: 0u32.into(),
last_rewarded_block: 0u32.into(),
admin: admin.clone(),
};
// Insert the pool into storage.
let pool_id = NextPoolId::<T>::get();
Pools::<T>::insert(pool_id, pool);
NextPoolId::<T>::put(pool_id.saturating_add(1));
// Emit the event.
Self::deposit_event(Event::PoolCreated {
creator: origin_acc_id,
pool_id,
staking_asset_id: *staked_asset_id,
reward_asset_id: *reward_asset_id,
reward_rate_per_block,
admin,
});
Ok(())
}
/// Removes an existing reward pool.
......
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Test environment for Staking Rewards pallet.
use super::*;
use crate as pallet_staking_rewards;
use core::default::Default;
use frame_support::{
construct_runtime, derive_impl,
instances::Instance1,
ord_parameter_types, parameter_types,
traits::{
tokens::fungible::{NativeFromLeft, NativeOrWithId, UnionOf},
AsEnsureOriginWithArg, ConstU128, ConstU32, EnsureOrigin,
},
PalletId,
};
use frame_system::{ensure_signed, EnsureSigned};
use sp_runtime::{
traits::{AccountIdConversion, IdentityLookup},
BuildStorage,
};
type Block = frame_system::mocking::MockBlock<MockRuntime>;
construct_runtime!(
pub enum MockRuntime
{
System: frame_system,
Balances: pallet_balances,
Assets: pallet_assets::<Instance1>,
StakingRewards: pallet_staking_rewards,
}
);
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for MockRuntime {
type AccountId = u128;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = Block;
type AccountData = pallet_balances::AccountData<u128>;
}
impl pallet_balances::Config for MockRuntime {
type Balance = u128;
type DustRemoval = ();
type RuntimeEvent = RuntimeEvent;
type ExistentialDeposit = ConstU128<100>;
type AccountStore = System;
type WeightInfo = ();
type MaxLocks = ();
type MaxReserves = ConstU32<50>;
type ReserveIdentifier = [u8; 8];
type FreezeIdentifier = ();
type MaxFreezes = ();
type RuntimeHoldReason = ();
type RuntimeFreezeReason = ();
}
impl pallet_assets::Config<Instance1> for MockRuntime {
type RuntimeEvent = RuntimeEvent;
type Balance = u128;
type RemoveItemsLimit = ConstU32<1000>;
type AssetId = u32;
type AssetIdParameter = u32;
type Currency = Balances;
type CreateOrigin = AsEnsureOriginWithArg<EnsureSigned<Self::AccountId>>;
type ForceOrigin = frame_system::EnsureRoot<Self::AccountId>;
type AssetDeposit = ConstU128<1>;
type AssetAccountDeposit = ConstU128<10>;
type MetadataDepositBase = ConstU128<1>;
type MetadataDepositPerByte = ConstU128<1>;
type ApprovalDeposit = ConstU128<1>;
type StringLimit = ConstU32<50>;
type Freezer = ();
type Extra = ();
type WeightInfo = ();
type CallbackHandle = ();
pallet_assets::runtime_benchmarks_enabled! {
type BenchmarkHelper = ();
}
}
parameter_types! {
pub const StakingRewardsPalletId: PalletId = PalletId(*b"py/stkrd");
pub const Native: NativeOrWithId<u32> = NativeOrWithId::Native;
pub const PermissionedAccountId: u128 = 1;
}
ord_parameter_types! {
pub const AssetConversionOrigin: u128 = AccountIdConversion::<u128>::into_account_truncating(&StakingRewardsPalletId::get());
}
pub struct MockPermissionedPoolCreator;
impl EnsureOrigin<RuntimeOrigin> for MockPermissionedPoolCreator {
type Success = ();
fn try_origin(
origin: RuntimeOrigin,
// key: &RuntimeParametersKey,
) -> Result<Self::Success, RuntimeOrigin> {
// Set account 1 to admin in tests
if ensure_signed(origin.clone()).map_or(false, |acc| acc == 1) {
return Ok(());
}
return Err(origin);
}
#[cfg(feature = "runtime-benchmarks")]
fn try_successful_origin() -> Result<O, ()> {
todo!()
}
}
pub type NativeAndAssets = UnionOf<Balances, Assets, NativeFromLeft, NativeOrWithId<u32>, u128>;
impl Config for MockRuntime {
type RuntimeEvent = RuntimeEvent;
type AssetId = NativeOrWithId<u32>;
type Balance = <Self as pallet_balances::Config>::Balance;
type Assets = NativeAndAssets;
type PalletId = StakingRewardsPalletId;
// allow account id 1 to be permissioned creator
type PermissionedPoolCreator = MockPermissionedPoolCreator;
}
pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::<MockRuntime>::default().build_storage().unwrap();
// pallet_assets::GenesisConfig::<MockRuntime, Instance1> {
// // Genesis assets: id, owner, is_sufficient, min_balance
// // pub assets: Vec<(T::AssetId, T::AccountId, bool, T::Balance)>,
// assets: vec![(1, 1, true, 10000)],
// // Genesis metadata: id, name, symbol, decimals
// // pub metadata: Vec<(T::AssetId, Vec<u8>, Vec<u8>, u8)>,
// metadata: vec![(1, b"test".to_vec(), b"TST".to_vec(), 18)],
// // Genesis accounts: id, account_id, balance
// // pub accounts: Vec<(T::AssetId, T::AccountId, T::Balance)>,
// accounts: vec![(1, 1, 10000)],
// }
// .assimilate_storage(&mut t)
// .unwrap();
pallet_balances::GenesisConfig::<MockRuntime> {
balances: vec![(1, 10000), (2, 20000), (3, 30000), (4, 40000)],
}
.assimilate_storage(&mut t)
.unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
ext
}
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{mock::*, *};
use frame_support::{assert_ok, traits::fungible::NativeOrWithId};
fn create_tokens(owner: u128, tokens: Vec<NativeOrWithId<u32>>) {
create_tokens_with_ed(owner, tokens, 1)
}
fn create_tokens_with_ed(owner: u128, tokens: Vec<NativeOrWithId<u32>>, ed: u128) {
for token_id in tokens {
let asset_id = match token_id {
NativeOrWithId::WithId(id) => id,
_ => unreachable!("invalid token"),
};
assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset_id, owner, false, ed));
}
}
fn events() -> Vec<Event<MockRuntime>> {
let result = System::events()
.into_iter()
.map(|r| r.event)
.filter_map(|e| {
if let mock::RuntimeEvent::StakingRewards(inner) = e {
Some(inner)
} else {
None
}
})
.collect();
System::reset_events();
result
}
fn pools() -> Vec<(u32, PoolInfo<u128, NativeOrWithId<u32>, u128, u64>)> {
Pools::<MockRuntime>::iter().collect()
}
#[test]
fn create_pool_works() {
new_test_ext().execute_with(|| {
// Setup
let user = 1;
let staking_asset_id = NativeOrWithId::<u32>::Native;
let reward_asset_id = NativeOrWithId::<u32>::WithId(1);
let reward_rate_per_block = 100;
create_tokens(user, vec![reward_asset_id.clone()]);
assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 1000));
// Create a pool with default admin.
assert_eq!(NextPoolId::<MockRuntime>::get(), 0);
assert_ok!(StakingRewards::create_pool(
RuntimeOrigin::signed(user),
Box::new(staking_asset_id.clone()),
Box::new(reward_asset_id.clone()),
reward_rate_per_block,
None
));
// Event is emitted.
assert_eq!(
events(),
[Event::<MockRuntime>::PoolCreated {
pool_id: 0,
staking_asset_id: staking_asset_id.clone(),
reward_asset_id: reward_asset_id.clone(),
reward_rate_per_block,
admin: user,
}]
);
// State is updated correctly.
assert_eq!(NextPoolId::<MockRuntime>::get(), 1);
assert_eq!(
pools(),
vec![(
0,
PoolInfo {
staking_asset_id: staking_asset_id.clone(),
reward_asset_id: reward_asset_id.clone(),
reward_rate_per_block,
admin: user,
total_tokens_staked: 0,
accumulated_rewards_per_share: 0,
last_rewarded_block: 0
}
)]
);
// Create another pool with explicit admin.
let admin = 2;
assert_ok!(StakingRewards::create_pool(
RuntimeOrigin::signed(user),
Box::new(staking_asset_id.clone()),
Box::new(reward_asset_id.clone()),
reward_rate_per_block,
Some(admin)
));
// Event is emitted.
assert_eq!(
events(),
[Event::<MockRuntime>::PoolCreated {
pool_id: 1,
staking_asset_id: staking_asset_id.clone(),
reward_asset_id: reward_asset_id.clone(),
reward_rate_per_block,
admin,
}]
);
// State is updated correctly.
assert_eq!(NextPoolId::<MockRuntime>::get(), 2);
assert_eq!(
pools(),
vec![
(
0,
PoolInfo {
staking_asset_id: staking_asset_id.clone(),
reward_asset_id: reward_asset_id.clone(),
reward_rate_per_block,
admin: user,
total_tokens_staked: 0,
accumulated_rewards_per_share: 0,
last_rewarded_block: 0
}
),
(
1,
PoolInfo {
staking_asset_id,
reward_asset_id,
reward_rate_per_block,
admin,
total_tokens_staked: 0,
accumulated_rewards_per_share: 0,
last_rewarded_block: 0
}
)
]
);
});
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment