Skip to content
Snippets Groups Projects
Unverified Commit ba00f235 authored by Shaopeng Wang's avatar Shaopeng Wang Committed by GitHub
Browse files

Tokens Module (#3)

* Define MultiCurrency trait.

* Declare storage.

* Impl multi currency trait for tokens module.

* Balance transfer.

* Fix typo.

* fmt

* Add transferred event.

* Update tokens trait and slash impl.

* Use value type for currency id params.

* fmt
parent 51c9fbc4
No related branches found
No related tags found
No related merge requests found
...@@ -8,8 +8,8 @@ edition = "2018" ...@@ -8,8 +8,8 @@ edition = "2018"
serde = { version = "1.0", optional = true } serde = { version = "1.0", optional = true }
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false }
sr-primitives = { git = "https://github.com/paritytech/substrate.git", default-features = false } sr-primitives = { git = "https://github.com/paritytech/substrate.git", default-features = false }
support = { package = "srml-support", git = "https://github.com/paritytech/substrate.git", default-features = false } srml-support = { package = "srml-support", git = "https://github.com/paritytech/substrate.git", default-features = false }
system = { package = "srml-system", git = "https://github.com/paritytech/substrate.git", default-features = false } srml-system = { package = "srml-system", git = "https://github.com/paritytech/substrate.git", default-features = false }
runtime_io = { package = "sr-io", git = "https://github.com/paritytech/substrate.git", default-features = false } runtime_io = { package = "sr-io", git = "https://github.com/paritytech/substrate.git", default-features = false }
rstd = { package = "sr-std", git = "https://github.com/paritytech/substrate.git", default-features = false } rstd = { package = "sr-std", git = "https://github.com/paritytech/substrate.git", default-features = false }
...@@ -22,8 +22,8 @@ std = [ ...@@ -22,8 +22,8 @@ std = [
"serde", "serde",
"codec/std", "codec/std",
"sr-primitives/std", "sr-primitives/std",
"support/std", "srml-support/std",
"system/std", "srml-system/std",
"runtime_io/std", "runtime_io/std",
"rstd/std", "rstd/std",
"traits/std", "traits/std",
......
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
use support::{decl_event, decl_module, decl_storage}; use rstd::result;
use sr_primitives::traits::{
CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Member, SimpleArithmetic, StaticLookup,
};
use srml_support::{decl_event, decl_module, decl_storage, ensure, Parameter};
// FIXME: `srml-` prefix should be used for all srml modules, but currently `srml_system`
// would cause compiling error in `decl_module!` and `construct_runtime!`
// #3295 https://github.com/paritytech/substrate/issues/3295
use srml_system::{self as system, ensure_signed};
use traits::MultiCurrency; use traits::MultiCurrency;
pub trait Trait: system::Trait { pub trait Trait: srml_system::Trait {
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>; type Event: From<Event<Self>> + Into<<Self as srml_system::Trait>::Event>;
type Balance: Parameter + Member + SimpleArithmetic + Default + Copy + MaybeSerializeDeserialize;
type CurrencyId: Parameter + Member + SimpleArithmetic + Default + Copy + MaybeSerializeDeserialize;
} }
decl_storage! { decl_storage! {
trait Store for Module<T: Trait> as Tokens { trait Store for Module<T: Trait> as Tokens {
/// The total issuance of a token type;
pub TotalIssuance get(fn total_issuance) build(|config: &GenesisConfig<T>| {
let issuance = config.initial_balance * (config.endowed_accounts.len() as u32).into();
config.tokens.iter().map(|id| (id.clone(), issuance)).collect::<Vec<_>>()
}): map T::CurrencyId => T::Balance;
/// The balance of a token type under an account.
pub Balance get(fn balance): double_map T::CurrencyId, blake2_256(T::AccountId) => T::Balance;
}
add_extra_genesis {
config(tokens): Vec<T::CurrencyId>;
config(initial_balance): T::Balance;
config(endowed_accounts): Vec<T::AccountId>;
} }
} }
decl_event!( decl_event!(
pub enum Event<T> where pub enum Event<T> where
<T as system::Trait>::AccountId <T as srml_system::Trait>::AccountId,
<T as Trait>::CurrencyId,
<T as Trait>::Balance
{ {
Dummy(AccountId), /// Token transfer success (currency_id, from, to, amount)
Transferred(CurrencyId, AccountId, AccountId, Balance),
} }
); );
...@@ -25,9 +51,90 @@ decl_module! { ...@@ -25,9 +51,90 @@ decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin { pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event() = default; fn deposit_event() = default;
/// Transfer some balance to another account.
pub fn transfer(
origin,
dest: <T::Lookup as StaticLookup>::Source,
#[compact] currency_id: T::CurrencyId,
#[compact] amount: T::Balance,
) {
let from = ensure_signed(origin)?;
let to = T::Lookup::lookup(dest)?;
<Self as MultiCurrency<_>>::transfer(currency_id, &from, &to, amount)?;
Self::deposit_event(RawEvent::Transferred(currency_id, from, to, amount));
}
} }
} }
impl<T: Trait> Module<T> {} impl<T: Trait> Module<T> {}
impl<T: Trait> MultiCurrency for Module<T> {} impl<T: Trait> MultiCurrency<T::AccountId> for Module<T> {
type Balance = T::Balance;
type CurrencyId = T::CurrencyId;
fn total_inssuance(currency_id: Self::CurrencyId) -> Self::Balance {
<TotalIssuance<T>>::get(currency_id)
}
fn balance(currency_id: Self::CurrencyId, who: &T::AccountId) -> Self::Balance {
<Balance<T>>::get(currency_id, who)
}
fn transfer(
currency_id: Self::CurrencyId,
from: &T::AccountId,
to: &T::AccountId,
amount: Self::Balance,
) -> result::Result<(), &'static str> {
ensure!(
Self::balance(currency_id, from) >= amount,
"balance too low to send amount",
);
if from != to {
<Balance<T>>::mutate(currency_id, from, |balance| *balance -= amount);
<Balance<T>>::mutate(currency_id, to, |balance| *balance += amount);
}
Ok(())
}
fn mint(
currency_id: Self::CurrencyId,
who: &T::AccountId,
amount: Self::Balance,
) -> result::Result<(), &'static str> {
ensure!(
Self::total_inssuance(currency_id).checked_add(&amount).is_some(),
"total issuance overflow",
);
<TotalIssuance<T>>::mutate(currency_id, |v| *v += amount);
<Balance<T>>::mutate(currency_id, who, |v| *v += amount);
Ok(())
}
fn burn(
currency_id: Self::CurrencyId,
who: &T::AccountId,
amount: Self::Balance,
) -> result::Result<(), &'static str> {
ensure!(
Self::balance(currency_id, who).checked_sub(&amount).is_some(),
"insufficient balance to burn",
);
<TotalIssuance<T>>::mutate(currency_id, |v| *v -= amount);
<Balance<T>>::mutate(currency_id, who, |v| *v -= amount);
Ok(())
}
fn slash(currency_id: Self::CurrencyId, who: &T::AccountId, amount: Self::Balance) -> Self::Balance {
let actual_amount = Self::balance(currency_id, who).min(amount);
<Balance<T>>::mutate(currency_id, who, |v| *v -= actual_amount);
actual_amount
}
}
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
pub trait MultiCurrency {} use codec::FullCodec;
use rstd::{fmt::Debug, result};
use sr_primitives::traits::{MaybeSerializeDeserialize, SimpleArithmetic};
/// Abstraction over a fungible multi-currency system.
pub trait MultiCurrency<AccountId> {
/// The currency identifier.
type CurrencyId: FullCodec + Copy + MaybeSerializeDeserialize + Debug;
/// The balance of an account.
type Balance: SimpleArithmetic + FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default;
// Public immutables
/// The total amount of issuance of `currency_id`.
fn total_inssuance(currency_id: Self::CurrencyId) -> Self::Balance;
/// The combined balance of `who` under `currency_id`.
fn balance(currency_id: Self::CurrencyId, who: &AccountId) -> Self::Balance;
// Public mutables
/// Transfer some amount from one account to another.
fn transfer(
currency_id: Self::CurrencyId,
from: &AccountId,
to: &AccountId,
amount: Self::Balance,
) -> result::Result<(), &'static str>;
/// Mint and increase the total inssuance of `currency_id` by adding `amount` to `who`.
fn mint(currency_id: Self::CurrencyId, who: &AccountId, amount: Self::Balance) -> result::Result<(), &'static str>;
/// Burn and reduce the total inssuance of `currency_id` by moving `amount` from `who`.
fn burn(currency_id: Self::CurrencyId, who: &AccountId, amount: Self::Balance) -> result::Result<(), &'static str>;
/// Deduct the balance of `who` by up to `amount`.
///
/// As much funds up to `amount` will be deducted as possible, the actual slashed amount will be returned.
fn slash(currency_id: Self::CurrencyId, who: &AccountId, amount: Self::Balance) -> Self::Balance;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment