#![cfg_attr(not(feature = "std"), no_std)]
use codec::{Decode, Encode, HasCompact, Input, Output, Error as CodecError};
use sp_runtime::{RuntimeDebug, DispatchResult, DispatchError};
use sp_runtime::traits::{
CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Member, One, Saturating, AtLeast32Bit,
Zero, Bounded,
};
use sp_std::prelude::*;
use sp_std::{cmp, result, fmt::Debug};
use frame_support::{
decl_event, decl_module, decl_storage, ensure, decl_error,
traits::{
Currency, ExistenceRequirement, Imbalance, LockIdentifier, LockableCurrency, ReservableCurrency,
SignedImbalance, WithdrawReason, WithdrawReasons, TryDrop, BalanceStatus,
},
Parameter, StorageMap,
};
use frame_system::{self as system, ensure_signed, ensure_root};
mod mock;
mod tests;
pub use self::imbalances::{NegativeImbalance, PositiveImbalance};
pub trait Trait: frame_system::Trait {
type Balance: Parameter
+ Member
+ AtLeast32Bit
+ Default
+ Copy
+ MaybeSerializeDeserialize
+ Debug;
type AssetId: Parameter + Member + AtLeast32Bit + Default + Copy;
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
}
pub trait Subtrait: frame_system::Trait {
type Balance: Parameter
+ Member
+ AtLeast32Bit
+ Default
+ Copy
+ MaybeSerializeDeserialize
+ Debug;
type AssetId: Parameter + Member + AtLeast32Bit + Default + Copy;
}
impl<T: Trait> Subtrait for T {
type Balance = T::Balance;
type AssetId = T::AssetId;
}
#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)]
pub struct AssetOptions<Balance: HasCompact, AccountId> {
#[codec(compact)]
pub initial_issuance: Balance,
pub permissions: PermissionLatest<AccountId>,
}
#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)]
pub enum Owner<AccountId> {
None,
Address(AccountId),
}
impl<AccountId> Default for Owner<AccountId> {
fn default() -> Self {
Owner::None
}
}
#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)]
pub struct PermissionsV1<AccountId> {
pub update: Owner<AccountId>,
pub mint: Owner<AccountId>,
pub burn: Owner<AccountId>,
}
#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)]
#[repr(u8)]
enum PermissionVersionNumber {
V1 = 0,
}
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
pub enum PermissionVersions<AccountId> {
V1(PermissionsV1<AccountId>),
}
pub enum PermissionType {
Burn,
Mint,
Update,
}
pub type PermissionLatest<AccountId> = PermissionsV1<AccountId>;
impl<AccountId> Default for PermissionVersions<AccountId> {
fn default() -> Self {
PermissionVersions::V1(Default::default())
}
}
impl<AccountId: Encode> Encode for PermissionVersions<AccountId> {
fn encode_to<T: Output>(&self, dest: &mut T) {
match self {
PermissionVersions::V1(payload) => {
dest.push(&PermissionVersionNumber::V1);
dest.push(payload);
},
}
}
}
impl<AccountId: Encode> codec::EncodeLike for PermissionVersions<AccountId> {}
impl<AccountId: Decode> Decode for PermissionVersions<AccountId> {
fn decode<I: Input>(input: &mut I) -> core::result::Result<Self, CodecError> {
let version = PermissionVersionNumber::decode(input)?;
Ok(
match version {
PermissionVersionNumber::V1 => PermissionVersions::V1(Decode::decode(input)?)
}
)
}
}
impl<AccountId> Default for PermissionsV1<AccountId> {
fn default() -> Self {
PermissionsV1 {
update: Owner::None,
mint: Owner::None,
burn: Owner::None,
}
}
}
impl<AccountId> Into<PermissionLatest<AccountId>> for PermissionVersions<AccountId> {
fn into(self) -> PermissionLatest<AccountId> {
match self {
PermissionVersions::V1(v1) => v1,
}
}
}
impl<AccountId> Into<PermissionVersions<AccountId>> for PermissionLatest<AccountId> {
fn into(self) -> PermissionVersions<AccountId> {
PermissionVersions::V1(self)
}
}
decl_error! {
pub enum Error for Module<T: Trait> {
NoIdAvailable,
ZeroAmount,
NoUpdatePermission,
NoMintPermission,
NoBurnPermission,
TotalMintingOverflow,
FreeMintingOverflow,
TotalBurningUnderflow,
FreeBurningUnderflow,
IdAlreadyTaken,
IdUnavailable,
InsufficientBalance,
LiquidityRestrictions,
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
type Error = Error<T>;
fn deposit_event() = default;
#[weight = frame_support::weights::SimpleDispatchInfo::default()]
fn create(origin, options: AssetOptions<T::Balance, T::AccountId>) -> DispatchResult {
let origin = ensure_signed(origin)?;
Self::create_asset(None, Some(origin), options)
}
#[weight = frame_support::weights::SimpleDispatchInfo::default()]
pub fn transfer(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, #[compact] amount: T::Balance) {
let origin = ensure_signed(origin)?;
ensure!(!amount.is_zero(), Error::<T>::ZeroAmount);
Self::make_transfer_with_event(&asset_id, &origin, &to, amount)?;
}
#[weight = frame_support::weights::SimpleDispatchInfo::default()]
fn update_permission(
origin,
#[compact] asset_id: T::AssetId,
new_permission: PermissionLatest<T::AccountId>
) -> DispatchResult {
let origin = ensure_signed(origin)?;
let permissions: PermissionVersions<T::AccountId> = new_permission.into();
if Self::check_permission(&asset_id, &origin, &PermissionType::Update) {
<Permissions<T>>::insert(asset_id, &permissions);
Self::deposit_event(RawEvent::PermissionUpdated(asset_id, permissions.into()));
Ok(())
} else {
Err(Error::<T>::NoUpdatePermission)?
}
}
#[weight = frame_support::weights::SimpleDispatchInfo::default()]
fn mint(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, amount: T::Balance) -> DispatchResult {
let who = ensure_signed(origin)?;
Self::mint_free(&asset_id, &who, &to, &amount)?;
Self::deposit_event(RawEvent::Minted(asset_id, to, amount));
Ok(())
}
#[weight = frame_support::weights::SimpleDispatchInfo::default()]
fn burn(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, amount: T::Balance) -> DispatchResult {
let who = ensure_signed(origin)?;
Self::burn_free(&asset_id, &who, &to, &amount)?;
Self::deposit_event(RawEvent::Burned(asset_id, to, amount));
Ok(())
}
#[weight = frame_support::weights::SimpleDispatchInfo::default()]
fn create_reserved(
origin,
asset_id: T::AssetId,
options: AssetOptions<T::Balance, T::AccountId>
) -> DispatchResult {
ensure_root(origin)?;
Self::create_asset(Some(asset_id), None, options)
}
}
}
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
pub struct BalanceLock<Balance> {
pub id: LockIdentifier,
pub amount: Balance,
pub reasons: WithdrawReasons,
}
decl_storage! {
trait Store for Module<T: Trait> as GenericAsset {
pub TotalIssuance get(fn total_issuance) build(|config: &GenesisConfig<T>| {
let issuance = config.initial_balance * (config.endowed_accounts.len() as u32).into();
config.assets.iter().map(|id| (id.clone(), issuance)).collect::<Vec<_>>()
}): map hasher(twox_64_concat) T::AssetId => T::Balance;
pub FreeBalance:
double_map hasher(twox_64_concat) T::AssetId, hasher(blake2_128_concat) T::AccountId => T::Balance;
pub ReservedBalance:
double_map hasher(twox_64_concat) T::AssetId, hasher(blake2_128_concat) T::AccountId => T::Balance;
pub NextAssetId get(fn next_asset_id) config(): T::AssetId;
pub Permissions get(fn get_permission):
map hasher(twox_64_concat) T::AssetId => PermissionVersions<T::AccountId>;
pub Locks get(fn locks):
map hasher(blake2_128_concat) T::AccountId => Vec<BalanceLock<T::Balance>>;
pub StakingAssetId get(fn staking_asset_id) config(): T::AssetId;
pub SpendingAssetId get(fn spending_asset_id) config(): T::AssetId;
}
add_extra_genesis {
config(assets): Vec<T::AssetId>;
config(initial_balance): T::Balance;
config(endowed_accounts): Vec<T::AccountId>;
build(|config: &GenesisConfig<T>| {
config.assets.iter().for_each(|asset_id| {
config.endowed_accounts.iter().for_each(|account_id| {
<FreeBalance<T>>::insert(asset_id, account_id, &config.initial_balance);
});
});
});
}
}
decl_event!(
pub enum Event<T> where
<T as frame_system::Trait>::AccountId,
<T as Trait>::Balance,
<T as Trait>::AssetId,
AssetOptions = AssetOptions<<T as Trait>::Balance, <T as frame_system::Trait>::AccountId>
{
Created(AssetId, AccountId, AssetOptions),
Transferred(AssetId, AccountId, AccountId, Balance),
PermissionUpdated(AssetId, PermissionLatest<AccountId>),
Minted(AssetId, AccountId, Balance),
Burned(AssetId, AccountId, Balance),
}
);
impl<T: Trait> Module<T> {
pub fn total_balance(asset_id: &T::AssetId, who: &T::AccountId) -> T::Balance {
Self::free_balance(asset_id, who) + Self::reserved_balance(asset_id, who)
}
pub fn free_balance(asset_id: &T::AssetId, who: &T::AccountId) -> T::Balance {
<FreeBalance<T>>::get(asset_id, who)
}
pub fn reserved_balance(asset_id: &T::AssetId, who: &T::AccountId) -> T::Balance {
<ReservedBalance<T>>::get(asset_id, who)
}
pub fn mint_free(
asset_id: &T::AssetId,
who: &T::AccountId,
to: &T::AccountId,
amount: &T::Balance,
) -> DispatchResult {
if Self::check_permission(asset_id, who, &PermissionType::Mint) {
let original_free_balance = Self::free_balance(&asset_id, &to);
let current_total_issuance = <TotalIssuance<T>>::get(asset_id);
let new_total_issuance = current_total_issuance.checked_add(&amount)
.ok_or(Error::<T>::TotalMintingOverflow)?;
let value = original_free_balance.checked_add(&amount)
.ok_or(Error::<T>::FreeMintingOverflow)?;
<TotalIssuance<T>>::insert(asset_id, new_total_issuance);
Self::set_free_balance(&asset_id, &to, value);
Ok(())
} else {
Err(Error::<T>::NoMintPermission)?
}
}
pub fn burn_free(
asset_id: &T::AssetId,
who: &T::AccountId,
to: &T::AccountId,
amount: &T::Balance,
) -> DispatchResult {
if Self::check_permission(asset_id, who, &PermissionType::Burn) {
let original_free_balance = Self::free_balance(asset_id, to);
let current_total_issuance = <TotalIssuance<T>>::get(asset_id);
let new_total_issuance = current_total_issuance.checked_sub(amount)
.ok_or(Error::<T>::TotalBurningUnderflow)?;
let value = original_free_balance.checked_sub(amount)
.ok_or(Error::<T>::FreeBurningUnderflow)?;
<TotalIssuance<T>>::insert(asset_id, new_total_issuance);
Self::set_free_balance(asset_id, to, value);
Ok(())
} else {
Err(Error::<T>::NoBurnPermission)?
}
}
pub fn create_asset(
asset_id: Option<T::AssetId>,
from_account: Option<T::AccountId>,
options: AssetOptions<T::Balance, T::AccountId>,
) -> DispatchResult {
let asset_id = if let Some(asset_id) = asset_id {
ensure!(!<TotalIssuance<T>>::contains_key(&asset_id), Error::<T>::IdAlreadyTaken);
ensure!(asset_id < Self::next_asset_id(), Error::<T>::IdUnavailable);
asset_id
} else {
let asset_id = Self::next_asset_id();
let next_id = asset_id
.checked_add(&One::one())
.ok_or(Error::<T>::NoIdAvailable)?;
<NextAssetId<T>>::put(next_id);
asset_id
};
let account_id = from_account.unwrap_or_default();
let permissions: PermissionVersions<T::AccountId> = options.permissions.clone().into();
<TotalIssuance<T>>::insert(asset_id, &options.initial_issuance);
<FreeBalance<T>>::insert(&asset_id, &account_id, &options.initial_issuance);
<Permissions<T>>::insert(&asset_id, permissions);
Self::deposit_event(RawEvent::Created(asset_id, account_id, options));
Ok(())
}
pub fn make_transfer(
asset_id: &T::AssetId,
from: &T::AccountId,
to: &T::AccountId,
amount: T::Balance
) -> DispatchResult {
let new_balance = Self::free_balance(asset_id, from)
.checked_sub(&amount)
.ok_or(Error::<T>::InsufficientBalance)?;
Self::ensure_can_withdraw(asset_id, from, amount, WithdrawReason::Transfer.into(), new_balance)?;
if from != to {
<FreeBalance<T>>::mutate(asset_id, from, |balance| *balance -= amount);
<FreeBalance<T>>::mutate(asset_id, to, |balance| *balance += amount);
}
Ok(())
}
pub fn make_transfer_with_event(
asset_id: &T::AssetId,
from: &T::AccountId,
to: &T::AccountId,
amount: T::Balance,
) -> DispatchResult {
Self::make_transfer(asset_id, from, to, amount)?;
if from != to {
Self::deposit_event(RawEvent::Transferred(*asset_id, from.clone(), to.clone(), amount));
}
Ok(())
}
pub fn reserve(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance)
-> DispatchResult
{
let original_reserve_balance = Self::reserved_balance(asset_id, who);
let original_free_balance = Self::free_balance(asset_id, who);
if original_free_balance < amount {
Err(Error::<T>::InsufficientBalance)?
}
let new_reserve_balance = original_reserve_balance + amount;
Self::set_reserved_balance(asset_id, who, new_reserve_balance);
let new_free_balance = original_free_balance - amount;
Self::set_free_balance(asset_id, who, new_free_balance);
Ok(())
}
pub fn unreserve(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> T::Balance {
let b = Self::reserved_balance(asset_id, who);
let actual = sp_std::cmp::min(b, amount);
let original_free_balance = Self::free_balance(asset_id, who);
let new_free_balance = original_free_balance + actual;
Self::set_free_balance(asset_id, who, new_free_balance);
Self::set_reserved_balance(asset_id, who, b - actual);
amount - actual
}
pub fn slash(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> Option<T::Balance> {
let free_balance = Self::free_balance(asset_id, who);
let free_slash = sp_std::cmp::min(free_balance, amount);
let new_free_balance = free_balance - free_slash;
Self::set_free_balance(asset_id, who, new_free_balance);
if free_slash < amount {
Self::slash_reserved(asset_id, who, amount - free_slash)
} else {
None
}
}
pub fn slash_reserved(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> Option<T::Balance> {
let original_reserve_balance = Self::reserved_balance(asset_id, who);
let slash = sp_std::cmp::min(original_reserve_balance, amount);
let new_reserve_balance = original_reserve_balance - slash;
Self::set_reserved_balance(asset_id, who, new_reserve_balance);
if amount == slash {
None
} else {
Some(amount - slash)
}
}
pub fn repatriate_reserved(
asset_id: &T::AssetId,
who: &T::AccountId,
beneficiary: &T::AccountId,
amount: T::Balance,
status: BalanceStatus,
) -> T::Balance {
let b = Self::reserved_balance(asset_id, who);
let slash = sp_std::cmp::min(b, amount);
match status {
BalanceStatus::Free => {
let original_free_balance = Self::free_balance(asset_id, beneficiary);
let new_free_balance = original_free_balance + slash;
Self::set_free_balance(asset_id, beneficiary, new_free_balance);
}
BalanceStatus::Reserved => {
let original_reserved_balance = Self::reserved_balance(asset_id, beneficiary);
let new_reserved_balance = original_reserved_balance + slash;
Self::set_reserved_balance(asset_id, beneficiary, new_reserved_balance);
}
}
let new_reserve_balance = b - slash;
Self::set_reserved_balance(asset_id, who, new_reserve_balance);
amount - slash
}
pub fn check_permission(asset_id: &T::AssetId, who: &T::AccountId, what: &PermissionType) -> bool {
let permission_versions: PermissionVersions<T::AccountId> = Self::get_permission(asset_id);
let permission = permission_versions.into();
match (what, permission) {
(
PermissionType::Burn,
PermissionLatest {
burn: Owner::Address(account),
..
},
) => account == *who,
(
PermissionType::Mint,
PermissionLatest {
mint: Owner::Address(account),
..
},
) => account == *who,
(
PermissionType::Update,
PermissionLatest {
update: Owner::Address(account),
..
},
) => account == *who,
_ => false,
}
}
pub fn ensure_can_withdraw(
asset_id: &T::AssetId,
who: &T::AccountId,
_amount: T::Balance,
reasons: WithdrawReasons,
new_balance: T::Balance,
) -> DispatchResult {
if asset_id != &Self::staking_asset_id() {
return Ok(());
}
let locks = Self::locks(who);
if locks.is_empty() {
return Ok(());
}
if Self::locks(who)
.into_iter().all(|l| new_balance >= l.amount || !l.reasons.intersects(reasons))
{
Ok(())
} else {
Err(Error::<T>::LiquidityRestrictions)?
}
}
fn set_reserved_balance(asset_id: &T::AssetId, who: &T::AccountId, balance: T::Balance) {
<ReservedBalance<T>>::insert(asset_id, who, &balance);
}
fn set_free_balance(asset_id: &T::AssetId, who: &T::AccountId, balance: T::Balance) {
<FreeBalance<T>>::insert(asset_id, who, &balance);
}
fn set_lock(
id: LockIdentifier,
who: &T::AccountId,
amount: T::Balance,
reasons: WithdrawReasons,
) {
let mut new_lock = Some(BalanceLock {
id,
amount,
reasons,
});
let mut locks = <Module<T>>::locks(who)
.into_iter()
.filter_map(|l| {
if l.id == id {
new_lock.take()
} else {
Some(l)
}
})
.collect::<Vec<_>>();
if let Some(lock) = new_lock {
locks.push(lock)
}
<Locks<T>>::insert(who, locks);
}
fn extend_lock(
id: LockIdentifier,
who: &T::AccountId,
amount: T::Balance,
reasons: WithdrawReasons,
) {
let mut new_lock = Some(BalanceLock {
id,
amount,
reasons,
});
let mut locks = <Module<T>>::locks(who)
.into_iter()
.filter_map(|l| {
if l.id == id {
new_lock.take().map(|nl| BalanceLock {
id: l.id,
amount: l.amount.max(nl.amount),
reasons: l.reasons | nl.reasons,
})
} else {
Some(l)
}
})
.collect::<Vec<_>>();
if let Some(lock) = new_lock {
locks.push(lock)
}
<Locks<T>>::insert(who, locks);
}
fn remove_lock(id: LockIdentifier, who: &T::AccountId) {
let mut locks = <Module<T>>::locks(who);
locks.retain(|l| l.id != id);
<Locks<T>>::insert(who, locks);
}
}
pub trait AssetIdProvider {
type AssetId;
fn asset_id() -> Self::AssetId;
}
mod imbalances {
use super::{
result, AssetIdProvider, Imbalance, Saturating, StorageMap, Subtrait, Zero, TryDrop
};
use sp_std::mem;
#[must_use]
pub struct PositiveImbalance<T: Subtrait, U: AssetIdProvider<AssetId = T::AssetId>>(
T::Balance,
sp_std::marker::PhantomData<U>,
);
impl<T, U> PositiveImbalance<T, U>
where
T: Subtrait,
U: AssetIdProvider<AssetId = T::AssetId>,
{
pub fn new(amount: T::Balance) -> Self {
PositiveImbalance(amount, Default::default())
}
}
#[must_use]
pub struct NegativeImbalance<T: Subtrait, U: AssetIdProvider<AssetId = T::AssetId>>(
T::Balance,
sp_std::marker::PhantomData<U>,
);
impl<T, U> NegativeImbalance<T, U>
where
T: Subtrait,
U: AssetIdProvider<AssetId = T::AssetId>,
{
pub fn new(amount: T::Balance) -> Self {
NegativeImbalance(amount, Default::default())
}
}
impl<T, U> TryDrop for PositiveImbalance<T, U>
where
T: Subtrait,
U: AssetIdProvider<AssetId = T::AssetId>,
{
fn try_drop(self) -> result::Result<(), Self> {
self.drop_zero()
}
}
impl<T, U> Imbalance<T::Balance> for PositiveImbalance<T, U>
where
T: Subtrait,
U: AssetIdProvider<AssetId = T::AssetId>,
{
type Opposite = NegativeImbalance<T, U>;
fn zero() -> Self {
Self::new(Zero::zero())
}
fn drop_zero(self) -> result::Result<(), Self> {
if self.0.is_zero() {
Ok(())
} else {
Err(self)
}
}
fn split(self, amount: T::Balance) -> (Self, Self) {
let first = self.0.min(amount);
let second = self.0 - first;
mem::forget(self);
(Self::new(first), Self::new(second))
}
fn merge(mut self, other: Self) -> Self {
self.0 = self.0.saturating_add(other.0);
mem::forget(other);
self
}
fn subsume(&mut self, other: Self) {
self.0 = self.0.saturating_add(other.0);
mem::forget(other);
}
fn offset(self, other: Self::Opposite) -> result::Result<Self, Self::Opposite> {
let (a, b) = (self.0, other.0);
mem::forget((self, other));
if a >= b {
Ok(Self::new(a - b))
} else {
Err(NegativeImbalance::new(b - a))
}
}
fn peek(&self) -> T::Balance {
self.0.clone()
}
}
impl<T, U> TryDrop for NegativeImbalance<T, U>
where
T: Subtrait,
U: AssetIdProvider<AssetId = T::AssetId>,
{
fn try_drop(self) -> result::Result<(), Self> {
self.drop_zero()
}
}
impl<T, U> Imbalance<T::Balance> for NegativeImbalance<T, U>
where
T: Subtrait,
U: AssetIdProvider<AssetId = T::AssetId>,
{
type Opposite = PositiveImbalance<T, U>;
fn zero() -> Self {
Self::new(Zero::zero())
}
fn drop_zero(self) -> result::Result<(), Self> {
if self.0.is_zero() {
Ok(())
} else {
Err(self)
}
}
fn split(self, amount: T::Balance) -> (Self, Self) {
let first = self.0.min(amount);
let second = self.0 - first;
mem::forget(self);
(Self::new(first), Self::new(second))
}
fn merge(mut self, other: Self) -> Self {
self.0 = self.0.saturating_add(other.0);
mem::forget(other);
self
}
fn subsume(&mut self, other: Self) {
self.0 = self.0.saturating_add(other.0);
mem::forget(other);
}
fn offset(self, other: Self::Opposite) -> result::Result<Self, Self::Opposite> {
let (a, b) = (self.0, other.0);
mem::forget((self, other));
if a >= b {
Ok(Self::new(a - b))
} else {
Err(PositiveImbalance::new(b - a))
}
}
fn peek(&self) -> T::Balance {
self.0.clone()
}
}
impl<T, U> Drop for PositiveImbalance<T, U>
where
T: Subtrait,
U: AssetIdProvider<AssetId = T::AssetId>,
{
fn drop(&mut self) {
<super::TotalIssuance<super::ElevatedTrait<T>>>::mutate(&U::asset_id(), |v| *v = v.saturating_add(self.0));
}
}
impl<T, U> Drop for NegativeImbalance<T, U>
where
T: Subtrait,
U: AssetIdProvider<AssetId = T::AssetId>,
{
fn drop(&mut self) {
<super::TotalIssuance<super::ElevatedTrait<T>>>::mutate(&U::asset_id(), |v| *v = v.saturating_sub(self.0));
}
}
}
struct ElevatedTrait<T: Subtrait>(T);
impl<T: Subtrait> Clone for ElevatedTrait<T> {
fn clone(&self) -> Self {
unimplemented!()
}
}
impl<T: Subtrait> PartialEq for ElevatedTrait<T> {
fn eq(&self, _: &Self) -> bool {
unimplemented!()
}
}
impl<T: Subtrait> Eq for ElevatedTrait<T> {}
impl<T: Subtrait> frame_system::Trait for ElevatedTrait<T> {
type Origin = T::Origin;
type Call = T::Call;
type Index = T::Index;
type BlockNumber = T::BlockNumber;
type Hash = T::Hash;
type Hashing = T::Hashing;
type AccountId = T::AccountId;
type Lookup = T::Lookup;
type Header = T::Header;
type Event = ();
type BlockHashCount = T::BlockHashCount;
type MaximumBlockWeight = T::MaximumBlockWeight;
type MaximumBlockLength = T::MaximumBlockLength;
type AvailableBlockRatio = T::AvailableBlockRatio;
type Version = T::Version;
type ModuleToIndex = ();
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
}
impl<T: Subtrait> Trait for ElevatedTrait<T> {
type Balance = T::Balance;
type AssetId = T::AssetId;
type Event = ();
}
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
pub struct AssetCurrency<T, U>(sp_std::marker::PhantomData<T>, sp_std::marker::PhantomData<U>);
impl<T, U> Currency<T::AccountId> for AssetCurrency<T, U>
where
T: Trait,
U: AssetIdProvider<AssetId = T::AssetId>,
{
type Balance = T::Balance;
type PositiveImbalance = PositiveImbalance<T, U>;
type NegativeImbalance = NegativeImbalance<T, U>;
fn total_balance(who: &T::AccountId) -> Self::Balance {
Self::free_balance(&who) + Self::reserved_balance(&who)
}
fn free_balance(who: &T::AccountId) -> Self::Balance {
<Module<T>>::free_balance(&U::asset_id(), &who)
}
fn total_issuance() -> Self::Balance {
<Module<T>>::total_issuance(U::asset_id())
}
fn minimum_balance() -> Self::Balance {
Zero::zero()
}
fn transfer(
transactor: &T::AccountId,
dest: &T::AccountId,
value: Self::Balance,
_: ExistenceRequirement,
) -> DispatchResult {
<Module<T>>::make_transfer(&U::asset_id(), transactor, dest, value)
}
fn ensure_can_withdraw(
who: &T::AccountId,
amount: Self::Balance,
reasons: WithdrawReasons,
new_balance: Self::Balance,
) -> DispatchResult {
<Module<T>>::ensure_can_withdraw(&U::asset_id(), who, amount, reasons, new_balance)
}
fn withdraw(
who: &T::AccountId,
value: Self::Balance,
reasons: WithdrawReasons,
_: ExistenceRequirement,
) -> result::Result<Self::NegativeImbalance, DispatchError> {
let new_balance = Self::free_balance(who)
.checked_sub(&value)
.ok_or(Error::<T>::InsufficientBalance)?;
Self::ensure_can_withdraw(who, value, reasons, new_balance)?;
<Module<T>>::set_free_balance(&U::asset_id(), who, new_balance);
Ok(NegativeImbalance::new(value))
}
fn deposit_into_existing(
who: &T::AccountId,
value: Self::Balance,
) -> result::Result<Self::PositiveImbalance, DispatchError> {
Ok(Self::deposit_creating(who, value))
}
fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance {
let imbalance = Self::make_free_balance_be(who, Self::free_balance(who) + value);
if let SignedImbalance::Positive(p) = imbalance {
p
} else {
Self::PositiveImbalance::zero()
}
}
fn make_free_balance_be(
who: &T::AccountId,
balance: Self::Balance,
) -> SignedImbalance<Self::Balance, Self::PositiveImbalance> {
let original = <Module<T>>::free_balance(&U::asset_id(), who);
let imbalance = if original <= balance {
SignedImbalance::Positive(PositiveImbalance::new(balance - original))
} else {
SignedImbalance::Negative(NegativeImbalance::new(original - balance))
};
<Module<T>>::set_free_balance(&U::asset_id(), who, balance);
imbalance
}
fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool {
<Module<T>>::free_balance(&U::asset_id(), &who) >= value
}
fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
let remaining = <Module<T>>::slash(&U::asset_id(), who, value);
if let Some(r) = remaining {
(NegativeImbalance::new(value - r), r)
} else {
(NegativeImbalance::new(value), Zero::zero())
}
}
fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance {
<TotalIssuance<T>>::mutate(&U::asset_id(), |issued|
issued.checked_sub(&amount).unwrap_or_else(|| {
amount = *issued;
Zero::zero()
})
);
PositiveImbalance::new(amount)
}
fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance {
<TotalIssuance<T>>::mutate(&U::asset_id(), |issued|
*issued = issued.checked_add(&amount).unwrap_or_else(|| {
amount = Self::Balance::max_value() - *issued;
Self::Balance::max_value()
})
);
NegativeImbalance::new(amount)
}
}
impl<T, U> ReservableCurrency<T::AccountId> for AssetCurrency<T, U>
where
T: Trait,
U: AssetIdProvider<AssetId = T::AssetId>,
{
fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
Self::free_balance(who)
.checked_sub(&value)
.map_or(false, |new_balance|
<Module<T>>::ensure_can_withdraw(
&U::asset_id(), who, value, WithdrawReason::Reserve.into(), new_balance
).is_ok()
)
}
fn reserved_balance(who: &T::AccountId) -> Self::Balance {
<Module<T>>::reserved_balance(&U::asset_id(), &who)
}
fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult {
<Module<T>>::reserve(&U::asset_id(), who, value)
}
fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
<Module<T>>::unreserve(&U::asset_id(), who, value)
}
fn slash_reserved(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
let b = Self::reserved_balance(&who.clone());
let slash = cmp::min(b, value);
<Module<T>>::set_reserved_balance(&U::asset_id(), who, b - slash);
(NegativeImbalance::new(slash), value - slash)
}
fn repatriate_reserved(
slashed: &T::AccountId,
beneficiary: &T::AccountId,
value: Self::Balance,
status: BalanceStatus,
) -> result::Result<Self::Balance, DispatchError> {
Ok(<Module<T>>::repatriate_reserved(&U::asset_id(), slashed, beneficiary, value, status))
}
}
pub struct StakingAssetIdProvider<T>(sp_std::marker::PhantomData<T>);
impl<T: Trait> AssetIdProvider for StakingAssetIdProvider<T> {
type AssetId = T::AssetId;
fn asset_id() -> Self::AssetId {
<Module<T>>::staking_asset_id()
}
}
pub struct SpendingAssetIdProvider<T>(sp_std::marker::PhantomData<T>);
impl<T: Trait> AssetIdProvider for SpendingAssetIdProvider<T> {
type AssetId = T::AssetId;
fn asset_id() -> Self::AssetId {
<Module<T>>::spending_asset_id()
}
}
impl<T> LockableCurrency<T::AccountId> for AssetCurrency<T, StakingAssetIdProvider<T>>
where
T: Trait,
T::Balance: MaybeSerializeDeserialize + Debug,
{
type Moment = T::BlockNumber;
fn set_lock(
id: LockIdentifier,
who: &T::AccountId,
amount: T::Balance,
reasons: WithdrawReasons,
) {
<Module<T>>::set_lock(id, who, amount, reasons)
}
fn extend_lock(
id: LockIdentifier,
who: &T::AccountId,
amount: T::Balance,
reasons: WithdrawReasons,
) {
<Module<T>>::extend_lock(id, who, amount, reasons)
}
fn remove_lock(id: LockIdentifier, who: &T::AccountId) {
<Module<T>>::remove_lock(id, who)
}
}
pub type StakingAssetCurrency<T> = AssetCurrency<T, StakingAssetIdProvider<T>>;
pub type SpendingAssetCurrency<T> = AssetCurrency<T, SpendingAssetIdProvider<T>>;