Deposits and confirmations

How a customer's XMR deposit address comes into existence, how the addon detects an incoming transfer without an explorer, the six-confirmation rule, and the monitor lifecycle that keeps watching after the user closes the page.

6 min readUpdated 3 August 2026deposits, confirmations, wallet-rpc, monitoring, subaddresses

Monero deposit detection has no explorer, no webhook and no address index. The only thing on earth that can tell you a transaction belongs to an address is a wallet holding that address's view key. So the addon does the only thing it can: it keeps a wallet file per customer and asks each one, repeatedly, whether anything arrived.

Everything below follows from that.

One wallet file per customer wallet

The first time a user opens the XMR deposit page, the platform finds or creates their ECO wallet for XMR and then, because the address map has no XMR entry yet, calls create_wallet on monero-wallet-rpc with the ECO wallet's UUID as the filename. It reads back the primary address and the mnemonic, encrypts the mnemonic with the Ecosystem vault key, and writes both the address map entry and a wallet_data row.

Returns the user's ECO wallet for a currency, creating and backfilling addresses as needed

So your --wallet-dir accumulates one wallet per customer who has ever viewed the XMR deposit page. That is the design, and it is why the wallet directory is the single most important thing to back up on this chain — there is no HD seed these can be re-derived from.

Every operation uses account_index: 0 and the wallet's primary address. The addon never calls create_address, and it never generates an integrated address with a payment ID. A user gets one address, permanently, and it is a standard address beginning with 4 on mainnet.

This is why the model is one wallet per user rather than one wallet with many subaddresses: subaddress accounting would need a payment-ID or index mapping the platform does not keep.

Detection: two paths, both serialised

The wallet RPC can only hold one wallet open at a time, so every check is a queued open → refresh → get_transfers → close cycle. Two things schedule those cycles.

The live session monitor

When a user is on the deposit page, the frontend holds a WebSocket to the Ecosystem deposit endpoint. That registers a Monero monitor for their wallet, which joins a global monitoring queue.

The processor then loops: it collects every monitored wallet whose last check was more than five seconds ago, works through them in batches of three (sequentially — the wallet RPC cannot parallelise), and sleeps two seconds between passes. Each pass calls get_transfers with in: true and pending: true.

The refresh before get_transfers is not optional and not cosmetic. A freshly-opened wallet answers from its on-disk cache, so without an explicit refresh the wallet reports the world as it was at its last sync — which is how a wallet with funds in it reports a zero balance.

The background scanner

Deposits do not stop arriving when the user closes the tab. Ecosystem's background deposit scanner keeps a working set of recently-seen deposit addresses in Redis, with a 72-hour TTL refreshed on every visit, and sweeps them on a rate-limited schedule.

Monero's rate is deliberately the slowest of any chain: 0.05 passes per second, one wallet sweep every twenty seconds, because each pass takes exclusive possession of the single wallet RPC and would otherwise starve live sessions. A sweep is a no-op if a live monitor already owns that wallet, and crediting is idempotent, so the two paths cannot double-credit.

ECOSYSTEM_BACKGROUND_SCAN="false" turns the scanner off platform-wide. Do not do that on an install with Monero enabled — it is the only thing that catches a deposit made hours after the deposit page was closed.

The six-confirmation rule

A transfer is credited when confirmations >= 6. Nothing lower is credited, ever, and there is no setting that changes the number.

Monero blocks target two minutes, so six confirmations is roughly twelve to twenty minutes after the transaction is mined — longer if it sat in the mempool first. Tell your support team that number, because "my deposit is missing" at the eight-minute mark is the most common false alarm on this chain.

Below six confirmations the transfer is not ignored. Each time the confirmation count changes, the addon broadcasts a pending update over the deposit WebSocket carrying the transaction hash, the amount, the fee, the current confirmations and the required six, with status PENDING. Transfers still in the mempool are broadcast the same way with zero confirmations. That is what drives the progress the user sees.

What a credited deposit records

Once six confirmations are reached, the addon fetches the transfer with get_transfer_by_txid and writes a transaction with:

Field Value
chain XMR
contractType NATIVE
type DEPOSIT
status COMPLETED
address The user's own Monero address
from N/A
amount / fee Piconero divided by 10^12, to eight decimal places

Two of those rows deserve a note.

from is N/A and always will be. Monero transactions do not reveal a sender. If your compliance process expects a source address on every deposit, this chain cannot supply one — decide that before you enable it, not during an audit.

Amounts are stored to eight decimal places although Monero has twelve. The last four digits of a piconero amount are not retained on the transaction record. In practice this only matters for dust; it is not a rounding of the customer's credited balance beyond the eighth place.

Crediting is deduplicated twice: an in-memory walletId-txid key with a thirty-minute expiry, and a database check for an existing COMPLETED transaction with the same trxId and the same walletId. The pairing with walletId is what makes Monero's pay-to-many work — one transaction can pay several of your customers at once, and each wallet must credit it independently.

Monitor lifecycle

The monitor is not tied to the WebSocket session, and this surprises people.

A monitor stops when either of two things happens:

  • It has been idle. Three consecutive checks that find no incoming and no pending transfers, and the wallet has been monitored for at least ten minutes. Both conditions are required; the ten-minute floor exists because a wallet that has not finished syncing legitimately reports nothing.
  • It hits the wall-clock cap. Ninety minutes, unconditionally.

There is also a retry cap of 120 checks, but it only applies when nothing is in flight. While a deposit is visible and still confirming, the monitor keeps running past the cap until six confirmations or the ninety-minute ceiling — because a monitor that stopped at the cap would leave a transaction to confirm with nobody watching, and it would never be credited.

Closing the deposit page does not stop a Monero monitor. Other chains implement a polling stop that the WebSocket teardown calls; the Monero monitor does not, so it runs its own course. That is the correct behaviour here: the user usually closes the tab long before six confirmations.

Background staleness refresh

Separately from any deposit, the addon keeps dormant wallets from drifting too far behind the chain. When the monitoring loop is completely idle — no monitored wallets, nothing queued, no daemon back-off — it picks the single most stale XMR wallet with a positive balance, round-robin, and refreshes it at low priority with a ten-minute budget. "Stale" means six hours since its last refresh.

The point is latency at the moment it matters. A wallet that has been synced in the last six hours opens for a withdrawal in about two minutes; one that has not is given fifteen. Without the background refresh, the first withdrawal from a long-dormant wallet would spend that fifteen minutes with a customer watching.

Operating notes

  • Wallet count is a real capacity number. Every customer wallet is a file and every check is an exclusive open. A few thousand wallets is fine; the serialisation, not the disk, is what eventually binds.
  • Deposits stop the moment the daemon does. The wallet RPC can be perfectly healthy and still return nothing, because it cannot sync. Monitor monerod, not just the wallet RPC.
  • XMR is the log tag. Every line the service emits carries it. See Troubleshooting for what to grep for.