Skip to content

Commit

Permalink
feature/accounts (#70)
Browse files Browse the repository at this point in the history
* add accounts module
  • Loading branch information
wangjj9219 authored Jan 20, 2020
1 parent f3e2821 commit 9e9a24b
Show file tree
Hide file tree
Showing 6 changed files with 437 additions and 5 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 41 additions & 0 deletions modules/accounts/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[package]
name = "module-accounts"
version = "0.0.1"
authors = ["Acala Developers"]
edition = "2018"

[dependencies]
serde = { version = "1.0", optional = true }
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate.git", default-features = false }
frame-support = { package = "frame-support", git = "https://github.com/paritytech/substrate.git", default-features = false }
system = { package = "frame-system", git = "https://github.com/paritytech/substrate.git", default-features = false }
rstd = { package = "sp-std", git = "https://github.com/paritytech/substrate.git", default-features = false }
orml-traits = { package = "orml-traits", path = "../../orml/traits", default-features = false }
orml-tokens = { package = "orml-tokens", path = "../../orml/tokens", default-features = false }
orml-currencies = { package = "orml-currencies", path = "../../orml/currencies", default-features = false }
support = { package = "module-support", path = "../support", default-features = false }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate.git", default-features = false }

[dev-dependencies]
primitives = { package = "sp-core", git = "https://github.com/paritytech/substrate.git", default-features = false }
runtime-io = { package = "sp-io", git = "https://github.com/paritytech/substrate.git", default-features = false }
pallet-balances= { package = "pallet-balances", git = "https://github.com/paritytech/substrate.git", default-features = false }
pallet-timestamp= { package = "pallet-timestamp", git = "https://github.com/paritytech/substrate.git", default-features = false }


[features]
default = ["std"]
std = [
"serde",
"codec/std",
"sp-runtime/std",
"frame-support/std",
"system/std",
"rstd/std",
"orml-traits/std",
"orml-tokens/std",
"orml-currencies/std",
"support/std",
"pallet-transaction-payment/std",
]
167 changes: 167 additions & 0 deletions modules/accounts/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#![cfg_attr(not(feature = "std"), no_std)]

use codec::{Decode, Encode};
use frame_support::{
decl_module, decl_storage,
dispatch::Dispatchable,
traits::{Currency, ExistenceRequirement, Get, OnReapAccount, OnUnbalanced, Time, WithdrawReason},
weights::DispatchInfo,
IsSubType, Parameter,
};
use orml_traits::MultiCurrency;
use rstd::prelude::*;
use sp_runtime::{
traits::{SaturatedConversion, Saturating, SignedExtension, Zero},
transaction_validity::{
InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransaction,
},
};

mod mock;
mod tests;

//type BalanceOf<T> = <<T as Trait>::Currency as MultiCurrency<<T as system::Trait>::AccountId>>::Balance;
type MomentOf<T> = <<T as Trait>::Time as Time>::Moment;
type PalletBalanceOf<T> =
<<T as pallet_transaction_payment::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;

pub trait Trait: system::Trait + pallet_transaction_payment::Trait + orml_currencies::Trait {
type FreeTransferCount: Get<u8>;
type FreeTransferPeriod: Get<MomentOf<Self>>;
type Time: Time;
type Currency: MultiCurrency<Self::AccountId> + Send + Sync;
type Call: Parameter
+ Dispatchable<Origin = <Self as system::Trait>::Origin>
+ IsSubType<orml_currencies::Module<Self>, Self>;
}

decl_storage! {
trait Store for Module<T: Trait> as Accounts {
LastFreeTransfers get(fn last_free_transfers): map T::AccountId => Vec<MomentOf<T>>;
}
}

decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
const FreeTransferCount: u8 = T::FreeTransferCount::get();
const FreeTransferPeriod: MomentOf<T> = T::FreeTransferPeriod::get();
}
}

impl<T: Trait> Module<T> {
pub fn try_free_transfer(who: &T::AccountId) -> bool {
let mut last_free_transfer = Self::last_free_transfers(who);
let now = T::Time::now();
let free_transfer_period = T::FreeTransferPeriod::get();

// remove all the expired entries
last_free_transfer.retain(|&x| x.saturating_add(free_transfer_period) > now);

// check if can transfer for free
if last_free_transfer.len() < T::FreeTransferCount::get() as usize {
// add entry to last_free_transfer
last_free_transfer.push(now);
<LastFreeTransfers<T>>::insert(who, last_free_transfer);
true
} else {
false
}
}
}

impl<T: Trait> OnReapAccount<T::AccountId> for Module<T> {
fn on_reap_account(who: &T::AccountId) {
<LastFreeTransfers<T>>::remove(who);
}
}

/// Require the transactor pay for themselves and maybe include a tip to gain additional priority
/// in the queue.
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(#[codec(compact)] PalletBalanceOf<T>);

impl<T: Trait + Send + Sync> ChargeTransactionPayment<T> {
/// utility constructor. Used only in client/factory code.
pub fn from(fee: PalletBalanceOf<T>) -> Self {
Self(fee)
}
}

impl<T: Trait + Send + Sync> rstd::fmt::Debug for ChargeTransactionPayment<T> {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
write!(f, "ChargeTransactionPayment<{:?}>", self.0)
}
#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
Ok(())
}
}

impl<T: Trait + Send + Sync> SignedExtension for ChargeTransactionPayment<T>
where
PalletBalanceOf<T>: Send + Sync,
{
type AccountId = T::AccountId;
type Call = <T as Trait>::Call;
type AdditionalSigned = ();
type DispatchInfo = DispatchInfo;
type Pre = ();
fn additional_signed(&self) -> rstd::result::Result<(), TransactionValidityError> {
Ok(())
}

fn validate(
&self,
who: &Self::AccountId,
call: &Self::Call,
info: Self::DispatchInfo,
len: usize,
) -> TransactionValidity {
// pay any fees.
let tip = self.0;
let fee: PalletBalanceOf<T> =
pallet_transaction_payment::ChargeTransactionPayment::<T>::compute_fee(len as u32, info, tip);

// check call type
let call = match call.is_sub_type() {
Some(call) => call,
None => return Ok(ValidTransaction::default()),
};
let skip_pay_fee = match call {
orml_currencies::Call::transfer(..) => {
// call try_free_transfer
if <Module<T>>::try_free_transfer(who) {
true
} else {
false
}
}
_ => false,
};

// skip payment withdraw if match conditions
if !skip_pay_fee {
let imbalance = match <T as pallet_transaction_payment::Trait>::Currency::withdraw(
who,
fee,
if tip.is_zero() {
WithdrawReason::TransactionPayment.into()
} else {
WithdrawReason::TransactionPayment | WithdrawReason::Tip
},
ExistenceRequirement::KeepAlive,
) {
Ok(imbalance) => imbalance,
Err(_) => return InvalidTransaction::Payment.into(),
};
<T as pallet_transaction_payment::Trait>::OnTransactionPayment::on_unbalanced(imbalance);
}

let mut r = ValidTransaction::default();
// NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which
// will be a bit more than setting the priority to tip. For now, this is enough.
r.priority = fee.saturated_into::<TransactionPriority>();
Ok(r)
}
}
Loading

0 comments on commit 9e9a24b

Please sign in to comment.