Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pallet-fellowship 2 #203

Merged
merged 12 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cant we use one function

seems both are similar except one checks a vec?

Are you concerned about the potential compute/block time?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea behind this was so we can just use an ensure_role_in instead of doing multiple ensure_role
As for compute time i guess its the developers responsibility to ensure the input isn't from the user and isn't over bounds.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks. LGTM!

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
Loading