Building the platform's crypto payment rails — a fintech capability at every transaction boundary

A two-year epic threading fiat↔USDT conversion through every transaction boundary on the platform — where a precision slip is a real financial loss.

OMNI · ERC-20 · TRC-20 2-engineer pair, 2 years exact-decimal balances

Situation

The platform needed cryptocurrency deposits and withdrawals alongside fiat — conversion at every transaction boundary, on a platform where some third-party providers couldn't hold crypto at all.

Approach

I drove the payments side for two years as half of a two-engineer pair (alongside the team's assistant lead): wallet protocol integrations (OMNI, ERC-20, TRC-20), USDT deposits and withdrawals, and fiat↔USDT conversion threaded through every transaction boundary. Provider heterogeneity was the judgment call — some providers couldn't hold USDT at all, so I absorbed the difference in one cross-cutting compatibility layer rather than leaking it into every call site — with exact-decimal handling at the wallet edges, where a rounding error becomes a real discrepancy.

Outcome

Crypto rails went live across every payment flow; later currencies reused the same config-driven paths instead of re-opening them.

The design, in code

Representative snippet of the production design — rewritten for publication; not company source.

deposit-state.ts
// On-chain confirmations arrive more than once. Credit is a state
// transition guarded by the current state — a replayed webhook is a no-op.
const NEXT: Record<State, State[]> = {
  detected:  ['confirmed'],
  confirmed: ['credited'],
  credited:  [], // terminal — a second "confirmed" changes nothing
};

function transition(from: State, to: State): State {
  if (!NEXT[from].includes(to)) throw new Error(`illegal ${from}→${to}`);
  return to; // illegal jumps throw instead of double-crediting
}
wallet-decimal.ts
// At a wallet edge a rounding error is a real discrepancy. Never float —
// convert in integer minor units, and absorb provider quirks in one layer.
function toUsdt(fiatMinor: bigint, rate: Rate): bigint {
  return (fiatMinor * rate.num) / rate.den; // exact integer math
}

// Some providers can't hold USDT at all — the difference is
// absorbed here, not leaked into every call site.
function settleCurrency(p: Provider): Currency {
  return p.supports('USDT') ? 'USDT' : convertAtBoundary(p.fiat);
}