Skip to content

Commit 5a0201c

Browse files
authored
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
1 parent 754b184 commit 5a0201c

File tree

2 files changed

+136
-1
lines changed

2 files changed

+136
-1
lines changed

src/frame/balances.rs

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ use codec::{
2525
Encode,
2626
};
2727
use core::marker::PhantomData;
28-
use frame_support::Parameter;
28+
use frame_support::{
29+
traits::LockIdentifier,
30+
Parameter,
31+
};
2932
use sp_runtime::traits::{
3033
AtLeast32Bit,
3134
MaybeSerialize,
@@ -80,6 +83,47 @@ pub struct TotalIssuanceStore<T: Balances> {
8083
pub _runtime: PhantomData<T>,
8184
}
8285

86+
/// The locks of the balances module.
87+
#[derive(Clone, Debug, Eq, PartialEq, Store, Encode, Decode)]
88+
pub struct LocksStore<'a, T: Balances> {
89+
#[store(returns = Vec<BalanceLock<T::Balance>>)]
90+
/// Account to retrieve the balance locks for.
91+
pub account_id: &'a T::AccountId,
92+
}
93+
94+
/// A single lock on a balance. There can be many of these on an account and they "overlap", so the
95+
/// same balance is frozen by multiple locks.
96+
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
97+
pub struct BalanceLock<Balance> {
98+
/// An identifier for this lock. Only one lock may be in existence for each identifier.
99+
pub id: LockIdentifier,
100+
/// The amount which the free balance may not drop below when this lock is in effect.
101+
pub amount: Balance,
102+
/// If true, then the lock remains in effect even for payment of transaction fees.
103+
pub reasons: Reasons,
104+
}
105+
106+
impl<Balance: Debug> Debug for BalanceLock<Balance> {
107+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
108+
f.debug_struct("BalanceLock")
109+
.field("id", &String::from_utf8_lossy(&self.id))
110+
.field("amount", &self.amount)
111+
.field("reasons", &self.reasons)
112+
.finish()
113+
}
114+
}
115+
116+
/// Simplified reasons for withdrawing balance.
117+
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug)]
118+
pub enum Reasons {
119+
/// Paying system transaction fees.
120+
Fee,
121+
/// Any reason other than paying system transaction fees.
122+
Misc,
123+
/// Any reason at all.
124+
All,
125+
}
126+
83127
/// Transfer some liquid free balance to another account.
84128
///
85129
/// `transfer` will set the `FreeBalance` of the sender and receiver.
@@ -181,6 +225,47 @@ mod tests {
181225
assert_ne!(info.data.free, 0);
182226
}
183227

228+
#[async_std::test]
229+
#[cfg(feature = "integration-tests")]
230+
async fn test_state_balance_lock() -> Result<(), crate::Error> {
231+
use crate::{
232+
frame::staking::{
233+
BondCallExt,
234+
RewardDestination,
235+
},
236+
runtimes::KusamaRuntime as RT,
237+
ClientBuilder,
238+
};
239+
240+
env_logger::try_init().ok();
241+
let bob = PairSigner::<RT, _>::new(AccountKeyring::Bob.pair());
242+
let client = ClientBuilder::<RT>::new().build().await?;
243+
244+
client
245+
.bond_and_watch(
246+
&bob,
247+
AccountKeyring::Charlie.to_account_id(),
248+
100_000_000_000,
249+
RewardDestination::Stash,
250+
)
251+
.await?;
252+
253+
let locks = client
254+
.locks(&AccountKeyring::Bob.to_account_id(), None)
255+
.await?;
256+
257+
assert_eq!(
258+
locks,
259+
vec![BalanceLock {
260+
id: *b"staking ",
261+
amount: 100_000_000_000,
262+
reasons: Reasons::All,
263+
}]
264+
);
265+
266+
Ok(())
267+
}
268+
184269
#[async_std::test]
185270
async fn test_transfer_error() {
186271
env_logger::try_init().ok();

src/frame/staking.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,19 @@ pub struct NominateCall<T: Staking> {
196196
pub targets: Vec<T::Address>,
197197
}
198198

199+
/// Take the origin account as a stash and lock up `value` of its balance.
200+
/// `controller` will be the account that controls it.
201+
#[derive(Call, Encode, Debug)]
202+
pub struct BondCall<T: Staking> {
203+
/// Tٗhe controller account
204+
pub contrller: T::AccountId,
205+
/// Lock up `value` of its balance.
206+
#[codec(compact)]
207+
pub value: T::Balance,
208+
/// Destination of Staking reward.
209+
pub payee: RewardDestination<T::AccountId>,
210+
}
211+
199212
#[cfg(test)]
200213
#[cfg(feature = "integration-tests")]
201214
mod tests {
@@ -324,6 +337,43 @@ mod tests {
324337
Ok(())
325338
}
326339

340+
#[async_std::test]
341+
async fn test_bond() -> Result<(), Error> {
342+
env_logger::try_init().ok();
343+
let alice = PairSigner::<RT, _>::new(AccountKeyring::Alice.pair());
344+
let client = ClientBuilder::<RT>::new().build().await.unwrap();
345+
346+
let bond = client
347+
.bond_and_watch(
348+
&alice,
349+
AccountKeyring::Bob.to_account_id(),
350+
100_000_000_000,
351+
RewardDestination::Stash,
352+
)
353+
.await;
354+
355+
assert_matches!(bond, Ok(ExtrinsicSuccess {block: _, extrinsic: _, events}) => {
356+
// TOOD: this is unsatisfying – can we do better?
357+
assert_eq!(events.len(), 3);
358+
});
359+
360+
let bond_again = client
361+
.bond_and_watch(
362+
&alice,
363+
AccountKeyring::Bob.to_account_id(),
364+
100_000_000_000,
365+
RewardDestination::Stash,
366+
)
367+
.await;
368+
369+
assert_matches!(bond_again, Err(Error::Runtime(RuntimeError::Module(module_err))) => {
370+
assert_eq!(module_err.module, "Staking");
371+
assert_eq!(module_err.error, "AlreadyBonded");
372+
});
373+
374+
Ok(())
375+
}
376+
327377
#[async_std::test]
328378
async fn test_total_issuance_is_okay() -> Result<(), Error> {
329379
env_logger::try_init().ok();

0 commit comments

Comments
 (0)