-
Notifications
You must be signed in to change notification settings - Fork 41
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
Support all fractional places #1049
Conversation
Codecov Report
❗ Your organization is not using the GitHub App Integration. As a result you may experience degraded service beginning May 15th. Please install the Github App Integration for your organization. Read more. @@ Coverage Diff @@
## main #1049 +/- ##
=======================================
Coverage 92.93% 92.94%
=======================================
Files 92 92
Lines 21589 21581 -8
=======================================
- Hits 20064 20058 -6
+ Misses 1525 1523 -2
Flags with carried forward coverage won't be shown. Click here to find out more.
|
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.
It is an interesting approach to handle foreign assets easier on our chain. Especially that you calculated the risk of falling precision for having more foreign decimals places than our native base of ten.
} | ||
} | ||
|
||
asset.clone() |
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 see. No modification of decimal places is also allowed, because it's put into the UnkownAssetRegistry
. Assume the following scenario: An asset arrives which we don't have registered yet. So it arrives in the UnknownAssetRegistry
. Now we know which asset it is and register it in the AssetRegistry
. Is the asset amount updated to 10 decimal places too in this case?
let power = decimals.saturating_sub(native_decimals); | ||
let adjust_factor = 10u128.saturating_pow(power); | ||
// Floors the adjusted token amount, thus no tokens are generated | ||
Fungible(amount.saturating_div(adjust_factor)) |
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.
This seems to regard the ingoing asset to Zeitgeist since the high number of decimals need to decrease (saturating_div
). So this seems totally reasonable for the deposit
to Zeitgeist case.
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.
But where is the withdraw
case? So the outgoing asset from Zeitgeist for the higher decimal case?
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.
Any XCM uses the global canonical representation, i.e. 18 fractional decimal places for ETH.
Simplified:
deposit: When a reserve asset transfer with ETH is incoming, it puts the unadjusted ETH in the holding register. Then it deposits the adjusted ETH using the adjusted deposit
function into the target pallet and account. Then it subtracts the unadjusted value from the holding register.
withdraw: When a reserve asset transfer with ETH is sent, it withdraws the adjusted ETH using the adjusted withdraw
function from the target pallet and acount and afterwards puts the unadjusted ETH in the holding register (since the XCM contains the global canonical representation, in this case 18 fractional decimal places).
deposit: XCM in (unadjusted ETH) -> holding register (unadjusted) -> tokens (deposit, adjusted) -> update holding register (unadjusted)
withdraw: XCM out (unadjusted ETH) -> tokens (withdraw, adjusted) -> holding register (unadjusted)
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 trick here is that we demand the global canonical representation for any token. Simplified, if 1 ETH enters, the XCM contains the amount 10^18
. If an XCM is created on Zeitgeist to send ETH, the XCM contains the amount 10^18
as value, although internally it is stored as 10^10
.
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 see. So the point that I missed is that any XCM message either withdraw
or deposit
is (hopefully) always constructed with the external decimal representation (not our internal ten base representation).
I also missed the point in general that your AlignedFractionalTransactAsset
is only meant for the internal representation to get data from the XCM into the tokens pallet. AlignedFractionalTransactAsset
is not meant to get the asset amount out to the external representation, since the external representation is always the value inside the XCM.
It reminds me about a one-way hash function into the internal representation only.
from: &MultiLocation, | ||
to: &MultiLocation, | ||
) -> Result<Assets, XcmError> { | ||
let asset_adjusted = Self::adjust_fractional_places(asset); |
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 thinking about what transfer_asset
actually does for the orml MultiCurrencyAdapter
. How is Zeitgeist involved here, since the from
could be Moonbeam
and to
could be Astar
? Is it outgoing or ingoing for Zeitgeist? So, did we already adjust the decimal places internally and then call transfer_asset which transfers the amount to a completely different chain? If that is the case, then we withdraw
from Zeitgeist, but need to recover the original decimal places. If it is a deposit to Zeitgeist, then we need to adjust to the new decimal places (base 10).
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.
TransferAsset
is used to transfer an asset on a remote destination from the sending account to a receiving account on the same chain, like in this example (sent from sending account into parachain account). Messages that contain a location outside of Zeitgeist will fail in execution.
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.
Ok, I think I see now. Means other parachains can remotely transfer assets between two Zeitgeist accounts.
Co-authored-by: Chralt <[email protected]>
Co-authored-by: Chralt <[email protected]>
Co-authored-by: Chralt <[email protected]>
- ⚠️ All tokens now use 10 fractional decimal places. | ||
- cross-consensus messages (XCM) assume the global canonical representation for token balances. | ||
- The token metadata in the asset registry now assumes that the existential deposit and fee factor | ||
are stored in base 10,000,000,000. |
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.
This should be reformatted using prettier.
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 alignment of line 22 is intentional, it is a sub point of the one in line 21.
What does it do?
The PR adjusts all foreign token balance representations to the base
BASE
, currently10^10
.The PR solves the problem of aligning the decimal fractional places of all foreign tokens with the native representation by modifying the
TransactAsset
implementation that is used in thexcm-executor
. Until now theMultiCurrencyAdapter
has been used to as an implementation of theTransactAsset
. This PR adds a generic wrapperAlignedFractionalTransactAsset
around aTransactAsset
implementation, that applies proper alignment of token balances before calling the wrapped implementation (in that caseMultiCurrencyAdapter
.The
TransactAsset
does determine how tokens aredeposited
to an account after withdrawing them from the holding register, how they arewithdrawn
from an account before being deposited in the holding register and how they aretransfered
. Additional functions to handle teleports for example exist, but as of now the currently usedMultiCurrencyAdapter
does not support teleports (see this) and Zeitgeist does not utilize teleports.Tests ensure proper functioning in any case (ZTG out and in, token with more than 10 fractional decimals out and in, token with less than 10 fractional decimals out and in).
What important points should reviewers know?
1_000_000_000_000_000_000
(10^18
) in the XCM message. Locally, it would be aligned toBASE
, which (assuming there are no fees) results in10_000_000_000
(10^10
). When crafting an XCM to send 1 GLMR from Zeitgeist to Moonbeam, the XCM must contain the1_000_000_000_000_000_000
(10^18
), even though locally it is represented as10_000_000_000
(10^10
). The conversion can be done by receiving thedecimals
from the asset's metadata within theAssetRegistry
. The rationale behind this is that the XCM should have the same meaning everywhere, as other projects also execute XCM on Zeitgeist (for example to transfer ZTG to their protocol). Consequently, usingBASE
representation in XCM for any token and aligning it afterwards is not a coherent solution.fee_factor
of assets metadata within theAssetRegistry
has been changed to assumefee_factor
being in the native baseBASE
. From now on, the existential deposit and fee factor are assumed to be noted inBASE
.BASE
exist on Zeitgeist mainnet as of now. On Battery Station (Rococo) testnet, Rococo is already onboarded (12 decimal places). Alignment will happen manually in that case.Is there something left for follow-up PRs?
AlignedFractionalTransactAsset
#1055What alternative implementations were considered?
xcm-executor
andxtokens
BASE
representation for token amountsAre there relevant PRs or issues?
closes #969
References