← Blog

engineering

Money is a bigint, and the column name says so

Every currency bug we have seen started as a reasonable-looking float.

28 July 2026 · By Ada Okafor, Engineering · 4 mins

Token pricing runs to six decimal places. A thousand calls at $0.000015 is a number a float will quietly get wrong, and it will get it wrong in a direction nobody notices until a reconciliation fails.

ts
// $3.00 per million tokens, as an integer of micro-dollars
const INPUT_PER_MTOK = 3_000_000n

// what 1,204 input tokens cost, with no floating point anywhere on the path
const costMicros = (INPUT_PER_MTOK * 1_204n) / 1_000_000n  // → 3_612n  ($0.003612)

// the float version, for comparison
const asFloat = (3.0 / 1_000_000) * 1204            // 0.0036119999999999997
// …and a thousand of those is off by enough to fail a reconciliation

We store micro-dollars as `bigint`, and every column ends `_micros` so a reader cannot mistake the unit. It is not clever. It is the kind of decision that is free on day one and touches every table on day four hundred.

  wallet
  ┌──────────────────────────────────────────────┐
  │  balance_micros        34_210_000   $34.21   │  what they put in
  │  hold_micros            6_000_000   $ 6.00   │  reserved by calls in flight
  │  ──────────────────────────────────────────  │
  │  available            = balance − hold        │  computed, atomically
  │                        28_210_000   $28.21   │  never a column
  └──────────────────────────────────────────────┘
Three numbers, one of them derived. The derived one is never stored.

The other half is the invariant: `available = balance − hold`, computed atomically, never assembled in application code. A subtraction done in a template is a second place for it to be wrong.

Ready? Let's go.

Whether you want to examine the specific needs of your product, or go over the benefits of seams, we are here for you.