Skip to content

Commit

Permalink
pallet-fellowship 2 (#203)
Browse files Browse the repository at this point in the history
* path all fellowship files

* fmt clippy

* add -fellowhsip to members

* add maybe convery back

* fix build

* clippy pallet-fellowship

* fmt

* clippy

* fmt

* fix benchmarks

* fmt
  • Loading branch information
f-gate authored Sep 12, 2023
1 parent 7e4e9ea commit 0eda934
Show file tree
Hide file tree
Showing 11 changed files with 1,640 additions and 0 deletions.
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"pallets/grants",
"pallets/crowdfunding",
"pallets/deposits",
"pallets/fellowship",
"runtime/integration-tests",
"runtime/imbue-kusama",
"runtime/common",
Expand Down
6 changes: 6 additions & 0 deletions libs/common-traits/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,9 @@ pub trait TokenMetadata {

fn decimals(&self) -> u8;
}

/// Fallible conversion trait returning an [Option]. Generic over both source and destination types.
pub trait MaybeConvert<A, B> {
/// Attempt to make conversion.
fn maybe_convert(a: A) -> Option<B>;
}
56 changes: 56 additions & 0 deletions pallets/fellowship/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
[package]
name = "pallet-fellowship"
version = "0.1.0"
description = "Used to map the accounts to a fellowship role. Encompasses all the functionality associated with fellowship decisions."
authors = ["Substrate DevHub <https://github.com/substrate-developer-hub>"]
license = 'Apache 2.0'
homepage = 'https://github.com/ImbueNetwork/imbue'
repository = "https://github.com/ImbueNetwork/imbue"
edition = '2018'
resolver = "2"

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [
"derive",
] }
scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false, optional = true }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false }
orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.43", default-features = false }
common-types = { path = "../../libs/common-types", default-features = false}
common-traits = { path = "../../libs/common-traits", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false}
sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false }

[dev-dependencies]
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43"}
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43"}
sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43"}
common-types = { path = "../../libs/common-types"}
common-traits = { path = "../../libs/common-traits"}
orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.43"}
orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.43"}
sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43"}
sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43"}

[features]
default = ["std"]
std = [
"codec/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"scale-info/std",
"sp-runtime/std",
"sp-std/std",
]

runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
]

try-runtime = ["frame-support/try-runtime"]
143 changes: 143 additions & 0 deletions pallets/fellowship/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#![cfg(feature = "runtime-benchmarks")]

use super::*;
use crate::Pallet as Fellowship;
use crate::{traits::FellowshipHandle, Config, Role};
use common_types::CurrencyId;
use frame_benchmarking::v2::*;
use frame_support::assert_ok;
use frame_system::Pallet as System;
use frame_system::RawOrigin;
use orml_traits::MultiCurrency;
use sp_runtime::SaturatedConversion;

#[benchmarks( where <T as frame_system::Config>::AccountId: AsRef<[u8]>, crate::Event::<T>: Into<<T as frame_system::Config>::RuntimeEvent>)]
#[benchmarks]
mod benchmarks {
use super::*;
#[benchmark]
fn add_to_fellowship() {
let alice: T::AccountId =
create_funded_user::<T>("alice", 1, 1_000_000_000_000_000_000u128);
let bob: T::AccountId = create_funded_user::<T>("bob", 1, 1_000_000_000_000_000_000u128);

#[block]
{
<crate::Pallet<T> as FellowshipHandle<<T as frame_system::Config>::AccountId>>::add_to_fellowship(&alice, Role::Vetter, 10, Some(&bob), true);
}
}

#[benchmark]
fn force_add_fellowship() {
let alice: T::AccountId =
create_funded_user::<T>("alice", 1, 1_000_000_000_000_000_000u128);
#[extrinsic_call]
force_add_fellowship(RawOrigin::Root, alice.clone(), Role::Freelancer, 10);
System::<T>::assert_last_event(
Event::<T>::FellowshipAdded {
who: alice,
role: Role::Freelancer,
}
.into(),
);
}

#[benchmark]
fn leave_fellowship() {
let alice: T::AccountId =
create_funded_user::<T>("alice", 1, 1_000_000_000_000_000_000u128);
let bob: T::AccountId = create_funded_user::<T>("bob", 1, 1_000_000_000_000_000_000u128);
<crate::Pallet<T> as FellowshipHandle<<T as frame_system::Config>::AccountId>>::add_to_fellowship(&alice, Role::Vetter, 10, Some(&bob), true);

#[extrinsic_call]
leave_fellowship(RawOrigin::Signed(alice.clone()));

System::<T>::assert_last_event(Event::<T>::FellowshipRemoved { who: alice }.into());
}

#[benchmark]
fn force_remove_and_slash_fellowship() {
let alice: T::AccountId =
create_funded_user::<T>("alice", 1, 1_000_000_000_000_000_000u128);
let bob: T::AccountId = create_funded_user::<T>("bob", 1, 1_000_000_000_000_000_000u128);
<crate::Pallet<T> as FellowshipHandle<<T as frame_system::Config>::AccountId>>::add_to_fellowship(&alice, Role::Vetter, 10, Some(&bob), true);

#[extrinsic_call]
force_remove_and_slash_fellowship(RawOrigin::Root, alice.clone());
System::<T>::assert_last_event(Event::<T>::FellowshipSlashed { who: alice }.into());
}

#[benchmark]
fn add_candidate_to_shortlist() {
let alice: T::AccountId =
create_funded_user::<T>("alice", 1, 1_000_000_000_000_000_000u128);
let bob: T::AccountId = create_funded_user::<T>("bob", 1, 1_000_000_000_000_000_000u128);
<crate::Pallet<T> as FellowshipHandle<<T as frame_system::Config>::AccountId>>::add_to_fellowship(&alice, Role::Vetter, 10, Some(&bob), true);

#[extrinsic_call]
add_candidate_to_shortlist(RawOrigin::Signed(alice), bob.clone(), Role::Vetter, 10);
System::<T>::assert_last_event(Event::<T>::CandidateAddedToShortlist { who: bob }.into());
}

#[benchmark]
fn remove_candidate_from_shortlist() {
let alice: T::AccountId =
create_funded_user::<T>("alice", 1, 1_000_000_000_000_000_000u128);
let bob: T::AccountId = create_funded_user::<T>("bob", 1, 1_000_000_000_000_000_000u128);
<crate::Pallet<T> as FellowshipHandle<<T as frame_system::Config>::AccountId>>::add_to_fellowship(&alice, Role::Vetter, 10, Some(&bob), true);
assert_ok!(Fellowship::<T>::add_candidate_to_shortlist(
RawOrigin::Signed(alice.clone()).into(),
bob.clone(),
Role::Vetter,
10,
));

#[extrinsic_call]
remove_candidate_from_shortlist(RawOrigin::Signed(alice), bob.clone());
System::<T>::assert_last_event(
Event::<T>::CandidateRemovedFromShortlist { who: bob }.into(),
);
}

#[benchmark]
fn pay_deposit_to_remove_pending_status() {
let bob: T::AccountId = account("bob", 1, 0);
let charlie: T::AccountId =
create_funded_user::<T>("alice", 1, 1_000_000_000_000_000_000u128);

<crate::Pallet<T> as FellowshipHandle<<T as frame_system::Config>::AccountId>>::add_to_fellowship(&bob, Role::Vetter, 10, Some(&charlie), true);
assert_ok!(<T::MultiCurrency as MultiCurrency<
<T as frame_system::Config>::AccountId,
>>::deposit(
CurrencyId::Native,
&bob,
1_000_000_000_000_000_000u128.saturated_into()
));

#[extrinsic_call]
pay_deposit_to_remove_pending_status(RawOrigin::Signed(bob.clone()));
System::<T>::assert_last_event(
Event::<T>::FellowshipAdded {
who: bob,
role: Role::Vetter,
}
.into(),
);
}

impl_benchmark_test_suite!(Fellowship, crate::mock::new_test_ext(), crate::mock::Test);
}

pub fn create_funded_user<T: Config>(
seed: &'static str,
n: u32,
balance_factor: u128,
) -> T::AccountId {
let user = account(seed, n, 0);
assert_ok!(<T::MultiCurrency as MultiCurrency<
<T as frame_system::Config>::AccountId,
>>::deposit(
CurrencyId::Native, &user, balance_factor.saturated_into()
));
user
}
79 changes: 79 additions & 0 deletions pallets/fellowship/src/impls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use crate::traits::EnsureRole;
use crate::*;
use common_traits::MaybeConvert;
use frame_support::{ensure, traits::Get};
use orml_traits::MultiReservableCurrency;
use sp_runtime::{
traits::{BadOrigin, Convert},
DispatchError, Percent,
};
use sp_std::vec::Vec;

/// Ensure that a account is of a given role.
/// Used in other pallets like an ensure origin.
pub struct EnsureFellowshipRole<T>(T);
impl<T: Config> EnsureRole<AccountIdOf<T>, Role> for EnsureFellowshipRole<T> {
type Success = ();

fn ensure_role(
acc: &AccountIdOf<T>,
role: Role,
rank: Option<Rank>,
) -> Result<Self::Success, DispatchError> {
let (actual_role, actual_rank) = Roles::<T>::get(acc).ok_or(BadOrigin)?;
ensure!(actual_role == role, BadOrigin);
if let Some(r) = rank {
ensure!(r == actual_rank, BadOrigin);
}
Ok(())
}
fn ensure_role_in(
acc: &AccountIdOf<T>,
roles: Vec<Role>,
ranks: Option<Vec<Rank>>,
) -> Result<Self::Success, DispatchError> {
let (actual_role, actual_rank) = Roles::<T>::get(acc).ok_or(BadOrigin)?;
ensure!(roles.contains(&actual_role), BadOrigin);
if let Some(r) = ranks {
ensure!(r.contains(&actual_rank), BadOrigin);
}
Ok(())
}
}

impl<T: Config> MaybeConvert<&AccountIdOf<T>, VetterIdOf<T>> for Pallet<T> {
fn maybe_convert(fellow: &AccountIdOf<T>) -> Option<VetterIdOf<T>> {
FellowToVetter::<T>::get(fellow)
}
}

pub struct RoleToPercentFee;
impl Convert<crate::Role, Percent> for RoleToPercentFee {
fn convert(role: Role) -> Percent {
match role {
Role::Vetter => Percent::from_percent(50u8),
Role::Freelancer => Percent::from_percent(50u8),
Role::BusinessDev => Percent::from_percent(50u8),
Role::Approver => Percent::from_percent(50u8),
}
}
}

impl<T: Config> Pallet<T> {
/// Try take the membership deposit from who
/// If the deposit was taken, this will return true, else false.
pub(crate) fn try_take_deposit(who: &AccountIdOf<T>) -> bool {
let membership_deposit = <T as Config>::MembershipDeposit::get();
if <T as Config>::MultiCurrency::reserve(
T::DepositCurrencyId::get(),
who,
membership_deposit,
)
.is_ok()
{
FellowshipReserves::<T>::insert(who, membership_deposit);
return true;
}
false
}
}
Loading

0 comments on commit 0eda934

Please sign in to comment.