diff --git a/Cargo.lock b/Cargo.lock
index 7cfde057041e3..495fd7c866c38 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6019,6 +6019,25 @@ dependencies = [
"sp-std",
]
+[[package]]
+name = "pallet-multi-asset-treasury"
+version = "4.0.0-dev"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "impl-trait-for-tuples",
+ "pallet-assets",
+ "pallet-balances",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
[[package]]
name = "pallet-multisig"
version = "4.0.0-dev"
diff --git a/Cargo.toml b/Cargo.toml
index 80dd2b90f85c2..458e062c7fc8e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -114,6 +114,7 @@ members = [
"frame/lottery",
"frame/membership",
"frame/merkle-mountain-range",
+ "frame/multi-asset-treasury",
"frame/multisig",
"frame/nicks",
"frame/node-authorization",
diff --git a/bin/node/runtime/src/assets_api.rs b/bin/node/runtime/src/assets_api.rs
new file mode 100644
index 0000000000000..cf1a663d70300
--- /dev/null
+++ b/bin/node/runtime/src/assets_api.rs
@@ -0,0 +1,34 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program 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.
+
+// This program 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 .
+
+//! Runtime API definition for assets.
+
+use codec::Codec;
+use sp_std::vec::Vec;
+
+sp_api::decl_runtime_apis! {
+ pub trait AssetsApi
+ where
+ AccountId: Codec,
+ AssetBalance: Codec,
+ AssetId: Codec,
+ {
+ /// Returns the list of `AssetId`s and corresponding balance that an `AccountId` has.
+ fn account_balances(account: AccountId) -> Vec<(AssetId, AssetBalance)>;
+ }
+}
diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs
index 063a15085849c..d4cff6e6ade54 100644
--- a/bin/node/runtime/src/lib.rs
+++ b/bin/node/runtime/src/lib.rs
@@ -108,6 +108,9 @@ use sp_runtime::generic::Era;
/// Generated voter bag information.
mod voter_bags;
+/// Runtime API definition for assets.
+pub mod assets_api;
+
// Make the WASM binary available.
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
@@ -2093,6 +2096,18 @@ impl_runtime_apis! {
}
}
+ impl assets_api::AssetsApi<
+ Block,
+ AccountId,
+ Balance,
+ u32,
+ > for Runtime
+ {
+ fn account_balances(account: AccountId) -> Vec<(u32, Balance)> {
+ Assets::account_balances(account)
+ }
+ }
+
impl pallet_contracts::ContractsApi for Runtime
{
fn call(
diff --git a/frame/assets/src/functions.rs b/frame/assets/src/functions.rs
index b6c5ca87ab710..2f1e46f1fb64a 100644
--- a/frame/assets/src/functions.rs
+++ b/frame/assets/src/functions.rs
@@ -931,4 +931,11 @@ impl, I: 'static> Pallet {
Ok(())
})
}
+
+ /// Returns all the non-zero balances for all assets of the given `account`.
+ pub fn account_balances(account: T::AccountId) -> Vec<(T::AssetId, T::Balance)> {
+ Asset::::iter_keys()
+ .filter_map(|id| Self::maybe_balance(id, account.clone()).map(|balance| (id, balance)))
+ .collect::>()
+ }
}
diff --git a/frame/assets/src/impl_fungibles.rs b/frame/assets/src/impl_fungibles.rs
index 2e7ba0e2eef99..58ad095ace338 100644
--- a/frame/assets/src/impl_fungibles.rs
+++ b/frame/assets/src/impl_fungibles.rs
@@ -17,11 +17,14 @@
//! Implementations for fungibles trait.
-use frame_support::traits::tokens::{
- Fortitude,
- Precision::{self, BestEffort},
- Preservation::{self, Expendable},
- Provenance::{self, Minted},
+use frame_support::traits::{
+ fungibles::Credit,
+ tokens::{
+ Fortitude,
+ Precision::{self, BestEffort},
+ Preservation::{self, Expendable},
+ Provenance::{self, Minted},
+ },
};
use super::*;
@@ -258,3 +261,69 @@ impl, I: 'static> fungibles::InspectEnumerable for Pa
Asset::::iter_keys()
}
}
+
+impl, I: 'static> fungibles::MutateHold<::AccountId>
+ for Pallet
+{
+}
+
+impl, I: 'static> fungibles::InspectHold<::AccountId>
+ for Pallet
+{
+ type Reason = T::HoldIdentifier;
+
+ fn total_balance_on_hold(asset: T::AssetId, who: &T::AccountId) -> T::Balance {
+ Zero::zero()
+ }
+ fn reducible_total_balance_on_hold(
+ asset: T::AssetId,
+ who: &T::AccountId,
+ force: Fortitude,
+ ) -> T::Balance {
+ // // The total balance must never drop below the freeze requirements if we're not forcing:
+ // let a = Self::account(who);
+ // let unavailable = if force == Force {
+ // Self::Balance::zero()
+ // } else {
+ // // The freeze lock applies to the total balance, so we can discount the free balance
+ // // from the amount which the total reserved balance must provide to satisfy it.
+ // a.frozen.saturating_sub(a.free)
+ // };
+ // a.reserved.saturating_sub(unavailable)
+ Zero::zero()
+ }
+ fn balance_on_hold(asset: T::AssetId, reason: &Self::Reason, who: &T::AccountId) -> T::Balance {
+ // Holds::::get(who)
+ // .iter()
+ // .find(|x| &x.id == reason)
+ // .map_or_else(Zero::zero, |x| x.amount)
+ Zero::zero()
+ }
+ fn hold_available(asset: T::AssetId, reason: &Self::Reason, who: &T::AccountId) -> bool {
+ false
+ }
+}
+
+impl, I: 'static> fungibles::UnbalancedHold<::AccountId>
+ for Pallet
+{
+ fn set_balance_on_hold(
+ asset: T::AssetId,
+ reason: &T::HoldIdentifier,
+ who: &T::AccountId,
+ amount: T::Balance,
+ ) -> DispatchResult {
+ Ok(())
+ }
+}
+
+impl, I: 'static> fungibles::BalancedHold for Pallet {
+ fn slash(
+ asset: T::AssetId,
+ reason: &T::HoldIdentifier,
+ who: &T::AccountId,
+ amount: T::Balance,
+ ) -> (Credit, T::Balance) {
+ (>::zero(asset), Zero::zero())
+ }
+}
diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs
index 94542f9564997..e3abd0a668f2e 100644
--- a/frame/assets/src/lib.rs
+++ b/frame/assets/src/lib.rs
@@ -229,6 +229,8 @@ pub mod pallet {
+ MaxEncodedLen
+ TypeInfo;
+ type HoldIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy;
+
/// Max number of items to destroy per `destroy_accounts` and `destroy_approvals` call.
///
/// Must be configured to result in a weight that makes each call fit in a block.
diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs
index 13d5c607a34f2..51ad636e06766 100644
--- a/frame/assets/src/tests.rs
+++ b/frame/assets/src/tests.rs
@@ -37,11 +37,14 @@ fn asset_ids() -> Vec {
fn basic_minting_should_work() {
new_test_ext().execute_with(|| {
assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1));
+ assert_ok!(Assets::force_create(RuntimeOrigin::root(), 1, 1, true, 1));
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100));
assert_eq!(Assets::balance(0, 1), 100);
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 100));
assert_eq!(Assets::balance(0, 2), 100);
- assert_eq!(asset_ids(), vec![0, 999]);
+ assert_eq!(asset_ids(), vec![0, 1, 999]);
+ assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100));
+ assert_eq!(Assets::account_balances(1), vec![(0, 100), (999, 100), (1, 100)]);
});
}
diff --git a/frame/multi-asset-treasury/Cargo.toml b/frame/multi-asset-treasury/Cargo.toml
new file mode 100644
index 0000000000000..402e960d71948
--- /dev/null
+++ b/frame/multi-asset-treasury/Cargo.toml
@@ -0,0 +1,54 @@
+[package]
+name = "pallet-multi-asset-treasury"
+version = "4.0.0-dev"
+authors = ["Parity Technologies "]
+edition = "2021"
+license = "Apache-2.0"
+homepage = "https://substrate.io"
+repository = "https://github.com/paritytech/substrate/"
+description = "FRAME pallet to manage treasury"
+readme = "README.md"
+
+[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",
+ "max-encoded-len",
+] }
+impl-trait-for-tuples = "0.2.2"
+scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
+serde = { version = "1.0.136", features = ["derive"], optional = true }
+frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" }
+frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
+frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
+pallet-balances = { version = "4.0.0-dev", default-features = false, path = "../balances" }
+pallet-assets = { version = "4.0.0-dev", default-features = false, path = "../assets" }
+sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" }
+sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" }
+
+[dev-dependencies]
+sp-core = { version = "7.0.0", path = "../../primitives/core" }
+sp-io = { version = "7.0.0", path = "../../primitives/io" }
+
+[features]
+default = ["std"]
+std = [
+ "frame-benchmarking?/std",
+ "codec/std",
+ "frame-support/std",
+ "frame-system/std",
+ "pallet-balances/std",
+ "pallet-assets/std",
+ "scale-info/std",
+ "serde",
+ "sp-runtime/std",
+ "sp-std/std",
+]
+runtime-benchmarks = [
+ "frame-benchmarking/runtime-benchmarks",
+ "frame-support/runtime-benchmarks",
+ "frame-system/runtime-benchmarks",
+]
+try-runtime = ["frame-support/try-runtime"]
diff --git a/frame/multi-asset-treasury/README.md b/frame/multi-asset-treasury/README.md
new file mode 100644
index 0000000000000..4945d79d14296
--- /dev/null
+++ b/frame/multi-asset-treasury/README.md
@@ -0,0 +1,31 @@
+# Treasury Pallet
+
+The Treasury pallet provides a "pot" of funds that can be managed by stakeholders in the system and
+a structure for making spending proposals from this pot.
+
+## Overview
+
+The Treasury Pallet itself provides the pot to store funds, and a means for stakeholders to propose,
+approve, and deny expenditures. The chain will need to provide a method (e.g.inflation, fees) for
+collecting funds.
+
+By way of example, the Council could vote to fund the Treasury with a portion of the block reward
+and use the funds to pay developers.
+
+### Terminology
+
+- **Proposal:** A suggestion to allocate funds from the pot to a beneficiary.
+- **Beneficiary:** An account who will receive the funds from a proposal if the proposal is
+ approved.
+- **Deposit:** Funds that a proposer must lock when making a proposal. The deposit will be returned
+ or slashed if the proposal is approved or rejected respectively.
+- **Pot:** Unspent funds accumulated by the treasury pallet.
+
+## Interface
+
+### Dispatchable Functions
+
+General spending/proposal protocol:
+- `propose_spend` - Make a spending proposal and stake the required deposit.
+- `reject_proposal` - Reject a proposal, slashing the deposit.
+- `approve_proposal` - Accept the proposal, returning the deposit.
diff --git a/frame/multi-asset-treasury/src/benchmarking.rs b/frame/multi-asset-treasury/src/benchmarking.rs
new file mode 100644
index 0000000000000..667008e09eea4
--- /dev/null
+++ b/frame/multi-asset-treasury/src/benchmarking.rs
@@ -0,0 +1,144 @@
+// 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.
+
+//! Treasury pallet benchmarking.
+
+#![cfg(feature = "runtime-benchmarks")]
+
+use super::{Pallet as Treasury, *};
+
+use frame_benchmarking::v1::{account, benchmarks_instance_pallet, BenchmarkError};
+use frame_support::{
+ dispatch::UnfilteredDispatchable,
+ ensure,
+ traits::{EnsureOrigin, OnInitialize},
+};
+use frame_system::RawOrigin;
+use sp_runtime::Saturating;
+
+const SEED: u32 = 0;
+
+// Create the pre-requisite information needed to create a treasury `propose_spend`.
+fn setup_proposal, I: 'static>(
+ u: u32,
+) -> (T::AccountId, BalanceOf, AccountIdLookupOf) {
+ let caller = account("caller", u, SEED);
+ let value: BalanceOf = T::ProposalBondMinimum::get().saturating_mul(100u32.into());
+ let _ = T::Currency::set_balance(&caller, value);
+ let beneficiary = account("beneficiary", u, SEED);
+ let beneficiary_lookup = T::Lookup::unlookup(beneficiary);
+ (caller, value, beneficiary_lookup)
+}
+
+// Create proposals that are approved for use in `on_initialize`.
+fn create_approved_proposals, I: 'static>(n: u32) -> Result<(), &'static str> {
+ for i in 0..n {
+ let (caller, value, lookup) = setup_proposal::(i);
+ Treasury::::propose_spend(RawOrigin::Signed(caller).into(), value, lookup)?;
+ let proposal_id = >::get() - 1;
+ Treasury::::approve_proposal(RawOrigin::Root.into(), proposal_id)?;
+ }
+ ensure!(>::get().len() == n as usize, "Not all approved");
+ Ok(())
+}
+
+fn setup_pot_account, I: 'static>() {
+ let pot_account = Treasury::::account_id();
+ let value = >::minimum_balance()
+ .saturating_mul(1_000_000_000u32.into());
+ let _ = T::Currency::set_balance(&pot_account, value);
+}
+
+fn assert_last_event, I: 'static>(generic_event: >::RuntimeEvent) {
+ frame_system::Pallet::::assert_last_event(generic_event.into());
+}
+
+benchmarks_instance_pallet! {
+ // This benchmark is short-circuited if `SpendOrigin` cannot provide
+ // a successful origin, in which case `spend` is un-callable and can use weight=0.
+ spend {
+ let (_, value, beneficiary_lookup) = setup_proposal::(SEED);
+ let origin = T::SpendOrigin::try_successful_origin();
+ let beneficiary = T::Lookup::lookup(beneficiary_lookup.clone()).unwrap();
+ let call = Call::::spend { amount: value, beneficiary: beneficiary_lookup };
+ }: {
+ if let Ok(origin) = origin.clone() {
+ call.dispatch_bypass_filter(origin)?;
+ }
+ }
+ verify {
+ if origin.is_ok() {
+ assert_last_event::(Event::SpendApproved { proposal_index: 0, amount: value, beneficiary }.into())
+ }
+ }
+
+ propose_spend {
+ let (caller, value, beneficiary_lookup) = setup_proposal::(SEED);
+ // Whitelist caller account from further DB operations.
+ let caller_key = frame_system::Account::::hashed_key_for(&caller);
+ frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into());
+ }: _(RawOrigin::Signed(caller), value, beneficiary_lookup)
+
+ reject_proposal {
+ let (caller, value, beneficiary_lookup) = setup_proposal::(SEED);
+ Treasury::::propose_spend(
+ RawOrigin::Signed(caller).into(),
+ value,
+ beneficiary_lookup
+ )?;
+ let proposal_id = Treasury::::proposal_count() - 1;
+ let reject_origin =
+ T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
+ }: _(reject_origin, proposal_id)
+
+ approve_proposal {
+ let p in 0 .. T::MaxApprovals::get() - 1;
+ create_approved_proposals::(p)?;
+ let (caller, value, beneficiary_lookup) = setup_proposal::(SEED);
+ Treasury::::propose_spend(
+ RawOrigin::Signed(caller).into(),
+ value,
+ beneficiary_lookup
+ )?;
+ let proposal_id = Treasury::::proposal_count() - 1;
+ let approve_origin =
+ T::ApproveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
+ }: _(approve_origin, proposal_id)
+
+ remove_approval {
+ let (caller, value, beneficiary_lookup) = setup_proposal::(SEED);
+ Treasury::::propose_spend(
+ RawOrigin::Signed(caller).into(),
+ value,
+ beneficiary_lookup
+ )?;
+ let proposal_id = Treasury::::proposal_count() - 1;
+ Treasury::::approve_proposal(RawOrigin::Root.into(), proposal_id)?;
+ let reject_origin =
+ T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
+ }: _(reject_origin, proposal_id)
+
+ on_initialize_proposals {
+ let p in 0 .. T::MaxApprovals::get();
+ setup_pot_account::();
+ create_approved_proposals::(p)?;
+ }: {
+ Treasury::::on_initialize(T::BlockNumber::zero());
+ }
+
+ impl_benchmark_test_suite!(Treasury, crate::tests::new_test_ext(), crate::tests::Test);
+}
diff --git a/frame/multi-asset-treasury/src/lib.rs b/frame/multi-asset-treasury/src/lib.rs
new file mode 100644
index 0000000000000..23c6539876a77
--- /dev/null
+++ b/frame/multi-asset-treasury/src/lib.rs
@@ -0,0 +1,549 @@
+// 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.
+
+//! # Treasury Pallet
+//!
+//! The Treasury pallet provides a "pot" of funds that can be managed by stakeholders in the system
+//! and a structure for making spending proposals from this pot.
+//!
+//! - [`Config`]
+//! - [`Call`]
+//!
+//! ## Overview
+//!
+//! The Treasury Pallet itself provides the pot to store funds, and a means for stakeholders to
+//! propose, approve, and deny expenditures. The chain will need to provide a method (e.g.
+//! inflation, fees) for collecting funds.
+//!
+//! By way of example, the Council could vote to fund the Treasury with a portion of the block
+//! reward and use the funds to pay developers.
+//!
+//!
+//! ### Terminology
+//!
+//! - **Proposal:** A suggestion to allocate funds from the pot to a beneficiary.
+//! - **Beneficiary:** An account who will receive the funds from a proposal iff the proposal is
+//! approved.
+//! - **Deposit:** Funds that a proposer must lock when making a proposal. The deposit will be
+//! returned or slashed if the proposal is approved or rejected respectively.
+//! - **Pot:** Unspent funds accumulated by the treasury pallet.
+//!
+//! ## Interface
+//!
+//! ### Dispatchable Functions
+//!
+//! General spending/proposal protocol:
+//! - `propose_spend` - Make a spending proposal and stake the required deposit.
+//! - `reject_proposal` - Reject a proposal, slashing the deposit.
+//! - `approve_proposal` - Accept the proposal, returning the deposit.
+//! - `remove_approval` - Remove an approval, the deposit will no longer be returned.
+//!
+//! ## GenesisConfig
+//!
+//! The Treasury pallet depends on the [`GenesisConfig`].
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+mod benchmarking;
+#[cfg(test)]
+mod tests;
+pub mod weights;
+
+use codec::{Decode, Encode, MaxEncodedLen};
+use frame_support::{
+ print,
+ traits::{
+ fungible::{self, HandleImbalanceDrop, *},
+ fungibles::{
+ self, Balanced as FunsBalanced, BalancedHold as FunsBalancedHold, Credit, Debt,
+ Inspect as FunsInspect, Mutate as FunsMutate, MutateHold as FunsMutateHold,
+ Unbalanced as FunsUnbalanced, *,
+ },
+ tokens::{AssetId, Precision, Preservation},
+ Get,
+ },
+ weights::Weight,
+ PalletId,
+};
+pub use pallet::*;
+use scale_info::TypeInfo;
+use sp_runtime::{
+ traits::{AccountIdConversion, StaticLookup, Zero},
+ DispatchError, Permill, RuntimeDebug,
+};
+use sp_std::prelude::*;
+pub use weights::WeightInfo;
+
+pub type BalanceOf = <>::Assets as fungibles::Inspect<
+ ::AccountId,
+>>::Balance;
+
+pub type DebtOf = Debt<::AccountId, >::Assets>;
+pub type CreditOf =
+ Credit<::AccountId, >::Assets>;
+type AccountIdLookupOf = <::Lookup as StaticLookup>::Source;
+
+/// A trait to allow the Treasury Pallet to spend it's funds for other purposes.
+/// There is an expectation that the implementer of this trait will correctly manage
+/// the mutable variables passed to it:
+/// * `budget_remaining`: How much available funds that can be spent by the treasury. As funds are
+/// spent, you must correctly deduct from this value.
+/// * `imbalance`: Any imbalances that you create should be subsumed in here to maximize efficiency
+/// of updating the total issuance. (i.e. `deposit_creating`)
+/// * `total_weight`: Track any weight that your `spend_fund` implementation uses by updating this
+/// value.
+/// * `missed_any`: If there were items that you want to spend on, but there were not enough funds,
+/// mark this value as `true`. This will prevent the treasury from burning the excess funds.
+// #[impl_trait_for_tuples::impl_for_tuples(30)]
+pub trait SpendFunds, I: 'static = ()> {
+ fn spend_funds(
+ budget_remaining: &mut Vec<(T::AssetId, BalanceOf)>,
+ // imbalance: &mut DebtOf,
+ total_weight: &mut Weight,
+ missed_any: &mut bool,
+ ) {
+ }
+}
+
+/// A trait that allows us get all the balances which a given account has.
+pub trait AccountBalances
+where
+ AccountId: codec::Codec,
+ AssetBalance: codec::Codec,
+ AssetId: codec::Codec,
+{
+ /// Returns the list of `AssetId`s and corresponding balance that an `AccountId` has.
+ fn account_balances(account: AccountId) -> Vec<(AssetId, AssetBalance)>;
+}
+
+/// An index of a proposal. Just a `u32`.
+pub type ProposalIndex = u32;
+
+/// A spending proposal.
+#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+#[derive(Encode, Decode, Clone, PartialEq, Eq, MaxEncodedLen, RuntimeDebug, TypeInfo)]
+pub struct Proposal {
+ /// The account proposing it.
+ proposer: AccountId,
+ /// The (total) amount that should be paid if the proposal is accepted.
+ value: Balance,
+ /// The account to whom the payment should be made if the proposal is accepted.
+ beneficiary: AccountId,
+ /// The amount held on deposit (reserved) for making this proposal.
+ bond: Balance,
+ /// The asset which we are refering to.
+ asset_id: AssetId,
+}
+
+#[frame_support::pallet]
+pub mod pallet {
+ use super::*;
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
+
+ #[pallet::pallet]
+ #[pallet::generate_store(pub(super) trait Store)]
+ pub struct Pallet(PhantomData<(T, I)>);
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ type AssetId: AssetId + PartialOrd;
+
+ type Assets: FunsInspect
+ + FunsMutate
+ + FunsUnbalanced
+ + FunsBalanced
+ + FunsMutateHold
+ + FunsBalancedHold;
+
+ type AccountBalances: AccountBalances, Self::AssetId>;
+
+ /// The name for the reserve ID.
+ #[pallet::constant]
+ type HoldReason: Get<>::Reason>;
+
+ /// The name for the reserve ID.
+ #[pallet::constant]
+ type HoldAssetReason: Get<>::Reason>;
+
+ /// Origin from which approvals must come.
+ type ApproveOrigin: EnsureOrigin;
+
+ /// Origin from which rejections must come.
+ type RejectOrigin: EnsureOrigin;
+
+ /// The overarching event type.
+ type RuntimeEvent: From>
+ + IsType<::RuntimeEvent>;
+
+ /// Handler for the unbalanced decrease when slashing for a rejected proposal or bounty.
+ type OnSlash: HandleImbalanceDrop>;
+
+ /// Fraction of a proposal's value that should be bonded in order to place the proposal.
+ /// An accepted proposal gets these back. A rejected proposal does not.
+ #[pallet::constant]
+ type ProposalBond: Get;
+
+ /// Minimum amount of funds that should be placed in a deposit for making a proposal.
+ #[pallet::constant]
+ type ProposalBondMinimum: Get>;
+
+ /// Maximum amount of funds that should be placed in a deposit for making a proposal.
+ #[pallet::constant]
+ type ProposalBondMaximum: Get