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

Feature: Dispenser Pallet #226

Merged
merged 14 commits into from
Apr 10, 2024
23 changes: 23 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 @@ -46,6 +46,7 @@ assert_matches2 = "0.1.2"
substrate-wasm-builder = { version = "17.0.0" }

# Internal pallets (with default disabled)
pallet-dispenser = { path = "pallets/dispenser", default-features = false }
pallet-funding = { path = "pallets/funding", default-features = false }
pallet-democracy = { path = "pallets/democracy", default-features = false }
pallet-elections-phragmen = { path = "pallets/elections-phragmen", default-features = false }
Expand Down
4 changes: 4 additions & 0 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ sp-std.workspace = true
sp-core.workspace = true
sp-runtime.workspace = true
sp-io.workspace = true
pallet-dispenser.workspace = true
pallet-transaction-payment.workspace = true
pallet-funding.workspace = true
pallet-linear-release.workspace = true
xcm.workspace = true
Expand Down Expand Up @@ -96,6 +98,7 @@ std = [
"pallet-collective/std",
"pallet-democracy/std",
"pallet-elections-phragmen/std",
"pallet-dispenser/std",
"pallet-funding/std",
"pallet-im-online/std",
"pallet-linear-release/std",
Expand All @@ -104,6 +107,7 @@ std = [
"pallet-parachain-staking/std",
"pallet-scheduler/std",
"pallet-staking/std",
"pallet-transaction-payment/std",
"pallet-treasury/std",
"pallet-vesting/std",
"pallet-xcm/std",
Expand Down
6 changes: 6 additions & 0 deletions integration-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ decl_test_parachains! {
LocalAssets: politest_runtime::ContributionTokensInstance,
ForeignAssets: politest_runtime::ForeignAssets,
FundingPallet: politest_runtime::Funding,
Dispenser: politest_runtime::Dispenser,
Vesting: politest_runtime::Vesting,
}
},
pub struct AssetHub {
Expand Down Expand Up @@ -203,5 +205,9 @@ pub mod shortcuts {
pub type PenpalSystem = <PenNet as Chain>::System;
pub type AssetHubSystem = <AssetNet as Chain>::System;
pub type PolimecSystem = <PolimecNet as Chain>::System;

// Politest specific pallets
pub type PolitestDispenser = <PolitestNet as PolitestParaPallet>::Dispenser;
pub type PolitestVesting = <PolitestNet as PolitestParaPallet>::Vesting;
}
pub use shortcuts::*;
63 changes: 61 additions & 2 deletions integration-tests/src/tests/credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use crate::*;
use frame_support::assert_ok;
use frame_support::{assert_err, assert_ok, dispatch::GetDispatchInfo, traits::tokens::currency::VestingSchedule};
use macros::generate_accounts;
use polimec_common::credentials::InvestorType;
use polimec_common_test_utils::{get_fake_jwt, get_test_jwt};
use sp_runtime::{AccountId32, DispatchError};
use sp_runtime::{generic::Era, traits::SignedExtension, AccountId32, DispatchError};
use sp_runtime::transaction_validity::InvalidTransaction::Payment;
use sp_runtime::transaction_validity::TransactionValidityError;
use tests::defaults::*;

#[test]
Expand Down Expand Up @@ -55,3 +58,59 @@ fn test_jwt_verification() {
);
});
}

generate_accounts!(EMPTY_ACCOUNT);

#[test]
fn dispenser_signed_extensions_pass_for_new_account() {
PolitestNet::execute_with(|| {
let who = PolitestAccountId::from(EMPTY_ACCOUNT);
assert_eq!(PolimecBalances::free_balance(who.clone()), 0);

let jwt = get_test_jwt(who.clone(), InvestorType::Retail);
let free_call = PolitestCall::Dispenser(pallet_dispenser::Call::dispense { jwt: jwt.clone() });
let paid_call = PolitestCall::System(frame_system::Call::remark{remark: vec![69, 69]});
let extra: politest_runtime::SignedExtra = (
frame_system::CheckNonZeroSender::<PolitestRuntime>::new(),
frame_system::CheckSpecVersion::<PolitestRuntime>::new(),
frame_system::CheckTxVersion::<PolitestRuntime>::new(),
frame_system::CheckGenesis::<PolitestRuntime>::new(),
frame_system::CheckEra::<PolitestRuntime>::from(Era::mortal(0u64, 0u64)),
pallet_dispenser::extensions::CheckNonce::<PolitestRuntime>::from(0u32),
frame_system::CheckWeight::<PolitestRuntime>::new(),
pallet_transaction_payment::ChargeTransactionPayment::<PolitestRuntime>::from(0u64.into()).into(),
);
assert_err!(extra.validate(&who, &paid_call, &paid_call.get_dispatch_info(), 0), TransactionValidityError::Invalid(Payment));
assert_err!(extra.clone().pre_dispatch(&who, &paid_call, &paid_call.get_dispatch_info(), 0), TransactionValidityError::Invalid(Payment));

assert_ok!(extra.validate(&who, &free_call, &free_call.get_dispatch_info(), 0));
assert_ok!(extra.pre_dispatch(&who, &free_call, &free_call.get_dispatch_info(), 0));
});
}

#[test]
fn dispenser_works_with_runtime_values() {
PolitestNet::execute_with(|| {
let who = PolitestAccountId::from(EMPTY_ACCOUNT);
let jwt = get_test_jwt(who.clone(), InvestorType::Retail);
PolitestBalances::force_set_balance(
PolitestOrigin::root(),
PolitestDispenser::dispense_account().into(),
1000 * PLMC,
)
.unwrap();
assert_ok!(PolitestDispenser::dispense(PolitestOrigin::signed(who.clone()), jwt));
assert_eq!(PolitestBalances::free_balance(&who), 700 * PLMC);
assert_eq!(
PolitestBalances::usable_balance(who.clone()),
<PolitestRuntime as pallet_dispenser::Config>::FreeDispenseAmount::get()
);
assert_eq!(
PolitestVesting::vesting_balance(&who),
Some(
<PolitestRuntime as pallet_dispenser::Config>::InitialDispenseAmount::get() -
<PolitestRuntime as pallet_dispenser::Config>::FreeDispenseAmount::get()
)
);
})
}
2 changes: 0 additions & 2 deletions integration-tests/src/tests/ct_migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,6 @@ fn create_settled_project() -> (ProjectId, Vec<AccountId>) {
fn full_migration_test() {
let (project_id, participants) = create_settled_project();

dbg!(get_migrations_for_participants(project_id, participants.clone()));

mock_hrmp_establishment(project_id);

assert_migration_is_ready(project_id);
Expand Down
4 changes: 2 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ benchmark-runtime chain="polimec-local" pallet="pallet-elections-phragmen" featu
# src: https://github.com/paritytech/polkadot-sdk/blob/bc2e5e1fe26e2c2c8ee766ff9fe7be7e212a0c62/substrate/frame/nfts/src/weights.rs
# Run the Runtime benchmarks for a specific pallet
# TODO: Adjust the `--chain` flag to match the chain you are benchmarking
benchmark-pallet chain="polimec-local" pallet="pallet-elections-phragmen" features="runtime-benchmarks":
cargo run --features {{ features }} --profile production -p polimec-node benchmark pallet \
benchmark-pallet chain="politest-local" pallet="pallet-dispenser" features="runtime-benchmarks":
cargo run --features {{ features }} --release -p polimec-node benchmark pallet \
--chain={{ chain }} \
--steps=50 \
--repeat=20 \
Expand Down
80 changes: 80 additions & 0 deletions pallets/dispenser/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
[package]
name = "pallet-dispenser"
description = "Polimec Dispenser Logic"
authors.workspace = true
documentation.workspace = true
edition.workspace = true
homepage.workspace = true
license-file.workspace = true
readme.workspace = true
repository.workspace = true
version.workspace = true

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

[dependencies]
parity-scale-codec = { version = "3.6.5", features = [
"derive",
], default-features = false }
scale-info = { version = "2.9.0", default-features = false, features = [
"derive",
] }

# Substrate
frame-benchmarking = { workspace = true, optional = true}
frame-support.workspace = true
frame-system.workspace = true

polimec-common.workspace = true
polimec-common-test-utils.workspace = true
sp-std.workspace = true
sp-runtime.workspace = true

[dev-dependencies]
sp-core.workspace = true
sp-io.workspace = true
sp-runtime.workspace = true
pallet-balances.workspace = true
pallet-timestamp.workspace = true
pallet-vesting.workspace = true

[features]
default = [ "std" ]
std = [
"parity-scale-codec/std",
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
"pallet-balances/std",
"pallet-timestamp/std",
"pallet-vesting/std",
"polimec-common/std",
"polimec-common-test-utils/std",
"scale-info/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-timestamp/runtime-benchmarks",
"pallet-vesting/runtime-benchmarks",
"polimec-common/runtime-benchmarks",
"polimec-common-test-utils/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
]

try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"pallet-balances/try-runtime",
"pallet-timestamp/try-runtime",
"pallet-vesting/try-runtime",
"polimec-common/try-runtime",
"sp-runtime/try-runtime",
]
Empty file added pallets/dispenser/README.md
Empty file.
74 changes: 74 additions & 0 deletions pallets/dispenser/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Polimec Blockchain – https://www.polimec.org/
// Copyright (C) Polimec 2022. All rights reserved.

// The Polimec Blockchain is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// The Polimec Blockchain is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

// If you feel like getting in touch with us, you can do so at info@polimec.org
#![cfg(feature = "runtime-benchmarks")]
use super::*;

#[allow(unused)]
use crate::Pallet as Dispenser;
use frame_benchmarking::v2::*;
use frame_support::traits::{EnsureOrigin, Get};
use frame_system::RawOrigin;
use polimec_common_test_utils::{generate_did_from_account, get_mock_jwt};
use sp_runtime::traits::One;

fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
frame_system::Pallet::<T>::assert_last_event(generic_event.into());
}

#[benchmarks]
mod benchmarks {
use super::*;

#[benchmark]
fn dispense() {
let caller: T::AccountId = whitelisted_caller();
let did = generate_did_from_account(1);
assert_eq!(Dispensed::<T>::get(did.clone()), None);
CurrencyOf::<T>::deposit_creating(&Dispenser::<T>::dispense_account(), T::InitialDispenseAmount::get());

let jwt = get_mock_jwt(caller.clone(), InvestorType::Retail, did.clone());
#[extrinsic_call]
dispense(RawOrigin::Signed(caller.clone()), jwt);

assert_eq!(Dispensed::<T>::get(did.clone()), Some(()));
assert_last_event::<T>(
Event::<T>::Dispensed {
dispensed_to_did: did.clone(),
dispensed_to: caller,
amount: T::InitialDispenseAmount::get(),
}
.into(),
);
}

#[benchmark]
fn set_dispense_amount() -> Result<(), BenchmarkError> {
let origin = T::AdminOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
assert_eq!(DispenseAmount::<T>::get(), T::InitialDispenseAmount::get());

let new_amount = T::InitialDispenseAmount::get() + One::one();
#[extrinsic_call]
set_dispense_amount(origin as T::RuntimeOrigin, new_amount);

assert_eq!(DispenseAmount::<T>::get(), new_amount);
assert_last_event::<T>(Event::<T>::DispenseAmountChanged(new_amount).into());
Ok(())
}

impl_benchmark_test_suite!(Dispenser, crate::mock::ExtBuilder::default().build(), crate::mock::Test);
}
Loading