Spot deposit modes — how a deposit to the shared exchange address finds its owner
Why a Spot deposit has to be attributed at all, the theft the old hash claim allowed, the three modes an admin can choose between (hardened hash claim, exact-amount match, the customer's own Ecosystem address), what each shows the customer and costs the platform, what lands in review, and the console that clears it.
A Spot wallet is custodied on your exchange provider, and the provider gives your platform one deposit address per currency and network. Every customer who deposits to Spot is shown that same address. Nothing on chain says which customer a transfer came from, so the platform has to work out whose deposit it was, and that is what a spot deposit mode decides.
Until this release the answer was "whoever pastes the transaction hash first". The claim route bound a hash to the account that submitted it and checked nothing else; once the exchange listed the deposit as accepted, that account was credited. The address is public, so anyone watching it could paste an incoming deposit's hash before the real sender did — and the real sender was then told the transaction already existed. That is closed in every mode below by one rule: a credit needs an intent that predates the deposit. A customer declares the deposit before sending it, the platform records the intent, and no deposit is credited against an intent younger than itself.
The setting
Admin → System → Settings → Wallet → Transactions → spotDepositMode.
Super Admin only. The mode after this update is hash_claim, which is what
ran before, with the hole closed — an upgrade never changes what a customer
is shown until you choose. The switch affects only new intents; an intent
already open finishes under the mode it was created in.
| Value | What the customer does | What the platform does |
|---|---|---|
hash_claim |
Declares the amount and network, is shown the shared address, sends, pastes the hash | Accepts the hash only against that customer's open intent for that currency and network, and credits only when the exchange lists the deposit as accepted, no older than the intent, for the declared amount net of the exchange's own fee |
amount_match |
Declares the amount, is told the exact amount to send (the declared amount plus a tiny nudge in the last decimals), sends. No hash. | Polls the exchange's deposit list once per currency every fifteen seconds, in a loop that starts when the currency's first amount-match intent is declared and stops sixty minutes after the loop itself started (or sooner, once nothing is open) — a later intent on the same currency shares whatever is left of that loop's hour rather than getting an hour of its own, and after the loop stops only the fifteen-minute job matches it; that job also carries the seven-day matching tail, a restart and a closed tab. Credits the intent whose exact amount arrived. The hash field stays on the screen as a fallback for a customer who sent a different amount |
ecosystem_custody |
Is shown their own permanent Funding address for that currency and chain, sends. No hash, no amount. | Credits the Funding wallet as any Ecosystem deposit, then sweeps the coins to the exchange's deposit address through the customer's own withdrawal path, claims the sweep's hash on the exchange itself, and credits Spot when the exchange lists it |
The third mode needs the Ecosystem extension and a listed token on that
chain. Where a network cannot use it — no Ecosystem token for the chain, a
network the exchange has disabled, an exchange address that needs a memo — the
customer is shown the amount_match flow for that network instead, and the
console says which mode each intent ran under.
The intent
Every mode writes one row to spot_deposit_intent when the customer
declares: who, which currency and network, which mode, the declared amount,
the exact amount to send (mode B), the address shown, and a sixty-minute
window. A deposit is attributable to an intent only if the exchange's own
timestamp for it is no earlier than two minutes before the intent was created;
matching keeps running for seven days, so a slow chain does not lose a
customer their money, only their place in the queue.
Limits that keep the table honest: at most three open intents per customer,
one per currency and network (asking again returns the same one), and at most
ten hash submissions per customer per hour. Under amount_match the exact
figure is unique for the currency across every network while it can still
arrive — it is not handed to a second customer until the first one's window
has closed — and two deposits that would satisfy two intents at once credit
nobody: both land in review.
What lands in review, and why nothing is credited blind
An intent goes to REVIEW rather than being credited when the deposit is found but does not satisfy its intent: the amount differs from what was declared (after the exchange's deposit fee), the deposit is older than the intent, or two intents claim it. A claim with no intent at all — an app build from before this release, pasting a hash the old way — is accepted, confirmed against the exchange, and then left pending for an admin instead of credited, on the console's pre-intent claims tab. The customer's screen shows Being reviewed — "This deposit is being reviewed" — with one sentence naming the reason (for a claim with no intent, "This deposit was submitted without a deposit request, so it is being reviewed by our team before it is credited."), and nothing is paid to whoever pasted first.
Under ecosystem_custody, review also holds a deposit below the exchange's
minimum or the Ecosystem withdrawal minimum: the coins stay in the customer's
Funding wallet, and the screen says so.
The console
Admin → Finance → Spot Deposit Intents — permission
view.spot.deposit.intent to see it, edit.spot.deposit.intent for the
doors. The list opens on what needs a human (review and failed) and can show
what is in flight. Each row names the mode, the stage, the reason it is
waiting, the exchange's evidence for the hash it carries, and where the money
is right now:
- Approve credits the customer's Spot wallet through the same locked path the deposit log uses, records the pool-backing evidence, and closes the intent. The amount is prefilled from what the exchange saw when the claim row carries no figure of its own — a row with its own amount keeps it, and a pasted-hash claim row carries zero until it is credited.
- Reject releases the hash so the real sender can claim it and closes the intent, naming where the coins are. A mode C intent whose sweep is already debited but not yet on chain cannot be rejected — closing it would strand a committed withdrawal — and the door says so.
- Resweep re-runs a mode C sweep that failed to broadcast. It refuses when the previous sweep did reach the chain: a second sweep would send the customer's coins twice.
A claim with no intent behind it has no intent row, so it is listed on the console's Pre-intent claims tab instead: the pending deposit rows from the old flow, each with the exchange's own answer for its hash. Approve and reject act on the claim itself — approve credits through the deposit log's locked path, reject releases the hash — and the badge over the menu entry counts both queues. The same rows stay visible in Deposit Records.
Mode C, step by step
- The customer's own Funding address is shown, the intent is recorded, and the deposit monitors watch the address as they would for any Ecosystem deposit.
- The deposit arrives and credits the Funding wallet exactly as before — idempotent, so a deposit seen by two monitors credits once.
- A hook after that credit finds the open intent for the wallet and chain, quotes the network fee, and creates a sweep: a withdrawal from the customer's Funding wallet to the exchange's deposit address for that network, through the same queue and movers a customer withdrawal uses. The customer pays the network fee out of the swept amount; the Ecosystem platform withdrawal fee is not charged, no confirmation email is sent, and no profit is booked. A sweep is never batched with other UTXO withdrawals: one hash must mean one customer.
- When the sweep broadcasts, the platform claims its hash on the exchange for the customer — the same pending deposit row the hash claim creates — and the ordinary verification watches for it. The pool-backing guard lets it through because the customer's own withdrawal row vouches for the hash.
- When the exchange lists the sweep as accepted, Spot is credited. The customer's screen shows the stages as they happen: received, moving to Spot, on the exchange, credited.
A sweep that fails to broadcast is retried once after ten minutes, then the intent is FAILED with the coins refunded to the Funding wallet and the resweep door open. A sweep the chain mined but the exchange has not listed by the end of the seven-day matching window is parked in review, naming the sweep's hash and that the coins are on the exchange under the platform's account; the customer is told once, and the console's approve door credits them once the exchange shows the hash.
Choosing
- Stay on
hash_claimif your customers are used to pasting hashes and you want no visible change. It is closed to theft now; its residual risk is a thief pre-declaring a popular round amount and racing the hash, which the rate limits bound and the amount rule catches. - Choose
amount_matchto remove the hash entirely at the cost of asking the customer to send an exact figure. It has no residual theft risk: the exact figure names one customer. - Choose
ecosystem_custodywhen you run the Ecosystem extension and want a deposit that needs nothing from the customer but the send. Every Spot credit it makes is backed on the exchange at the moment it is made, which is the property the Pool backing page measures for everything else. It costs one on-chain transfer per deposit and a second confirmation wait.
amount_match and ecosystem_custody are covered by the unit suites and,
for the sweep, by an on-chain suite against a local node with a scripted
exchange; neither has yet been exercised against a live exchange provider.
Run one deposit per mode on staging with your provider before you switch.