-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Aggregate all liquidity restrictions in a single place #1921
Conversation
It will be good if this logic can be abstracted into its own module so I can reuse it in my multi asset currency module. But otherwise I can just copy and adapt the logic in my module. |
I'll leave it like this for now - feel free to make a PR which pulls it out into its own module. |
srml/balances/src/tests.rs
Outdated
use srml_support::{assert_noop, assert_ok, assert_err}; | ||
use srml_support::{ | ||
assert_noop, assert_ok, assert_err, | ||
traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons, TransferAsset} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WithdrawReason
unused.
srml/balances/src/tests.rs
Outdated
|| { | ||
Balances::set_lock(ID_1, &1, 1280, u64::max_value(), WithdrawReasons::all()); | ||
Balances::set_lock(ID_2, &1, 1280, u64::max_value(), WithdrawReasons::all()); | ||
assert_ok!(<Balances as TransferAsset<_>>::transfer(&1, &2, 100)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I think I am missing something about the nature of the lock here that this strikes me as counter-intuitive. To me, this account should now have all of its 2*1280 units of fund locked and the transfer should fail. The implementation of ensure_account_can_withdraw
seems also close to what I assumed but something is missing in my mind here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
following up on the above: shouldn't a lock affect free-vesting balance?
In other words what would have made this test (of course for me) more understandable was to see the operation and then a query afterwards on how it affected the different balance storage items corresponding to 1
:
Balances::set_lock(ID_1, &1, 1280, u64::max_value(), WithdrawReasons::all());
Balances::set_lock(ID_2, &1, 1280, u64::max_value(), WithdrawReasons::all());
// knowing that initially free = 2580 and vesting = 0
assert_eq!(Balances::free_balance(&1), XXX);
assert_eq!(Balances::vesting_balance(&1), YYY);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joepetrowski since I see that you are assigned to the documentation of this module this might also be related to you.
Given that we decided to put as much doc as possible into the code files, documenting these might also be helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a few questions (more might be added) but I don't see any of them as things to hold back the PR and they are mostly to put my own mind at peace.
Edit: except for this if we want to be concise and strict on code merged to master.
@kianenigma could do with a final review as quite a bit changed. |
/// for the given reason. | ||
/// | ||
/// `Err(...)` with the reason why not otherwise. | ||
pub fn ensure_account_can_withdraw( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be moved to Currency
trait or LockableCurrency
trait?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, because it is used within the module (pub fn make_transfer
) to ensure that there is enough unvested balance. All chains using the balances
module allow vesting, so this check makes sense.
Regarding the use of locks, Locks
is a storage item of the entire module. If the logic makes it past the vesting check and the chain does not implement Currency
or LockableCurrency
then the locks
getter will return empty and the function will return Ok(())
.
let locks = Self::locks(who);
if locks.is_empty() {
return Ok(()) // <--- Will return here
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean it is better to also expose ensure_account_can_withdraw
in the Currency
or LockableCurrency
trait so other modules are able to check this liquidity before calling mutate method offered by Currency
trait.
/// `Err(...)` with the reason why not otherwise. | ||
pub fn ensure_account_can_withdraw( | ||
who: &T::AccountId, | ||
_amount: T::Balance, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the reason to include this unused amount
as well as new_balance
?
it should be able to calculate one from other so why passing it twice?
also it doesn't query user free balance and check amount is greater than that.
I found it is awkward to use this call. I need to check free balance is more than amount, and calculate the new amount, and call this method check to check if a transfer is allowed.
Shouldn't this does all the check and query for me?
Some(b) => b, | ||
}; | ||
if would_create && value < Self::existential_deposit() { | ||
return Err("value too low to create account"); | ||
} | ||
T::EnsureAccountLiquid::ensure_account_can_withdraw(transactor, value, WithdrawReason::Transfer)?; | ||
Self::ensure_account_can_withdraw(transactor, value, WithdrawReason::Transfer, new_from_balance)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing check?
Self::ensure_account_can_withdraw(transactor, fee, WithdrawReason::TransactionPayment, new_from_balance)?
fee
is used for transaction payment and value
is used for transfer.
This means ensure_account_can_withdraw
needs to be able to designed to be able to query multiple reasons at once?
Something like
Self::ensure_account_can_withdraw(transactor, vec![(fee, WithdrawReason::TransactionPayment), (value, WithdrawReason::Transfer)])?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or Self::ensure_account_can_withdraw(transactor, WithdrawReason::TransactionPayment | WithdrawReason::Transfer, new_from_balance)?
* Clean up session key rotation * Fix build * Bump version * Introduce feature to balances. * Move staking locking logic over to central point * ^^^ rest * First part of assimilation * More assimilation * More assimilation * Fix most tests * Fix build * Move Balances to new locking system * :q! * Bump runtime version * Build runtime * Convenience function * Test fix. * Whitespace * Improve type legibility. * Fix comment. * More tests. * More tests. * Bump version * Caps * Whitespace * Whitespace * Remove unneeded function.
Fixes #1896
Removes a bunch of serde dependencies, too.
Breaking changes
EnsureAccountLiquid
has been removed frombalances::Trait
andDemocracy
no longer implementsOnFreeBalanceZero
. Simply removeDemocracy
fromimpl balances::Trait for Runtime
'sOnFreeBalanceZero
type entry and removeEnsureAccountLiquid
altogether.If you use
EnsureAccountLiquid
in an SRML module, you'll need to refactor it to useLockableCurrency
instead. See the documentation for more details.