Payments Engine

Treasury

Where the float actually sits, how it gets there, and when it is not worth moving. The treasury context is the difference between paying customers and paying miners.

Tiers

TierWhat it holdsWhy it exists
UNSWEPTConfirmed deposits still sitting at the addresses they arrived at.Money you have, in the most awkward possible arrangement — a thousand addresses with dust in each.
HOTConsolidated, online, available for signing.What payouts actually draw from. Kept deliberately small: it is the amount an attacker could take.
COLDOffline, requires a two-actor procedure to move.Where the rest lives. Slow on purpose.

Reading positions

node
const positions = await payments.treasury.positions('BTC');

positions.totalMinor;   // "9007199255240993" — a string, always
positions.tiers;        // [{ tier, amountMinor, updatedAt }]

// Arithmetic in BigInt, never Number.
const hot = positions.tiers.find(t => t.tier === 'HOT');
const canPay = BigInt(hot.amountMinor) >= BigInt(withdrawal.amountMinor);

updatedAt is worth reading, not just the amount. A position that has not moved in hours during a busy period is more interesting than the number itself.

Sweeps

A sweep consolidates unswept deposits into the hot wallet. Candidates are selected, assembled into a batch with pinned inputs, signed by custody through the same allowlisted path as any payout, and broadcast idempotently.

1
Select
Candidates above the dust threshold, ranked by value against the fee they would cost.
2
Assemble
Inputs pinned into a batch so the signed payload is deterministic and re-broadcastable.
3
Sign
Through custody, under policy — sweeps are not exempt from velocity caps.
4
Broadcast
Idempotent, under a reservation, exactly as a payout is.
5
Reconcile
Positions updated only from confirmed on-chain totals, never from intent.

When a sweep is not worth doing

A sweep that costs more in fees than it consolidates is a loss, not a chore. The policy carries a fee ceiling as a proportion of the amount moved, and a candidate that breaches it is skipped — with the reason recorded, so nobody has to guess later why the float looked wrong.
Policy controlEffect
Minimum sweep amountDust below this is never swept alone. It waits for a batch.
Fee ceilingA proportion of the swept amount. Breach it and the candidate is skipped, with a reason.
Low-fee windowNon-urgent sweeps are scheduled when the observed fee rate is favourable.
Batch sizeBounded, so one sweep cannot become a transaction too large to confirm.
Skip recordsEvery skip is a row with a reason. "Nothing happened" is never a silent state.

Float thresholds

A threshold per asset defines the minimum hot balance. The worker checks continuously and emits treasury.hot_low when it is breached — before payouts start failing, not after.

webhook
{ "eventType": "treasury.hot_low",
  "asset": "BTC",
  "hotMinor": "412000",
  "thresholdMinor": "500000",
  "shortfallMinor": "88000" }

Page on this. A hot wallet that empties during a busy Saturday turns into a queue of failed withdrawals and a support incident, and the warning arrived hours earlier.

Cold to hot

Moving from cold to hot above the configured amount requires two actors: one proposes, another approves, and both are recorded with the approval token that authorised it. Neither can complete the movement alone.

This is the control that survives one compromised operator account. It is also the one people are most tempted to disable during an incident — which is precisely when it is doing its job.