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

Issue community tokens and mark one as sufficient #301

Closed
42 changes: 42 additions & 0 deletions pallets/communities/src/functions/fungibles.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use super::*;
use frame_support::traits::tokens::fungibles::{roles::Inspect, Create, Destroy};
use sp_runtime::traits::Zero;

impl<T: Config> Pallet<T> {
pub(crate) fn do_create_asset(
community_id: &CommunityIdOf<T>,
asset_id: AssetIdOf<T>,
min_balance: BalanceOf<T>,
) -> DispatchResult {
let community_account_id = Self::get_community_account_id(community_id);
let community_assets_count = <CommunityAssets<T>>::decode_len(community_id).unwrap_or_default();

T::Assets::create(
asset_id.clone(),
community_account_id,
community_assets_count.is_zero(),
min_balance,
)?;

<CommunityAssets<T>>::append(community_id, asset_id);

Ok(())
}

pub(crate) fn do_destroy_asset(community_id: &CommunityIdOf<T>, asset_id: AssetIdOf<T>) -> DispatchResult {
let community_account_id = Self::get_community_account_id(community_id);

let asset_owner = T::Assets::owner(asset_id.clone()).ok_or(Error::<T>::UnknownAsset)?;

if !<CommunityAssets<T>>::get(community_id).contains(&asset_id) || asset_owner != community_account_id {
return Err(Error::<T>::CannotDestroyUncontrolledAsset)?;
}

T::Assets::start_destroy(asset_id.clone(), Some(community_account_id))?;
T::Assets::destroy_accounts(asset_id.clone(), u32::MAX)?;
T::Assets::destroy_approvals(asset_id.clone(), u32::MAX)?;
T::Assets::finish_destroy(asset_id)?;

Ok(())
}
}
1 change: 1 addition & 0 deletions pallets/communities/src/functions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub(self) use frame_system::pallet_prelude::*;
pub(self) use types::*;

mod challenges;
mod fungibles;
mod getters;
mod membership;
mod registry;
Expand Down
89 changes: 80 additions & 9 deletions pallets/communities/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,19 +136,19 @@
//! any community member. Also, it shouldn't be possible to arbitrarily remove
//! the community admin, as some privileged calls would be impossible execute
//! thereafter.
//! - `promote_member`: Increases the rank of a member in the community. ! -
//! `demote_member`: Decreases the rank of a member in the community.
//! - `issue_token`: Creates a token that is either governance (only one per
//! community allowed) or economic. While the first economic token is
//! _"free"_," further ones would be subject to network-wide referenda.
//! - `promote_member`: Increases the rank of a member in the community.
//! - `demote_member`: Decreases the rank of a member in the community.
//! - `close_proposal`: Forcefully closes a proposal, dispatching the call when
//! approved.
//! - [`assets_transfer`][c04]: Transfers an amount of a given asset from the
//! treasury account to a beneficiary.
//! - [`balance_transfer`][c05]: Transfers funds from the treasury account to a
//! beneficiary.
//! - `set_sufficient_asset`: Marks an [asset][7] issued by the community as
//! sufficient. Only one asset at a time can be marked as such.
//! - [`create_asset`][c06]: Creates a [fungible asset][7] that is controlled by
//! the community treasury. The asset is not marked as sufficient, unless it's
//! the first one being issued.
//! - [`destroy_asset`][c07]: Destroys a [fungible asset][1] issued by the
//! community.
//! - `set_admin`: Sets an [`AccountId`][1] of the _admin_ of the community.
//! Ensures that the specified account is a member of the community.
//! - `set_voting_mechanism`: Transfers funds from the treasury account to a
Expand All @@ -170,8 +170,9 @@
//! - [`member_information`][g02]: Stores the information of a community
//! (specified by its [`CommunityId`][t00]) member (specified by it's
//! [`AccountId`][1]).
//! - [`members_count`][g03]: Store the count of community members. This
//! - [`members_count`][g03]: Stores the count of community members. This
//! simplifies the process of keeping track of members' count.
//! - [`assets`][g04]: Stores the list of assets created by the community.
//!
//! <!-- References -->
//! [1]: `frame_system::Config::AccountId`
Expand All @@ -192,11 +193,14 @@
//! [c03]: `crate::Pallet::remove_member`
//! [c04]: `crate::Pallet::assets_transfer`
//! [c05]: `crate::Pallet::balance_transfer`
//! [c06]: `crate::Pallet::create_asset`
//! [c07]: `crate::Pallet::destroy_asset`
//!
//! [g00]: `crate::Pallet::community`
//! [g01]: `crate::Pallet::metadata`
//! [g02]: `crate::Pallet::member_information`
//! [g03]: `crate::Pallet::members_count`
//! [g04]: `crate::Pallet::assets`
pub use pallet::*;

#[cfg(test)]
Expand All @@ -218,6 +222,7 @@ pub use weights::*;
pub mod pallet {
use super::*;
use frame_support::{
dispatch::Vec,
pallet_prelude::*,
traits::tokens::{fungible, fungibles},
Parameter,
Expand All @@ -243,7 +248,8 @@ pub mod pallet {
type Assets: fungibles::Inspect<Self::AccountId>
+ fungibles::Mutate<Self::AccountId>
+ fungibles::Create<Self::AccountId>
+ fungibles::Destroy<Self::AccountId>;
+ fungibles::Destroy<Self::AccountId>
+ fungibles::roles::Inspect<Self::AccountId>;

/// Type represents interactions between fungibles (i.e. assets)
type Balances: fungible::Inspect<Self::AccountId>
Expand Down Expand Up @@ -314,6 +320,13 @@ pub mod pallet {
#[pallet::getter(fn members_count)]
pub(super) type CommunityMembersCount<T> = StorageMap<_, Blake2_128Concat, CommunityIdOf<T>, u128>;

/// Stores the list of assets created by the community.
#[pallet::storage]
#[pallet::unbounded]
#[pallet::getter(fn assets)]
pub(super) type CommunityAssets<T> =
StorageMap<_, Blake2_128Concat, CommunityIdOf<T>, Vec<AssetIdOf<T>>, ValueQuery>;

// Pallets use events to inform users when important changes are made.
// https://docs.substrate.io/main-docs/build/events-errors/
#[pallet::event]
Expand Down Expand Up @@ -353,6 +366,13 @@ pub mod pallet {
/// [`CommunityId`][`Config::CommunityId`], especially if it's the
/// only member remaining. Please consider changing the admin first.
CannotRemoveAdmin,
/// The asset indicated with a specified
/// [`AssetId`][frame_system::Config::AssetId] cannot be found
UnknownAsset,
/// The asset specified with a certain
/// [`AssetId`][frame_system::Config::AssetId] is not controlled by the
/// community, therefore can't be destroyed
CannotDestroyUncontrolledAsset,
}

// Dispatchable functions allows users to interact with the pallet and invoke
Expand All @@ -361,6 +381,8 @@ pub mod pallet {
// weight and must return a DispatchResult.
#[pallet::call]
impl<T: Config> Pallet<T> {
// === Registry management ===

/// Registers an appliation as a new community, taking an
/// [existential deposit][8] used to create the community account.
///
Expand Down Expand Up @@ -423,6 +445,8 @@ pub mod pallet {
Ok(())
}

// === Memberships management ===

/// Enroll an account as a community member. In theory,
/// any community member should be able to add a member. However, this
/// can be changed to ensure it is a privileged function.
Expand Down Expand Up @@ -464,6 +488,8 @@ pub mod pallet {
Ok(())
}

// === Treasury management ===

/// Transfers an amount of a given asset from the treasury account to a
/// beneficiary.
#[pallet::call_index(4)]
Expand Down Expand Up @@ -508,5 +534,50 @@ pub mod pallet {

Ok(())
}

// === Fungibles management ===

/// Creates a [fungible asset][1] that is controlled by the community
/// treasury. The asset is not marked as sufficient, unless it's the
/// first one being issued.
///
/// [1]: https://paritytech.github.io/substrate/master/pallet_assets/index.html#terminology
#[pallet::call_index(6)]
#[pallet::weight(T::WeightInfo::create_asset())]
pub fn create_asset(
origin: OriginFor<T>,
community_id: T::CommunityId,
asset_id: AssetIdOf<T>,
min_balance: BalanceOf<T>,
) -> DispatchResult {
Self::ensure_origin_privileged(origin, &community_id)?;
Self::ensure_active(&community_id)?;

Self::do_create_asset(&community_id, asset_id, min_balance)?;

Ok(())
}

/// Destroys a [fungible asset][1] issued by the community.
///
/// **Warning:** By calling this method, you'll be entirely destroying
/// such asset, which will burn it from the holders' accounts as a
/// consequence.
///
/// [1]: https://paritytech.github.io/substrate/master/pallet_assets/index.html#terminology
#[pallet::call_index(7)]
#[pallet::weight(T::WeightInfo::destroy_asset())]
pub fn destroy_asset(
origin: OriginFor<T>,
community_id: T::CommunityId,
asset_id: AssetIdOf<T>,
) -> DispatchResult {
Self::ensure_origin_privileged(origin, &community_id)?;
Self::ensure_active(&community_id)?;

Self::do_destroy_asset(&community_id, asset_id)?;

Ok(())
}
}
}
8 changes: 5 additions & 3 deletions pallets/communities/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ use sp_runtime::{
};

type Block = frame_system::mocking::MockBlock<Test>;
type Balance = u128;
pub type Balance = u128;
pub type AccountId = u64;
pub type AssetId = u32;

// Configure a mock runtime to test the pallet.
frame_support::construct_runtime!(
Expand All @@ -35,7 +37,7 @@ impl frame_system::Config for Test {
type Nonce = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
Expand All @@ -54,7 +56,7 @@ impl frame_system::Config for Test {
impl pallet_assets::Config for Test {
type RuntimeEvent = RuntimeEvent;
type Balance = Balance;
type AssetId = u32;
type AssetId = AssetId;
type AssetIdParameter = codec::Compact<u32>;
type Currency = Balances;
type CreateOrigin = AsEnsureOriginWithArg<EnsureSigned<Self::AccountId>>;
Expand Down
Loading
Loading