-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add Scale
to SingleAssetVault
#5652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #5652 +/- ##
=========================================
+ Coverage 78.7% 78.7% +0.1%
=========================================
Files 816 816
Lines 71748 71924 +176
Branches 8477 8466 -11
=========================================
+ Hits 56440 56627 +187
+ Misses 15308 15297 -11
🚀 New features to boost your workflow:
|
AssetScale
to SingleAssetVaultScaleFactor
to SingleAssetVault
44422a5
to
44b60fe
Compare
ecfbf80
to
5f54dae
Compare
5f54dae
to
97e44c7
Compare
50a393d
to
2047661
Compare
ScaleFactor
to SingleAssetVaultScale
to SingleAssetVault
if (auto const ter = accountSend( | ||
view(), | ||
vaultAccount, | ||
dstAcct, | ||
assetsWithdrawn, | ||
j_, | ||
WaiveTransferFee::Yes)) |
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.
some other places in this PR uses isTesSuccess to check the result. I think we'd prefer to use this for explicitness and consistency
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.
thanks, added explicit isTesSuccess(ter)
. I also prefer it, only missed in few places, thanks for spotting it !
if (auto const ter = accountSend( | ||
view(), | ||
holder, | ||
vaultAccount, | ||
sharesDestroyed, | ||
j_, | ||
WaiveTransferFee::Yes)) |
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.
same here
if (auto const ter = accountSend( | ||
view(), | ||
account_, | ||
vaultAccount, | ||
sharesRedeemed, | ||
j_, | ||
WaiveTransferFee::Yes)) |
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.
same here
7f6e13a
to
f28ce76
Compare
f28ce76
to
d24632d
Compare
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 have one suggestion about a log message, but it's so minor that I'm going to approve this whether you add it or not.
return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0); | ||
} | ||
|
||
Number |
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.
Can simplify or maybe just use combination of towards_zero
rounding and static_cast
since truncate()
usage is limited.
inline Number
Number::truncate() const noexcept
{
if (exponent_ >= 0 || mantissa_ == 0)
return *this;
NumberRoundModeGuard mg(Number::towards_zero);
return static_cast<rep>(*this);
}
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 am not sure if that would be an improvement, given the complexity of operator rep()
. On the other hand, normalize()
is also quite complex. However with the current implementation we can guarantee noexcept
and I am not sure how this would look like if we used operator rep()
I will add a comment about noexcept
and leave it like this for now.
{sfDomainID, soeOPTIONAL}, | ||
{sfWithdrawalPolicy, soeOPTIONAL}, | ||
{sfData, soeOPTIONAL}, | ||
{sfScale, soeOPTIONAL}, |
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 sfShareScale
to stress that it applies to the shares?
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.
The sfScale
is used to similar effect elsewhere already; I think it's better to stick to this name.
|
||
// Compute exchange before transferring any amounts. | ||
auto const shares = assetsToSharesDeposit(vault, sleIssuance, assets); | ||
STAmount sharesCreated = {vault->at(sfShareMPTID)}, assetsDeposited; |
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.
Consider defining assetsDeposited
on a separate line. I don't think we really do this chained definition. It's also easier to read if defined on a separate line.
auto const share = MPTIssue(mptIssuanceID); | ||
STAmount shares, assets; | ||
if (amount.asset() == asset) | ||
STAmount sharesRedeemed = {vault->at(sfShareMPTID)}, assetsWithdrawn; |
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.
Consider defining assetsWithdraw
on a separate line for a better readability and style consistency.
|
||
if (sharesRedeemed == beast::zero) | ||
return tecPRECISION_LOSS; | ||
auto const maybeAssets = |
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.
Because of the truncation/recalculation, which is done on deposit and recalculation here then is it possible that if I deposit A and get S; if I withdraw A then I'll redeem more than S and likewise, if I withdraw S then I'll get less than A? Another case would be also bad; i.e. I withdraw A and redeem less than S and I withdraw S and get more than A. The same concern applies to VaultClawback
.
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.
If we are given an A (number of assets) then we always (i.e. in VaultDeposit
VaultClawback
VaultWithdraw
) do roundtrip calculation : first from assets to shares, then from such rounded shares to assets again. This guarantees that the number of assets always corresponds with the number of shares, bar rounding of assets (for MPT or XRP) which can go either way. This is similar how DEX works when one side is XRP - which also can go either way due to rounding. If the asset is an IOU then there is no rounding of assets (other than epsilon) which is also similar to how DEX works.
assets = sharesToAssetsWithdraw(vault, sleIssuance, shares); | ||
} | ||
else | ||
STAmount sharesDestroyed = {vault->at(sfShareMPTID)}, assetsRecovered; |
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.
Consider defining assetsRecovered
on a separate line.
{ | ||
if (auto const ter = | ||
removeEmptyHolding(view(), holder, sharesDestroyed.asset(), j_); | ||
isTesSuccess(ter)) |
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.
Shouldn't error be returned on failure?
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.
Maybe. We are opportunistically trying to remove MPToken
in case if its balance is zero. This is expected to fail if the balance is not zero, and we want to ignore this error.
On the other hand, reporting other errors would be useful here. I will add it.
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.
👍 LGTM
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 have couple of stylistic-ish nits, but otherwise looks good.
auto const mptIssuanceID = (*vault)[sfShareMPTID]; | ||
auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID)); | ||
auto const sleIssuance = view().read(keylet::mptIssuance(*mptIssuanceID)); |
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.
Rather than using the *
operator every time you access mptIssuanceID
, you could do it when you do the initial lookup:
auto const mptIssuanceID = (*vault)[sfShareMPTID]; | |
auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID)); | |
auto const sleIssuance = view().read(keylet::mptIssuance(*mptIssuanceID)); | |
auto const mptIssuanceID = *(*vault)[sfShareMPTID]; | |
auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID)); | |
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.
thanks, implemented in 6893015 (with extra parents for readability)
if (auto const ter = | ||
removeEmptyHolding(view(), holder, sharesDestroyed.asset(), j_); | ||
isTesSuccess(ter)) | ||
auto const ter = | ||
removeEmptyHolding(view(), holder, sharesDestroyed.asset(), j_); | ||
if (isTesSuccess(ter)) |
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.
Even with the else if
, ter
is only used in the context of the if
, so you can put it back.
if (auto const ter = | |
removeEmptyHolding(view(), holder, sharesDestroyed.asset(), j_); | |
isTesSuccess(ter)) | |
auto const ter = | |
removeEmptyHolding(view(), holder, sharesDestroyed.asset(), j_); | |
if (isTesSuccess(ter)) | |
if (auto const ter = | |
removeEmptyHolding(view(), holder, sharesDestroyed.asset(), j_); | |
isTesSuccess(ter)) |
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.
thanks, implemented in 6893015
auto const mptIssuanceID = (*vault)[sfShareMPTID]; | ||
auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID)); | ||
auto const sleIssuance = view().read(keylet::mptIssuance(*mptIssuanceID)); |
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.
Same as in VaultClawback
, mptIssuanceID
can be declared with the *
.
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.
thanks, implemented in 6893015 (with extra parents for readability)
auto const ter = | ||
removeEmptyHolding(view(), account_, sharesRedeemed.asset(), j_); | ||
if (isTesSuccess(ter)) |
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.
Same as in VaultClawback
, ter
can be put back into the if
statement.
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.
thanks, implemented in 6893015
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'm realizing there's a lot of duplicated code between VaultClawback
and VaultWithdraw
that can be refactored into helper functions, but that can be done later.
High Level Overview of Change
This change adds
Scale
to SAV when asset is an IOU, per XRPLF/XRPL-Standards#301It also removes empty
MPToken
for vault shares whenVaultClawback
orVaultWithdraw
zeroes the holders shares balance.Context of Change
SAV shares are always expressed as an integer, since they are MPTs. We need to ensure that this loss of precision does not cause problems when asset has fractional part, like IOUs do.
Type of Change
.gitignore
, formatting, dropping support for older tooling)