-
Notifications
You must be signed in to change notification settings - Fork 49
/
lib.rs
1335 lines (1208 loc) · 45.3 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//! # Balances Module
//!
//! The Balances module provides functionality for handling accounts and balances.
//!
//! - [`balances::Trait`](./trait.Trait.html)
//! - [`Call`](./enum.Call.html)
//! - [`Module`](./struct.Module.html)
//!
//! ## Overview
//!
//! The Balances module provides functions for:
//!
//! - Getting and setting free balances.
//! - Retrieving total, reserved and unreserved balances.
//! - Repatriating a reserved balance to a beneficiary account that exists.
//! - Transferring a balance between accounts (when not reserved).
//! - Slashing an account balance.
//! - Account creation and removal.
//! - Managing total issuance.
//! - Setting and managing locks.
//!
//! ### Terminology
//!
//! - **Existential Deposit:** The minimum balance required to create or keep an account open. This prevents
//! "dust accounts" from filling storage. When the free plus the reserved balance (i.e. the total balance)
//! fall below this, then the account is said to be dead; and it loses its functionality as well as any
//! prior history and all information on it is removed from the chain's state.
//! No account should ever have a total balance that is strictly between 0 and the existential
//! deposit (exclusive). If this ever happens, it indicates either a bug in this module or an
//! erroneous raw mutation of storage.
//!
//! - **Total Issuance:** The total number of units in existence in a system.
//!
//! - **Reaping an account:** The act of removing an account by resetting its nonce. Happens after its
//! total balance has become zero (or, strictly speaking, less than the Existential Deposit).
//!
//! - **Free Balance:** The portion of a balance that is not reserved. The free balance is the only
//! balance that matters for most operations.
//!
//! - **Reserved Balance:** Reserved balance still belongs to the account holder, but is suspended.
//! Reserved balance can still be slashed, but only after all the free balance has been slashed.
//!
//! - **Imbalance:** A condition when some funds were credited or debited without equal and opposite accounting
//! (i.e. a difference between total issuance and account balances). Functions that result in an imbalance will
//! return an object of the `Imbalance` trait that can be managed within your runtime logic. (If an imbalance is
//! simply dropped, it should automatically maintain any book-keeping such as total issuance.)
//!
//! - **Lock:** A freeze on a specified amount of an account's free balance until a specified block number. Multiple
//! locks always operate over the same funds, so they "overlay" rather than "stack".
//!
//! ### Implementations
//!
//! The Balances module provides implementations for the following traits. If these traits provide the functionality
//! that you need, then you can avoid coupling with the Balances module.
//!
//! - [`Currency`](../frame_support/traits/trait.Currency.html): Functions for dealing with a
//! fungible assets system.
//! - [`ReservableCurrency`](../frame_support/traits/trait.ReservableCurrency.html):
//! Functions for dealing with assets that can be reserved from an account.
//! - [`LockableCurrency`](../frame_support/traits/trait.LockableCurrency.html): Functions for
//! dealing with accounts that allow liquidity restrictions.
//! - [`Imbalance`](../frame_support/traits/trait.Imbalance.html): Functions for handling
//! imbalances between total issuance in the system and account balances. Must be used when a function
//! creates new funds (e.g. a reward) or destroys some funds (e.g. a system fee).
//! - [`IsDeadAccount`](../frame_system/trait.IsDeadAccount.html): Determiner to say whether a
//! given account is unused.
//!
//! ## Interface
//!
//! ### Dispatchable Functions
//!
//! - `transfer` - Transfer some liquid free balance to another account.
//! - `set_balance` - Set the balances of a given account. The origin of this call must be root.
//!
//! ## Usage
//!
//! The following examples show how to use the Balances module in your custom module.
//!
//! ### Examples from the FRAME
//!
//! The Contract module uses the `Currency` trait to handle gas payment, and its types inherit from `Currency`:
//!
//! ```
//! use frame_support::traits::Currency;
//! # pub trait Trait: frame_system::Trait {
//! # type Currency: Currency<Self::AccountId>;
//! # }
//!
//! pub type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
//! pub type NegativeImbalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::NegativeImbalance;
//!
//! # fn main() {}
//! ```
//!
//! The Staking module uses the `LockableCurrency` trait to lock a stash account's funds:
//!
//! ```
//! use frame_support::traits::{WithdrawReasons, LockableCurrency};
//! use sp_runtime::traits::Bounded;
//! pub trait Trait: frame_system::Trait {
//! type Currency: LockableCurrency<Self::AccountId, Moment=Self::BlockNumber>;
//! }
//! # struct StakingLedger<T: Trait> {
//! # stash: <T as frame_system::Trait>::AccountId,
//! # total: <<T as Trait>::Currency as frame_support::traits::Currency<<T as frame_system::Trait>::AccountId>>::Balance,
//! # phantom: std::marker::PhantomData<T>,
//! # }
//! # const STAKING_ID: [u8; 8] = *b"staking ";
//!
//! fn update_ledger<T: Trait>(
//! controller: &T::AccountId,
//! ledger: &StakingLedger<T>
//! ) {
//! T::Currency::set_lock(
//! STAKING_ID,
//! &ledger.stash,
//! ledger.total,
//! WithdrawReasons::all()
//! );
//! // <Ledger<T>>::insert(controller, ledger); // Commented out as we don't have access to Staking's storage here.
//! }
//! # fn main() {}
//! ```
//!
//! ## Genesis config
//!
//! The Balances module depends on the [`GenesisConfig`](./struct.GenesisConfig.html).
//!
//! ## Assumptions
//!
//! * Total issued balanced of all accounts should be less than `Trait::Balance::max_value()`.
#![cfg_attr(not(feature = "std"), no_std)]
// #[cfg(test)]
// mod tests_composite;
// #[cfg(test)]
// mod tests_local;
// #[cfg(test)]
// #[macro_use]
// mod tests;
// mod benchmarking;
use codec::{Codec, Decode, Encode};
use frame_support::{
decl_error, decl_event, decl_module, decl_storage, ensure,
traits::{
BalanceStatus as Status, Currency, ExistenceRequirement, ExistenceRequirement::AllowDeath,
ExistenceRequirement::KeepAlive, Get, Imbalance, IsDeadAccount, OnKilledAccount, OnUnbalanced,
ReservableCurrency, SignedImbalance, StoredMap, TryDrop,
},
weights::SimpleDispatchInfo,
Parameter, StorageValue,
};
use frame_system::{self as system, ensure_root, ensure_signed};
use sp_runtime::{
traits::{
AtLeast32Bit, Bounded, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Member, Saturating, StaticLookup,
Zero,
},
DispatchError, DispatchResult, RuntimeDebug,
};
use sp_std::{borrow::Borrow, cmp, convert::Infallible, fmt::Debug, mem, prelude::*};
use darwinia_support::balance::{lock::*, ExistentialCheck};
use imbalances::{NegativeImbalance, PositiveImbalance};
pub trait Subtrait<I: Instance = DefaultInstance>: system::Trait {
/// The balance of an account.
type Balance: Parameter + Member + AtLeast32Bit + Codec + Default + Copy + MaybeSerializeDeserialize + Debug;
/// The minimum amount required to keep an account open.
type ExistentialDeposit: Get<Self::Balance>;
/// The means of storing the balances of an account.
type AccountStore: StoredMap<Self::AccountId, AccountData<Self::Balance>>;
// TODO: doc
type TryDropKton: ExistentialCheck<Self::AccountId, Self::Balance>;
}
pub trait Trait<I: Instance = DefaultInstance>: system::Trait {
/// The balance of an account.
type Balance: Parameter + Member + AtLeast32Bit + Codec + Default + Copy + MaybeSerializeDeserialize + Debug;
/// Handler for the unbalanced reduction when removing a dust account.
type DustRemoval: OnUnbalanced<NegativeImbalance<Self, I>>;
/// The overarching event type.
type Event: From<Event<Self, I>> + Into<<Self as system::Trait>::Event>;
/// The minimum amount required to keep an account open.
type ExistentialDeposit: Get<Self::Balance>;
/// The means of storing the balances of an account.
type AccountStore: StoredMap<Self::AccountId, AccountData<Self::Balance>>;
// TODO: doc
type TryDropKton: ExistentialCheck<Self::AccountId, Self::Balance>;
}
impl<T: Trait<I>, I: Instance> Subtrait<I> for T {
type Balance = T::Balance;
type ExistentialDeposit = T::ExistentialDeposit;
type AccountStore = T::AccountStore;
type TryDropKton = T::TryDropKton;
}
decl_event!(
pub enum Event<T, I: Instance = DefaultInstance>
where
<T as system::Trait>::AccountId,
<T as Trait<I>>::Balance,
{
/// An account was created with some free balance.
Endowed(AccountId, Balance),
/// An account was removed whose balance was non-zero but below ExistentialDeposit,
/// resulting in an outright loss.
DustLost(AccountId, Balance, Balance),
/// Transfer succeeded (from, to, value).
Transfer(AccountId, AccountId, Balance),
/// A balance was set by root (who, free, reserved).
BalanceSet(AccountId, Balance, Balance),
/// Some amount was deposited (e.g. for transaction fees).
Deposit(AccountId, Balance),
}
);
decl_error! {
pub enum Error for Module<T: Trait<I>, I: Instance> {
/// Vesting balance too high to send value
VestingBalance,
/// Account liquidity restrictions prevent withdrawal
LiquidityRestrictions,
/// Got an overflow after adding
Overflow,
/// Balance too low to send value
InsufficientBalance,
/// Value too low to create account due to existential deposit
ExistentialDeposit,
/// Transfer/payment would kill account
KeepAlive,
/// A vesting schedule already exists for this account
ExistingVestingSchedule,
/// Beneficiary account must pre-exist
DeadAccount,
}
}
/// Active balance information for an account.
#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug)]
pub struct AccountData<Balance> {
/// Non-reserved part of the balance. There may still be restrictions on this, but it is the
/// total pool what may in principle be transferred, reserved and used for tipping.
///
/// This is the only balance that matters in terms of most operations on tokens. It
/// alone is used to determine the balance when in the contract execution environment.
pub free: Balance,
/// Balance which is reserved and may not be used at all.
///
/// This can still get slashed, but gets slashed last of all.
///
/// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens
/// that are still 'owned' by the account holder, but which are suspendable.
pub reserved: Balance,
}
impl<Balance> AccountData<Balance>
where
Balance: Copy + Ord + Saturating + Zero,
{
/// How much this account's balance can be reduced for the given `reasons`.
fn usable(&self, reasons: LockReasons, frozen_balance: FrozenBalance<Balance>) -> Balance {
self.free
.saturating_sub(FrozenBalance::frozen_for(reasons, frozen_balance))
}
/// The total balance in this account including any that is reserved and ignoring any frozen.
fn total(&self) -> Balance {
self.free.saturating_add(self.reserved)
}
}
/// Frozen balance information for an account.
pub struct FrozenBalance<Balance> {
/// The amount that `free` may not drop below when withdrawing specifically for transaction
/// fee payment.
fee: Balance,
/// The amount that `free` may not drop below when withdrawing for *anything except transaction
/// fee payment*.
misc: Balance,
}
impl<Balance> FrozenBalance<Balance>
where
Balance: Copy + Ord + Zero,
{
fn zero() -> Self {
Self {
fee: Zero::zero(),
misc: Zero::zero(),
}
}
/// The amount that this account's free balance may not be reduced beyond for the given
/// `reasons`.
fn frozen_for(reasons: LockReasons, frozen_balance: Self) -> Balance {
match reasons {
LockReasons::All => frozen_balance.misc.max(frozen_balance.fee),
LockReasons::Misc => frozen_balance.misc,
LockReasons::Fee => frozen_balance.fee,
}
}
}
decl_storage! {
trait Store for Module<T: Trait<I>, I: Instance=DefaultInstance> as Ring {
/// The total units issued in the system.
pub TotalIssuance get(fn total_issuance) build(|config: &GenesisConfig<T, I>| {
config
.balances
.iter()
.fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n)
}): T::Balance;
/// The balance of an account.
///
/// NOTE: THIS MAY NEVER BE IN EXISTENCE AND YET HAVE A `total().is_zero()`. If the total
/// is ever zero, then the entry *MUST* be removed.
///
/// NOTE: This is only used in the case that this module is used to store balances.
pub Account: map hasher(blake2_256) T::AccountId => AccountData<T::Balance>;
/// Any liquidity locks on some account balances.
/// NOTE: Should only be accessed when setting, changing and freeing a lock.
pub Locks get(fn locks): map hasher(blake2_256) T::AccountId => Vec<BalanceLock<T::Balance, T::BlockNumber>>;
}
add_extra_genesis {
config(balances): Vec<(T::AccountId, T::Balance)>;
// ^^ begin, length, amount liquid at genesis
build(|config: &GenesisConfig<T, I>| {
assert!(
<T as Trait<I>>::ExistentialDeposit::get() > Zero::zero(),
"The existential deposit should be greater than zero.",
);
for (_, balance) in &config.balances {
assert!(
*balance >= <T as Trait<I>>::ExistentialDeposit::get(),
"the balance of any account should always be more than existential deposit.",
)
}
for &(ref who, free) in config.balances.iter() {
T::AccountStore::insert(who, AccountData {
free,
..Default::default()
});
}
});
}
}
decl_module! {
pub struct Module<T: Trait<I>, I: Instance = DefaultInstance> for enum Call
where
origin: T::Origin
{
type Error = Error<T, I>;
/// The minimum amount required to keep an account open.
const ExistentialDeposit: T::Balance = T::ExistentialDeposit::get();
fn deposit_event() = default;
/// Transfer some liquid free balance to another account.
///
/// `transfer` will set the `FreeBalance` of the sender and receiver.
/// It will decrease the total issuance of the system by the `TransferFee`.
/// If the sender's account is below the existential deposit as a result
/// of the transfer, the account will be reaped.
///
/// The dispatch origin for this call must be `Signed` by the transactor.
///
/// # <weight>
/// - Dependent on arguments but not critical, given proper implementations for
/// input config types. See related functions below.
/// - It contains a limited number of reads and writes internally and no complex computation.
///
/// Related functions:
///
/// - `ensure_can_withdraw` is always called internally but has a bounded complexity.
/// - Transferring balances to accounts that did not exist before will cause
/// `T::OnNewAccount::on_new_account` to be called.
/// - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`.
/// - `transfer_keep_alive` works the same way as `transfer`, but has an additional
/// check that the transfer will not kill the origin account.
///
/// # </weight>
#[weight = SimpleDispatchInfo::FixedNormal(1_000_000)]
pub fn transfer(
origin,
dest: <T::Lookup as StaticLookup>::Source,
#[compact] value: T::Balance
) {
let transactor = ensure_signed(origin)?;
let dest = T::Lookup::lookup(dest)?;
<Self as Currency<_>>::transfer(&transactor, &dest, value, ExistenceRequirement::AllowDeath)?;
}
/// Set the balances of a given account.
///
/// This will alter `FreeBalance` and `ReservedBalance` in storage. it will
/// also decrease the total issuance of the system (`TotalIssuance`).
/// If the new free or reserved balance is below the existential deposit,
/// it will reset the account nonce (`system::AccountNonce`).
///
/// The dispatch origin for this call is `root`.
///
/// # <weight>
/// - Independent of the arguments.
/// - Contains a limited number of reads and writes.
/// # </weight>
#[weight = SimpleDispatchInfo::FixedOperational(50_000)]
fn set_balance(
origin,
who: <T::Lookup as StaticLookup>::Source,
#[compact] new_free: T::Balance,
#[compact] new_reserved: T::Balance
) {
ensure_root(origin)?;
let who = T::Lookup::lookup(who)?;
let existential_deposit = T::ExistentialDeposit::get();
let wipeout = new_free + new_reserved < existential_deposit;
let new_free = if wipeout { Zero::zero() } else { new_free };
let new_reserved = if wipeout { Zero::zero() } else { new_reserved };
let (free, reserved) = Self::mutate_account(&who, |account| {
if new_free > account.free {
mem::drop(PositiveImbalance::<T, I>::new(new_free - account.free));
} else if new_free < account.free {
mem::drop(NegativeImbalance::<T, I>::new(account.free - new_free));
}
if new_reserved > account.reserved {
mem::drop(PositiveImbalance::<T, I>::new(new_reserved - account.reserved));
} else if new_reserved < account.reserved {
mem::drop(NegativeImbalance::<T, I>::new(account.reserved - new_reserved));
}
account.free = new_free;
account.reserved = new_reserved;
(account.free, account.reserved)
});
Self::deposit_event(RawEvent::BalanceSet(who, free, reserved));
}
/// Exactly as `transfer`, except the origin must be root and the source account may be
/// specified.
#[weight = SimpleDispatchInfo::FixedNormal(1_000_000)]
pub fn force_transfer(
origin,
source: <T::Lookup as StaticLookup>::Source,
dest: <T::Lookup as StaticLookup>::Source,
#[compact] value: T::Balance
) {
ensure_root(origin)?;
let source = T::Lookup::lookup(source)?;
let dest = T::Lookup::lookup(dest)?;
<Self as Currency<_>>::transfer(&source, &dest, value, ExistenceRequirement::AllowDeath)?;
}
/// Same as the [`transfer`] call, but with a check that the transfer will not kill the
/// origin account.
///
/// 99% of the time you want [`transfer`] instead.
///
/// [`transfer`]: struct.Module.html#method.transfer
#[weight = SimpleDispatchInfo::FixedNormal(1_000_000)]
pub fn transfer_keep_alive(
origin,
dest: <T::Lookup as StaticLookup>::Source,
#[compact] value: T::Balance
) {
let transactor = ensure_signed(origin)?;
let dest = T::Lookup::lookup(dest)?;
<Self as Currency<_>>::transfer(&transactor, &dest, value, KeepAlive)?;
}
}
}
impl<T: Trait<I>, I: Instance> Module<T, I> {
// PRIVATE MUTABLES
/// Get the free balance of an account.
pub fn free_balance(who: impl Borrow<T::AccountId>) -> T::Balance {
Self::account(who.borrow()).free
}
/// Get the frozen balance of an account.
fn frozen_balance(who: impl Borrow<T::AccountId>) -> FrozenBalance<T::Balance> {
let now = <system::Module<T>>::block_number();
let mut frozen_balance = <FrozenBalance<T::Balance>>::zero();
for lock in Self::locks(who.borrow()).iter() {
let locked_amount = match &lock.lock_for {
LockFor::Common { amount } => *amount,
LockFor::Staking(staking_lock) => staking_lock.locked_amount(now),
};
if lock.lock_reasons == LockReasons::All || lock.lock_reasons == LockReasons::Misc {
frozen_balance.misc = frozen_balance.misc.max(locked_amount);
}
if lock.lock_reasons == LockReasons::All || lock.lock_reasons == LockReasons::Fee {
frozen_balance.fee = frozen_balance.fee.max(locked_amount);
}
}
frozen_balance
}
/// Get the balance of an account that can be used for transfers, reservations, or any other
/// non-locking, non-transaction-fee activity. Will be at most `free_balance`.
pub fn usable_balance(who: impl Borrow<T::AccountId>) -> T::Balance {
Self::account(who.borrow()).usable(LockReasons::Misc, Self::frozen_balance(who.borrow()))
}
/// Get the balance of an account that can be used for paying transaction fees (not tipping,
/// or any other kind of fees, though). Will be at most `free_balance`.
pub fn usable_balance_for_fees(who: impl Borrow<T::AccountId>) -> T::Balance {
Self::account(who.borrow()).usable(LockReasons::Fee, Self::frozen_balance(who.borrow()))
}
/// Get the reserved balance of an account.
pub fn reserved_balance(who: impl Borrow<T::AccountId>) -> T::Balance {
Self::account(who.borrow()).reserved
}
/// Get both the free and reserved balances of an account.
fn account(who: &T::AccountId) -> AccountData<T::Balance> {
T::AccountStore::get(&who)
}
/// Places the `free` and `reserved` parts of `new` into `account`. Also does any steps needed
/// after mutating an account. This includes DustRemoval unbalancing, in the case than the `new`
/// account's total balance is non-zero but below ED.
///
/// Returns the final free balance, iff the account was previously of total balance zero, known
/// as its "endowment".
fn post_mutation(who: &T::AccountId, new: AccountData<T::Balance>) -> Option<AccountData<T::Balance>> {
let total = new.total();
if total < T::ExistentialDeposit::get() {
let (dropped, dropped_kton) = T::TryDropKton::try_drop(who);
if dropped {
if !total.is_zero() {
T::DustRemoval::on_unbalanced(NegativeImbalance::new(total));
}
Self::deposit_event(RawEvent::DustLost(who.clone(), total, dropped_kton));
None
} else {
Some(new)
}
} else {
Some(new)
}
}
/// Mutate an account to some new value, or delete it entirely with `None`. Will enforce
/// `ExistentialDeposit` law, annulling the account as needed.
///
/// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used
/// when it is known that the account already exists.
///
/// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that
/// the caller will do this.
fn mutate_account<R>(who: &T::AccountId, f: impl FnOnce(&mut AccountData<T::Balance>) -> R) -> R {
Self::try_mutate_account(who, |a| -> Result<R, Infallible> { Ok(f(a)) }).expect("Error is infallible; qed")
}
/// Mutate an account to some new value, or delete it entirely with `None`. Will enforce
/// `ExistentialDeposit` law, annulling the account as needed. This will do nothing if the
/// result of `f` is an `Err`.
///
/// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used
/// when it is known that the account already exists.
///
/// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that
/// the caller will do this.
fn try_mutate_account<R, E>(
who: &T::AccountId,
f: impl FnOnce(&mut AccountData<T::Balance>) -> Result<R, E>,
) -> Result<R, E> {
T::AccountStore::try_mutate_exists(who, |maybe_account| {
let mut account = maybe_account.take().unwrap_or_default();
let was_zero = account.total().is_zero();
f(&mut account).map(move |result| {
let maybe_endowed = if was_zero { Some(account.free) } else { None };
*maybe_account = Self::post_mutation(who, account);
(maybe_endowed, result)
})
})
.map(|(maybe_endowed, result)| {
if let Some(endowed) = maybe_endowed {
Self::deposit_event(RawEvent::Endowed(who.clone(), endowed));
}
result
})
}
/// Update the account entry for `who`, given the locks.
fn update_locks(who: &T::AccountId, locks: &[BalanceLock<T::Balance, T::BlockNumber>]) {
let existed = Locks::<T, I>::contains_key(who);
if locks.is_empty() {
Locks::<T, I>::remove(who);
if existed {
// TODO: use Locks::<T, I>::hashed_key
// https://github.com/paritytech/substrate/issues/4969
system::Module::<T>::dec_ref(who);
}
} else {
Locks::<T, I>::insert(who, locks);
if !existed {
system::Module::<T>::inc_ref(who);
}
}
}
}
// wrapping these imbalances in a private module is necessary to ensure absolute privacy
// of the inner member.
mod imbalances {
use sp_std::mem;
use crate::*;
/// Opaque, move-only struct with private fields that serves as a token denoting that
/// funds have been created without any equal and opposite accounting.
#[must_use]
pub struct PositiveImbalance<T: Subtrait<I>, I: Instance = DefaultInstance>(T::Balance);
impl<T: Subtrait<I>, I: Instance> PositiveImbalance<T, I> {
/// Create a new positive imbalance from a balance.
pub fn new(amount: T::Balance) -> Self {
PositiveImbalance(amount)
}
}
/// Opaque, move-only struct with private fields that serves as a token denoting that
/// funds have been destroyed without any equal and opposite accounting.
#[must_use]
pub struct NegativeImbalance<T: Subtrait<I>, I: Instance = DefaultInstance>(T::Balance);
impl<T: Subtrait<I>, I: Instance> NegativeImbalance<T, I> {
/// Create a new negative imbalance from a balance.
pub fn new(amount: T::Balance) -> Self {
NegativeImbalance(amount)
}
}
impl<T: Trait<I>, I: Instance> TryDrop for PositiveImbalance<T, I> {
fn try_drop(self) -> Result<(), Self> {
self.drop_zero()
}
}
impl<T: Trait<I>, I: Instance> Imbalance<T::Balance> for PositiveImbalance<T, I> {
type Opposite = NegativeImbalance<T, I>;
fn zero() -> Self {
Self(Zero::zero())
}
fn drop_zero(self) -> 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(first), Self(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<Self, Self::Opposite> {
let (a, b) = (self.0, other.0);
mem::forget((self, other));
if a >= b {
Ok(Self(a - b))
} else {
Err(NegativeImbalance::new(b - a))
}
}
fn peek(&self) -> T::Balance {
self.0.clone()
}
}
impl<T: Trait<I>, I: Instance> TryDrop for NegativeImbalance<T, I> {
fn try_drop(self) -> Result<(), Self> {
self.drop_zero()
}
}
impl<T: Trait<I>, I: Instance> Imbalance<T::Balance> for NegativeImbalance<T, I> {
type Opposite = PositiveImbalance<T, I>;
fn zero() -> Self {
Self(Zero::zero())
}
fn drop_zero(self) -> 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(first), Self(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<Self, Self::Opposite> {
let (a, b) = (self.0, other.0);
mem::forget((self, other));
if a >= b {
Ok(Self(a - b))
} else {
Err(PositiveImbalance::new(b - a))
}
}
fn peek(&self) -> T::Balance {
self.0.clone()
}
}
impl<T: Subtrait<I>, I: Instance> Drop for PositiveImbalance<T, I> {
/// Basic drop handler will just square up the total issuance.
fn drop(&mut self) {
<super::TotalIssuance<super::ElevatedTrait<T, I>, I>>::mutate(|v| *v = v.saturating_add(self.0));
}
}
impl<T: Subtrait<I>, I: Instance> Drop for NegativeImbalance<T, I> {
/// Basic drop handler will just square up the total issuance.
fn drop(&mut self) {
<super::TotalIssuance<super::ElevatedTrait<T, I>, I>>::mutate(|v| *v = v.saturating_sub(self.0));
}
}
}
// TODO: #2052
// Somewhat ugly hack in order to gain access to module's `increase_total_issuance_by`
// using only the Subtrait (which defines only the types that are not dependent
// on Positive/NegativeImbalance). Subtrait must be used otherwise we end up with a
// circular dependency with Trait having some types be dependent on PositiveImbalance<Trait>
// and PositiveImbalance itself depending back on Trait for its Drop impl (and thus
// its type declaration).
// This works as long as `increase_total_issuance_by` doesn't use the Imbalance
// types (basically for charging fees).
// This should eventually be refactored so that the type item that
// depends on the Imbalance type (DustRemoval) is placed in its own pallet.
struct ElevatedTrait<T: Subtrait<I>, I: Instance>(T, I);
impl<T: Subtrait<I>, I: Instance> Clone for ElevatedTrait<T, I> {
fn clone(&self) -> Self {
unimplemented!()
}
}
impl<T: Subtrait<I>, I: Instance> PartialEq for ElevatedTrait<T, I> {
fn eq(&self, _: &Self) -> bool {
unimplemented!()
}
}
impl<T: Subtrait<I>, I: Instance> Eq for ElevatedTrait<T, I> {}
impl<T: Subtrait<I>, I: Instance> system::Trait for ElevatedTrait<T, I> {
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 = T::ModuleToIndex;
type AccountData = T::AccountData;
type OnNewAccount = T::OnNewAccount;
type OnKilledAccount = T::OnKilledAccount;
}
impl<T: Subtrait<I>, I: Instance> Trait<I> for ElevatedTrait<T, I> {
type Balance = T::Balance;
type DustRemoval = ();
type Event = ();
type ExistentialDeposit = T::ExistentialDeposit;
type AccountStore = T::AccountStore;
type TryDropKton = T::TryDropKton;
}
impl<T: Trait<I>, I: Instance> Currency<T::AccountId> for Module<T, I>
where
T::Balance: MaybeSerializeDeserialize + Debug,
{
type Balance = T::Balance;
type PositiveImbalance = PositiveImbalance<T, I>;
type NegativeImbalance = NegativeImbalance<T, I>;
fn total_balance(who: &T::AccountId) -> Self::Balance {
Self::account(who).total()
}
// Check if `value` amount of free balance can be slashed from `who`.
fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool {
if value.is_zero() {
return true;
}
Self::free_balance(who) >= value
}
fn total_issuance() -> Self::Balance {
<TotalIssuance<T, I>>::get()
}
fn minimum_balance() -> Self::Balance {
T::ExistentialDeposit::get()
}
// Burn funds from the total issuance, returning a positive imbalance for the amount burned.
// Is a no-op if amount to be burned is zero.
fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance {
if amount.is_zero() {
return PositiveImbalance::zero();
}
<TotalIssuance<T, I>>::mutate(|issued| {
*issued = issued.checked_sub(&amount).unwrap_or_else(|| {
amount = *issued;
Zero::zero()
});
});
PositiveImbalance::new(amount)
}
// Create new funds into the total issuance, returning a negative imbalance
// for the amount issued.
// Is a no-op if amount to be issued it zero.
fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance {
if amount.is_zero() {
return NegativeImbalance::zero();
}
<TotalIssuance<T, I>>::mutate(|issued| {
*issued = issued.checked_add(&amount).unwrap_or_else(|| {
amount = Self::Balance::max_value() - *issued;
Self::Balance::max_value()
})
});
NegativeImbalance::new(amount)
}
fn free_balance(who: &T::AccountId) -> Self::Balance {
Self::account(who).free
}
// Ensure that an account can withdraw from their free balance given any existing withdrawal
// restrictions like locks and vesting balance.
// Is a no-op if amount to be withdrawn is zero.
//
// # <weight>
// Despite iterating over a list of locks, they are limited by the number of
// lock IDs, which means the number of runtime modules that intend to use and create locks.
// # </weight>
fn ensure_can_withdraw(
who: &T::AccountId,
amount: T::Balance,
reasons: WithdrawReasons,
new_balance: T::Balance,
) -> DispatchResult {
if amount.is_zero() {
return Ok(());
}
let min_balance = FrozenBalance::frozen_for(reasons.into(), Self::frozen_balance(who.borrow()));
ensure!(new_balance >= min_balance, Error::<T, I>::LiquidityRestrictions);
Ok(())
}
// Transfer some free balance from `transactor` to `dest`, respecting existence requirements.
// Is a no-op if value to be transferred is zero or the `transactor` is the same as `dest`.
fn transfer(
transactor: &T::AccountId,
dest: &T::AccountId,
value: Self::Balance,
existence_requirement: ExistenceRequirement,
) -> DispatchResult {
if value.is_zero() || transactor == dest {
return Ok(());
}
Self::try_mutate_account(dest, |to_account| -> DispatchResult {
Self::try_mutate_account(transactor, |from_account| -> DispatchResult {
from_account.free = from_account
.free
.checked_sub(&value)
.ok_or(Error::<T, I>::InsufficientBalance)?;
// NOTE: total stake being stored in the same type means that this could never overflow
// but better to be safe than sorry.
to_account.free = to_account.free.checked_add(&value).ok_or(Error::<T, I>::Overflow)?;
let ed = T::ExistentialDeposit::get();
ensure!(to_account.total() >= ed, Error::<T, I>::ExistentialDeposit);
Self::ensure_can_withdraw(transactor, value, WithdrawReason::Transfer.into(), from_account.free)?;
let allow_death = existence_requirement == ExistenceRequirement::AllowDeath;
let allow_death = allow_death && system::Module::<T>::allow_death(transactor);
ensure!(allow_death || from_account.free >= ed, Error::<T, I>::KeepAlive);
Ok(())
})
})?;
// Emit transfer event.
Self::deposit_event(RawEvent::Transfer(transactor.clone(), dest.clone(), value));
Ok(())
}
/// Slash a target account `who`, returning the negative imbalance created and any left over
/// amount that could not be slashed.
///
/// Is a no-op if `value` to be slashed is zero.
///
/// NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn
/// from in extreme circumstances. `can_slash()` should be used prior to `slash()` to avoid having
/// to draw from reserved funds, however we err on the side of punishment if things are inconsistent
/// or `can_slash` wasn't used appropriately.
fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
if value.is_zero() {
return (NegativeImbalance::zero(), Zero::zero());
}
Self::mutate_account(who, |account| {
let free_slash = cmp::min(account.free, value);
account.free -= free_slash;
let remaining_slash = value - free_slash;
if !remaining_slash.is_zero() {
let reserved_slash = cmp::min(account.reserved, remaining_slash);
account.reserved -= reserved_slash;
(
NegativeImbalance::new(free_slash + reserved_slash),
remaining_slash - reserved_slash,
)
} else {
(NegativeImbalance::new(value), Zero::zero())
}
})
}
/// Deposit some `value` into the free balance of an existing target account `who`.
///
/// Is a no-op if the `value` to be deposited is zero.
fn deposit_into_existing(
who: &T::AccountId,
value: Self::Balance,
) -> Result<Self::PositiveImbalance, DispatchError> {
if value.is_zero() {
return Ok(PositiveImbalance::zero());
}
Self::try_mutate_account(who, |account| -> Result<Self::PositiveImbalance, DispatchError> {