Skip to content

Integer math and rounding

Treat units, precision, and rounding as part of the protocol contract.

Amounts are integer token base units. Prices use fixed-point representation. Ticks and liquidity changes may be negative; balances and liquidity stocks may not. Never pass financial amounts through JavaScript Number.

Units are explicit

ValueRepresentation
Token quantityUnsigned integer in mint base units
Human inputDecimal string, validated before conversion
TypeScript financial amountbigint
TickSigned integer in the supported domain
Liquidity changeSigned integer; positive adds and negative removes
Liquidity stockUnsigned integer with checked updates
PriceFixed-point square-root price in ordered-token units

Changing mint decimals changes the human interpretation of a raw price. Reversing A/B inverts the price. Neither transformation belongs in a floating-point settlement path.

Round for the obligation

Required deposit amounts must not be understated. Paid output must not exceed the amount owed. Liquidity calculated from a budget must not require a later unauthorized debit. Fee splits must conserve the collected fee and account for dust.

OperationRequired property
Multiply then divideUse sufficient intermediate width; reject overflow
DenominatorReject zero explicitly
Signed-to-unsigned conversionValidate the sign before casting
Narrowing conversionReject values beyond the target type
Fee accrualPreserve liabilities through truncation and collection
QuoteUse exactly the execution domain and rounding

Shared math is necessary, not sufficient

The core and an adapter should share deterministic primitives. That prevents divergent formulas but can share the same bug. Independent rational or high-precision fixtures are needed to check the implementation.

Do not transplant Uniswap Q64.96 constants into a Q64.64 implementation. The format, domain, tick factors, intermediate width, and rounding must be chosen together. Current definitions belong in math reference.