Unit Scaling

Orbital AMM uses three layers of unit scaling to prevent overflow while maintaining precision. This page explains why each layer exists and provides a conversion cheat sheet.

The Three Layers

LayerUnitExample (1M USDC)
RawMicrounits (ASA)1,000,000,000,000 (10¹²)
ScaledAMOUNT_SCALE units1,000,000,000 (10⁹)
MathPRECISION units1,000,000 (10⁶)

Why Scaling Is Needed

The torus invariant requires computing squares of reserves:

sumXSq = ∑xᵢ²

For a pool with 50,000 USDC (50,000 × 10⁶ = 5 × 10¹⁰ microunits):

(5 × 10¹⁰)² = 2.5 × 10²¹

This exceeds uint64 max (~1.8 × 10¹⁹) by 100×. We need to scale down before squaring.

AMOUNT_SCALE

AMOUNT_SCALE = 1000 divides reserves before math operations:

AMOUNT_SCALE = 1000

# Convert raw to scaled
scaled = raw // AMOUNT_SCALE

# Now squaring 50M tokens:
# (50,000,000,000 / 1000)² = (50,000,000)² = 2.5 × 10¹⁵
# This fits in uint64 ✓

After scaling by 1000, the max square is 2.5 × 10¹⁵ — well within uint64 range.

PRECISION

PRECISION = 10⁹ is used for fee growth and other fixed-point calculations:

PRECISION = 1_000_000_000

# Fee growth per unit of liquidity
fee_growth += fee_amount * PRECISION // r_int

PRECISION provides 9 decimal places of precision — enough for small fee accruals without overflow.

Conversion Cheat Sheet

# Raw → Scaled
scaled = raw // AMOUNT_SCALE  # Divide by 1000

# Scaled → Raw
raw = scaled * AMOUNT_SCALE  # Multiply by 1000

# Scaled → Math (PRECISION units)
math = scaled * PRECISION // AMOUNT_SCALE

# Math → Scaled
scaled = math * AMOUNT_SCALE // PRECISION

# Raw → Display (human-readable)
display = raw / (10 ** decimals)  # e.g., / 10⁶ for USDC

# Display → Raw
raw = int(display * (10 ** decimals))

Example Conversions

For 1M USDC (6 decimals):

FromToFormulaResult
Display (1M)Raw1M × 10⁶10¹²
RawScaled10¹² / 100010⁹
ScaledMath10⁹ × 10⁹ / 100010¹⁵
MathDisplay10¹⁵ / 10⁹ / 10⁶1M

Overflow Prevention

The contract enforces scaling at every step:

# Before any squaring:
assert x < MAX_SAFE_SQUARE_INPUT
# MAX_SAFE_SQUARE_INPUT = floor(sqrt(2⁶⁴ - 1)) ≈ 4.29 × 10⁹

# After AMOUNT_SCALE, max reserve is ~5 × 10⁷
# Square is ~2.5 × 10¹⁵, well below limit ✓

Precision Loss

Integer division introduces rounding errors. The contract uses:

  • Truncationa // b always rounds down
  • Tolerance — Invariant checks allow ±1000 error

For typical pool sizes (1M-100M), precision loss is < 0.01%. For very small pools (< 10k), it can reach 1%.

SDK note: The SDK handles all conversions internally. You work with display units (1M USDC), and it converts to raw/scaled/math as needed.