You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currency.ValueAsNumber is a decimal, and decimal cannot hold what the protocol allows. Every symptom in #148 follows from that, and none of them is fixable inside decimal — the fix there only chooses how to fail, because succeeding is not available.
67 orders of magnitude unreachable at the top, 53 at the bottom. A decimal also carries 28–29 significant digits against the protocol's 16, so precision is not the problem — range is.
What this costs today
GetBalanceChanges subtracts two balances. Out of range, one side is wrong or throws, so the whole call fails on metadata the caller does not control — and an indexer re-reading that transaction fails on it forever.
Offer.AmountEach divides two amounts and can return an exchange rate wrong by 67 orders of magnitude without throwing.
Amounts below 1e-28 become zero silently.
#148 makes these predictable rather than three-different-ways-wrong. It does not make them representable.
What is proposed
An exact amount type that covers the protocol range, and arithmetic performed in it rather than in decimal.
The model to copy is the one the protocol already defines and both reference implementations use: an integer mantissa with an exponent. rippled's STAmount keeps a std::uint64_t mantissa in [1e15, 1e16) with an exponent in [-96, 80]; xrpl.js reaches the same range through BigNumber with MIN_IOU_EXPONENT = -96, MAX_IOU_EXPONENT = 80, MAX_IOU_PRECISION = 16. In .NET the natural equivalent is System.Numerics.BigInteger for the mantissa alongside an int exponent, normalised the way rippled normalises.
This has been done once already in this repository and it went well: Xrpl.Sugar.AmmMath transcribes rippled's AMM equations rather than deriving them, and agrees with a live node to 15 significant digits. The same approach applies — take the protocol's own numeric model instead of approximating it with a CLR type that nearly fits.
Scope and shape
Deliberately not decided here, because it is worth deciding in the open:
Where does the arithmetic live?GetBalanceChanges and Offer.AmountEach are the two places that compute rather than merely read, and both would move to the exact type.
Does the type surface publicly, or only internally with decimal at the boundary? Only the first actually lets a caller handle a 1e90 balance.
#148 closes two live failures and is a minor-version change. This is a public API question that deserves discussion rather than being settled in a bugfix, days after 11.0.0 shipped.
Blocked by nothing; #148 does not have to land first, but it makes the interim behaviour honest while this is decided.
Currency.ValueAsNumberis adecimal, anddecimalcannot hold what the protocol allows. Every symptom in #148 follows from that, and none of them is fixable insidedecimal— the fix there only chooses how to fail, because succeeding is not available.The gap
From
STAmount.hon rippled develop:1e-811e96System.Decimal1e-287.9e2867 orders of magnitude unreachable at the top, 53 at the bottom. A
decimalalso carries 28–29 significant digits against the protocol's 16, so precision is not the problem — range is.What this costs today
GetBalanceChangessubtracts two balances. Out of range, one side is wrong or throws, so the whole call fails on metadata the caller does not control — and an indexer re-reading that transaction fails on it forever.Offer.AmountEachdivides two amounts and can return an exchange rate wrong by 67 orders of magnitude without throwing.1e-28become zero silently.#148 makes these predictable rather than three-different-ways-wrong. It does not make them representable.
What is proposed
An exact amount type that covers the protocol range, and arithmetic performed in it rather than in
decimal.The model to copy is the one the protocol already defines and both reference implementations use: an integer mantissa with an exponent. rippled's
STAmountkeeps astd::uint64_tmantissa in[1e15, 1e16)with an exponent in[-96, 80];xrpl.jsreaches the same range throughBigNumberwithMIN_IOU_EXPONENT = -96,MAX_IOU_EXPONENT = 80,MAX_IOU_PRECISION = 16. In .NET the natural equivalent isSystem.Numerics.BigIntegerfor the mantissa alongside anintexponent, normalised the way rippled normalises.This has been done once already in this repository and it went well:
Xrpl.Sugar.AmmMathtranscribes rippled's AMM equations rather than deriving them, and agrees with a live node to 15 significant digits. The same approach applies — take the protocol's own numeric model instead of approximating it with a CLR type that nearly fits.Scope and shape
Deliberately not decided here, because it is worth deciding in the open:
ValueAsNumberstay? It is used widely and is convenient for ordinary amounts. Keeping it as a lossy convenience that throws out of range — what Currency.ValueAsNumber throws on out-of-range token amounts, taking GetBalanceChanges down with it #148 leaves it as — while adding an exact accessor beside it, is one option. Replacing it is another, and is a breaking change.GetBalanceChangesandOffer.AmountEachare the two places that compute rather than merely read, and both would move to the exact type.decimalat the boundary? Only the first actually lets a caller handle a 1e90 balance.Why separately from #148
#148 closes two live failures and is a minor-version change. This is a public API question that deserves discussion rather than being settled in a bugfix, days after 11.0.0 shipped.
Blocked by nothing; #148 does not have to land first, but it makes the interim behaviour honest while this is decided.