-
Notifications
You must be signed in to change notification settings - Fork 78
-
Notifications
You must be signed in to change notification settings - Fork 78
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
[EPIC] Exchange amounts between UI and backend using strings with base units instead of js numbers #11376
Comments
The alternative approach is to perform conversion to base units already on UI side (that approach is already used in wallet, but it is also vulnerable to the distortions described in the task). To do it correctly, decimal arithmetic library could be used, e.g. https://mikemcl.github.io/decimal.js/
Update: |
Description
Currently in various places we pass amounts from UI to the backend as a JS number.
In most cases it works seamlessly but there are corner cases we should take into account to provide reliable solution.
The problem is that not all decimal fractions have a finite decimal representation (simply explained e.g. here: https://indepth.dev/posts/1019/the-simple-math-behind-decimal-binary-conversion-algorithms#why-not-all-fractions-can-be-finitely-represented-in-binary)
E.g. decimal number
0.1
in binary system is0.0(00110011)
, there is no finite representation.In practice the problem can be observed on that example: https://www.mycompiler.io/view/DLtkitu1nHN
Even simpler example is just:
It means that when user provides
0.07
as an input, the value passed to the backend as float is in fact a different number. If backend multiples that value by10**18
to operate on basicwei
units, it leads to distortion, the amount used is bigger then expected.As a consequence, when the remaining supply is
0.07
(70000000000000000
), the UI will ofc present it as0.07
, but making airdrop via passing0.07
float to the backend will fail because of insufficient funds.There is also an opposite problem. When the remaining supply is is
999999999999999999
(for eth-like amounts with dp 10^18), when sent from backend to the UI as a float it would be just1
. So defining airdrop for1
(effectively1000000000000000000 wei
) will fail as well.The proposed solution is to exchange amounts (UI <-> backend, both directions) in textual form representing amount in the basic units (like
wei
s, no floating points) instead of converting it to numbers.As a consequence:
0.07
or0,07
depending on locale to70000000000000000
when the multiplier is10^18
). UI needs to properly perform decimal arithmetic here (not direct multiplication on binary floats). For that, the proper multiplier obtained from the backend needs to be used, 0 (10^0) for collectibles and 18 (10^18) for assets in most cases, but it may be different for some currencies like USDC.69999999999999999
can be presented as<0.07
or0.07 - ε
to inform user that the presented amount is not strictly the value kept under the hood. It may be useful when user wants to transfer all funds - there should be possibility to specify directly "all funds" instead of taking numeric value. It also gives possibility to reliably compare amounts on UI side.To handle properly user input and values provided by the backend as strings, UI needs to perform decimal arithmetic properly.
Library https://github.com/MikeMcl/big.js/ seems to be a good choice:
An alternative is https://github.com/MikeMcl/decimal.js but it's bigger, containing a lot of functionality that is not needed for our purpose.
The UI uses localized representation of numbers. Because of that, user-provided input cannot be directly used for constructing
Big
object. First it needs to be de-localized to form produced byNumber.toString()
. Raw JSNumber
can be used as an intermediary form, assuming that conversion for user input to number is fully reversible (user input -> number -> string produces the same value), what is true when the input is limited to appropriate number of decimal digits.It's worth noting that
big.js
relies onNumber.toString()
conversion instead of actual binary value when constructed from raw js number:Sub-tasks
AmountsArithmetic
utility (arbitrary precision decimals based onbig.js
) #11487ChatCommandModal
#12118currency_amount.nim
to avoid loosing precision by using floats #11489The text was updated successfully, but these errors were encountered: