blockulator

Each figure links to its scenario on the ladder: 14 calculations protocols really run, repeated up to 10,000 times and checked against 300-digit arithmetic (ladder.json).

Where the bits go in WAD

A WAD library stores a value as an integer times 1018. That is about 260, so about 60 of the 256 bits hold the fraction.

The other 196 bits are not spare precision. They let x * y fit before the divide by 1018, and you cannot store a value in them: a balance of 1040 fits, but mulWad reverts once it multiplies it by more than 11.6. ABDK's 64.64 splits a 128-bit budget into 64 bits of fraction and 64 of integer, so it is the first to revert when a balance grows (annual compounding).

Where the bits go in BL127

BL127 spends the 256 bits on 128 bits of fraction, 127 integer bits and a sign. mul forms the full 512-bit product and keeps the middle 256 bits, so nothing is held back for headroom. That gives about 38 digits of fraction against WAD's 18, with a range of ±1.7 × 1038.

Why iteration is what breaks it

One mulWad costs nothing real: its error is below a token's smallest unit. But a lending protocol accrues again and again, and after N accruals it has rounded N times.

Take $10,000 at 10% a year, compounded daily (compound). After one day the WAD libraries are right to 19.5 digits, after a year 16.9, after 10,000 days 15.5. Over the same steps BL127 goes from 38.8 to 34.8. Both lose about a digit each time the steps grow tenfold. BL127 starts 19 digits higher, and the start is what you keep.

Subtraction is worse. An amortising loan computes b = b·(1+r) − payment every day, and near payoff the balance is a small difference of two large numbers. A $300,000 loan at 5%, paid daily over 10,001 days, has $55 left after 10,000 days. There the WAD libraries keep 11.9 digits and BL127 31.2 (amortise). Twelve digits of $55 is fine. Twelve digits of a position a basis point from liquidation, across a million accounts, may not be.

What it costs

Inline library, median gas per call: random inputs that take no shortcut for exp, ln, pow, sqrt and normCdf, a fixed set across magnitudes for mul and div. A blank is an op the library does not offer; the per-op tables have every op.

opBL127BL19QuadSoladyPRBMathABDKBalancer
mul267530709126485171
div41380081893509221
exp2,8353,2785,6684302,8163,3801,040
ln1,9443,10616,3565567,0837,0801,091
sqrt4691,2057603939788282,351
pow3,3175,96423,4791,27910,0652,223
normCdf6,03712,354

BL127 costs less than PRBMath on every op both offer here but exp (2,835 gas against 2,816), and more than Solady on every op Solady offers, because it computes twice the bits. The floats, BL19 and Quad, pay for their moving point on every add and mul. BL19 costs less than Quad on every op here but sqrt.

What it does not fix

BL127's range has two ends. It tops out near 1.7 × 1038: exp(89) reverts, and Uniswap's top tick, 1.0001887272, is one bit past it. Its smallest step is 2−128, so an argument of 10−30 keeps 8 digits, and so do its root and reciprocal (edges: exp, pow, sqrt, div).

Fixed point gets the most precision inside a band; a float gets the widest band. BL19 keeps at least 53 digits on every edge call, where ABDKMathQuad keeps as few as 4. BL127 is for the maths in the middle: the price, the ratio, the growth factor 1 + r. Above 3 × 10−5 it holds more digits than binary128's 34.

It does not replace the integer arithmetic of a transfer, it does not stop an exploit of an empty market, and it is not cheap enough for a path that does not need the digits.