Add Balances Locks (#197)

* Add Balances Locks

* Pass fmt

* Add tests for Balances Locks

In order to write this test, I just added the new staking::BondCall :).

* .

* In case you want to run multiple tests at the same time

* Return Result in integration test
This commit is contained in:
Liu-Cheng Xu
2021-01-15 17:34:35 +08:00
committed by GitHub
parent 754b184363
commit 5a0201c130
2 changed files with 136 additions and 1 deletions
+86 -1
View File
@@ -25,7 +25,10 @@ use codec::{
Encode,
};
use core::marker::PhantomData;
use frame_support::Parameter;
use frame_support::{
traits::LockIdentifier,
Parameter,
};
use sp_runtime::traits::{
AtLeast32Bit,
MaybeSerialize,
@@ -80,6 +83,47 @@ pub struct TotalIssuanceStore<T: Balances> {
pub _runtime: PhantomData<T>,
}
/// The locks of the balances module.
#[derive(Clone, Debug, Eq, PartialEq, Store, Encode, Decode)]
pub struct LocksStore<'a, T: Balances> {
#[store(returns = Vec<BalanceLock<T::Balance>>)]
/// Account to retrieve the balance locks for.
pub account_id: &'a T::AccountId,
}
/// A single lock on a balance. There can be many of these on an account and they "overlap", so the
/// same balance is frozen by multiple locks.
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
pub struct BalanceLock<Balance> {
/// An identifier for this lock. Only one lock may be in existence for each identifier.
pub id: LockIdentifier,
/// The amount which the free balance may not drop below when this lock is in effect.
pub amount: Balance,
/// If true, then the lock remains in effect even for payment of transaction fees.
pub reasons: Reasons,
}
impl<Balance: Debug> Debug for BalanceLock<Balance> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("BalanceLock")
.field("id", &String::from_utf8_lossy(&self.id))
.field("amount", &self.amount)
.field("reasons", &self.reasons)
.finish()
}
}
/// Simplified reasons for withdrawing balance.
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug)]
pub enum Reasons {
/// Paying system transaction fees.
Fee,
/// Any reason other than paying system transaction fees.
Misc,
/// Any reason at all.
All,
}
/// Transfer some liquid free balance to another account.
///
/// `transfer` will set the `FreeBalance` of the sender and receiver.
@@ -181,6 +225,47 @@ mod tests {
assert_ne!(info.data.free, 0);
}
#[async_std::test]
#[cfg(feature = "integration-tests")]
async fn test_state_balance_lock() -> Result<(), crate::Error> {
use crate::{
frame::staking::{
BondCallExt,
RewardDestination,
},
runtimes::KusamaRuntime as RT,
ClientBuilder,
};
env_logger::try_init().ok();
let bob = PairSigner::<RT, _>::new(AccountKeyring::Bob.pair());
let client = ClientBuilder::<RT>::new().build().await?;
client
.bond_and_watch(
&bob,
AccountKeyring::Charlie.to_account_id(),
100_000_000_000,
RewardDestination::Stash,
)
.await?;
let locks = client
.locks(&AccountKeyring::Bob.to_account_id(), None)
.await?;
assert_eq!(
locks,
vec![BalanceLock {
id: *b"staking ",
amount: 100_000_000_000,
reasons: Reasons::All,
}]
);
Ok(())
}
#[async_std::test]
async fn test_transfer_error() {
env_logger::try_init().ok();
+50
View File
@@ -196,6 +196,19 @@ pub struct NominateCall<T: Staking> {
pub targets: Vec<T::Address>,
}
/// Take the origin account as a stash and lock up `value` of its balance.
/// `controller` will be the account that controls it.
#[derive(Call, Encode, Debug)]
pub struct BondCall<T: Staking> {
/// Tٗhe controller account
pub contrller: T::AccountId,
/// Lock up `value` of its balance.
#[codec(compact)]
pub value: T::Balance,
/// Destination of Staking reward.
pub payee: RewardDestination<T::AccountId>,
}
#[cfg(test)]
#[cfg(feature = "integration-tests")]
mod tests {
@@ -324,6 +337,43 @@ mod tests {
Ok(())
}
#[async_std::test]
async fn test_bond() -> Result<(), Error> {
env_logger::try_init().ok();
let alice = PairSigner::<RT, _>::new(AccountKeyring::Alice.pair());
let client = ClientBuilder::<RT>::new().build().await.unwrap();
let bond = client
.bond_and_watch(
&alice,
AccountKeyring::Bob.to_account_id(),
100_000_000_000,
RewardDestination::Stash,
)
.await;
assert_matches!(bond, Ok(ExtrinsicSuccess {block: _, extrinsic: _, events}) => {
// TOOD: this is unsatisfying can we do better?
assert_eq!(events.len(), 3);
});
let bond_again = client
.bond_and_watch(
&alice,
AccountKeyring::Bob.to_account_id(),
100_000_000_000,
RewardDestination::Stash,
)
.await;
assert_matches!(bond_again, Err(Error::Runtime(RuntimeError::Module(module_err))) => {
assert_eq!(module_err.module, "Staking");
assert_eq!(module_err.error, "AlreadyBonded");
});
Ok(())
}
#[async_std::test]
async fn test_total_issuance_is_okay() -> Result<(), Error> {
env_logger::try_init().ok();