# Crypto deposits

Credits are bought with cryptocurrency, and crediting money read off a public
chain has no undo. A credit posted from a wrong price is money given away; a
payment silently not credited is money a customer lost. Every rule below
exists because one of those two failures is worth preventing.

The standard the design is held to is that a money error is unacceptable. The
price paid for it is latency: a deposit sometimes waits longer than it
strictly had to.

## The address is permanent, and nothing here can spend from it

Each account has one deposit address per chain, and it does not change between
top-ups. A wallet with it saved can keep sending to it, and a payment that
arrives with no quote behind it is still credited.

The address is derived rather than assigned. A hierarchical deterministic
wallet produces a whole tree of addresses from one key, and this service holds
only the **public** half of that key.

That is the property worth stating carefully. Deriving a customer's address
from a public key needs no private key, because the derivation steps involved
are the unhardened ones. The process that watches the chain and credits
balances therefore has no way to move any of the money it is watching. A
compromise of this service does not reach the funds; it reaches a list of
addresses that are already public on a chain.

The spending key lives outside the service entirely, and nothing in the
request path has ever seen it.

## A quote is a price, not a reservation

A top-up starts with a quote. It names an asset, a number of credits, and the
amount of coin that buys them at the price it was computed at.

The price is **locked**, and the lock has three bounds. Each one closes a
different way of getting value out of the product for free, and none of them
is about the honest payer.

**Time.** The lock holds for the quote's 2 hour live window plus a 6 hour
grace past it. Without a bound, a payer waits and pays only if the asset has
fallen, funding the same credits with less money. A quote whose asset rose is
never paid at all. Six hours is far longer than any of these chains needs to
confirm a payment. It is far too short to be worth holding an option over.

**Amount.** The lock funds the quoted amount plus 2 %, counted across every
transfer that quote ever attracts. The tolerance exists for the wallet that
takes its network fee out of the amount. It exists for the payer who rounds
the figure up while typing it. An unbounded tolerance would be a free option
on the market. Send one quote's worth while the price holds, and a hundred
times that once it has fallen.

**Order.** A transfer whose block is dated before the quote gets no lock at
all. The other direction is a look-back option. Pay first, watch the market,
then mint a quote at the old price once it has moved your way. A small
per-chain tolerance is allowed against it. A block's timestamp is the block
producer's opinion and a quote's is a database's, and the two clocks were
never synchronized.

What falls outside any of the three is still credited, at the corroborated
current price. The deposit is flagged for a person to look at. A late payment
loses the old price and not the money.

## A price needs corroboration

A price is never one source's opinion.

With three or more answers, the median decides and the outliers are discarded.
With exactly two, the median is meaningless. Both sit the same distance from
their own midpoint, so any tolerance keeps both or neither. The pair is used
only if the two agree within 2 %. With no agreement there is no price, and a
quote that cannot be priced is refused rather than guessed.

A deposit that arrives while no price is available waits for the next pass.
Half of it is never credited against a row already marked paid.

Stablecoins are priced like everything else rather than assumed to be worth a
dollar. A stablecoin is a claim, not an identity, and the one moment its price
is worth reading is the moment it is not holding. A reading outside a narrow
band around a dollar is refused rather than clamped to the edge of the band.
Pricing a depegged token at the floor would pay the difference on every top-up
for as long as the depeg ran.

## Depth is proportional to what is at stake

A transfer is not credited the moment it appears on a chain. It has to be
buried under enough later blocks that undoing it would cost more than the
credit it would claw back.

Each chain has two depths, and the deeper one is required once the account's
recent shallow exposure on that chain passes a threshold. A small deposit is
credited quickly; a large one waits.

That is a deliberate trade of latency for safety, sized by value rather than
applied flatly. A flat depth is either too slow for the ordinary payment or
too shallow for the large one.

Settlements for one account are serialised. Without that, two transfers of one
account settle in parallel, each reading the exposure before the other has
written its credit. A payment split in two then walks past the depth it should
have had to reach.

## Arriving amounts, and what happens to each

Three cases cover every amount that is not exactly the quoted one.

Less than quoted is credited for what arrived, at the locked price. The
difference sent later to the same address is credited the same way.

Up to the tolerance more converts wholly at the locked price.

More than that splits. The quoted portion converts at the locked price and the
excess at the current one, in the same ledger entry and the same commit. One
deposit is one movement, whatever it was funded by.

Every conversion rounds **down** to a whole credit. A fraction of a cent is
not credited. A transfer worth less than one credit after conversion is too
small to credit at all. The arithmetic produces nothing, and a credit of zero
would be a row saying money arrived and nothing happened.

A single transfer worth more than the ledger can hold is recorded as refused
with a flag rather than thrown away. A throw would roll back the row that says
money arrived, which is the one record that must survive whatever else does
not.

## What the watcher does not promise

Two limits are worth knowing because no amount of care removes them.

**Value moved by a contract call is invisible to these scans.**

A native-coin transfer made by a contract during a call does not appear the
way an ordinary transfer does. Crediting it is a manual step, and tokens are
not affected.

**An endpoint that lies consistently is eventually believed.**

The watcher refuses a chain reading that outruns wall-clock time or falls too
far behind what it last saw. It re-anchors when a run of consistent readings
agrees. That bounds a mistaken or transient answer. It is deliberately not a
defense against a sustained hostile one. A check that can never change its
mind wedges the watch on a chain whose anchor is wrong.

## Where a deposit goes when automation stops

A quote's live window is 2 hours, and the addresses keep being re-read by a
daily pass for 30 days after it.

Past those 30 days the automation stops, and that is all that stops. The
address stays valid, the funds are not lost, and a deposit that arrives
afterwards is a support conversation rather than a loss. Knowing that boundary
is the point of publishing it.
