MMashDiv

Environment and service reference

Every environment variable, RPC method, timing constant, Redis key, row field, file and admin endpoint the Monero addon uses — the flat reference behind the guides.

3 min readUpdated 1 October 2026reference, environment, rpc, constants, endpoints, session-lock

Everything on this page is read out of the shipped source. Where a number is given it is a compiled-in constant, not a setting, unless it names the setting that changes it.

Identity

Property Value
Chain code XMR
Currency XMR
Decimals 12 — amounts are handled in piconero and divided by 10^12
Product ID 54578959
Licence file lic/54578959.lic
Explorer https://xmrchain.net
Log tag XMR
Master wallet filename master_wallet
User wallet filename The ECO wallet's UUID

Environment variables

None of the XMR_* keys is in .env.example; add them to the project root .env yourself. The scanner keys further down are a different case: ECOSYSTEM_BACKGROUND_SCAN and ECOSYSTEM_SCAN_PASSIVE_SKIP_CHAINS are described in the commented scanner section of .env.example, and ECOSYSTEM_SCAN_RATE_XMR is not. After any change restart every backend process — with the default PM2 file, pm2 restart backend cron. The XMR_* keys are read once, when the Monero service is first constructed in each process.

XMR_DAEMON_RPC_URLtype: urldefault: http://127.0.0.1:18081/json_rpc
monerod JSON-RPC URL including the /json_rpc path — http:// only, the addon does not negotiate TLS. On an install with one daemon it is used for get_info, get_fee_estimate and the /get_transactions check after an ambiguous relay, and it is the daemon a session points its wallet at after a daemon error. When a daemon list is set (the next key), the list's daemons do all of that and this key is only the starting value until one of them has answered.
XMR_MAINNET_RPCtype: url
Comma-separated list of monerod JSON-RPC URLs (write http://). When set it replaces XMR_DAEMON_RPC_URL for every daemon query. The pool is not ordered by priority — it prefers an untried endpoint, then the fastest — so every entry must be healthy and on the same network; the diagnostics probe them all. Substitute the network XMR_NETWORK names — XMR_STAGENET_RPC, XMR_TESTNET_RPC.
XMR_MAINNET_RPC_FALLBACKtype: url
An optional second list, appended after the first. Same format.
XMR_WALLET_RPC_URLtype: urldefault: http://127.0.0.1:18083/json_rpc
monero-wallet-rpc JSON-RPC URL including the /json_rpc path — http:// only, the addon does not negotiate TLS. Its host and port name the session lock, so every backend process must spell it the same way. A failing get_version when the service starts marks the chain inactive in that process, and wallet creation, balance reads and transaction history refuse until it answers. Withdrawals do not check that flag.
XMR_RPC_USERtype: secret
HTTP Digest username, sent to BOTH the daemon and the wallet RPC and forwarded as the daemon login in set_daemon. Leave unset only when both services run with --disable-rpc-login.
XMR_RPC_PASSWORDtype: secret
Password paired with XMR_RPC_USER.
XMR_NETWORKtype: stringdefault: mainnet
Declares the operating network — mainnet, stagenet or testnet; mainnet when unset. Selects no endpoint. Decides which network's addresses a withdrawal may be sent to — read there with spaces trimmed and case ignored, and a name that is none of the three enforces nothing. It is also compared, letter for letter and case for case, with the XMR token row's network to decide whether XMR is offered for deposit, so write it in lowercase. Compared against the daemon nettype at startup and by the diagnostics.
XMR_WITHDRAWAL_REFUSAL_FAIL_AFTER_HOURStype: numberdefault: 24
Hours a withdrawal may keep being refused because its wallet file is not the wallet on record, or its wallet row has no recorded XMR address, before it is failed and refunded. A value that is not a positive number means 24. Never applies to a withdrawal with a trxId, a txHashPending or a recorded relay claim.

Every wallet-rpc session also needs the platform's Redis (the REDIS_* settings of the core): the session lock lives there, and no session starts without it.

Ecosystem's background deposit scanner also affects this chain:

ECOSYSTEM_BACKGROUND_SCANtype: booleandefault: true
Set to "false" to disable the background deposit scanner platform-wide. On a Monero install this is the only path that catches deposits arriving after the user closes the deposit page.
ECOSYSTEM_SCAN_RATE_XMRtype: numberdefault: 0.05
Overrides the per-chain background scan rate in passes per second. The Monero default is 0.05 — one wallet sweep per twenty seconds — because each pass takes exclusive possession of the single wallet RPC.
ECOSYSTEM_SCAN_PASSIVE_SKIP_CHAINStype: stringdefault: XMR
Chains kept off the scanner's passive list — the addresses signing in and the wallet list register. An XMR address is still scanned after the customer opens the deposit page or their XMR wallet. An empty value skips nothing.

Keys read by nothing

Key Reality
XMR_WALLET_PASSWORD Wallet files are created and opened with an empty password regardless of this value. The diagnostics warn when it is set
XMR_WALLET_USER Listed in the admin diagnostics, but read by no runtime code

RPC methods used

Useful when you are writing a restricted RPC policy or reading a proxy log.

monero-wallet-rpc

Method Used for
get_version The startup health gate, and the probe a session sends after a timed-out call before it releases the lock. The only method sent outside a session
set_daemon Re-pointing the open wallet at the daemon after a refresh fails with a daemon error, or when the daemon pool moves
create_wallet New user wallet and the master wallet
open_wallet / close_wallet Every session; one wallet at a time
refresh Syncing before any read — without it, results are the on-disk cache
get_address Every session: proving the opened file is the wallet on record, again before every read and spend, and after every relay. Also reads a new wallet's address
query_key Reading the mnemonic at creation, for encrypted storage — asked twice, before and after the file is re-opened
get_balance Total and unlocked balance
get_transfers Deposit detection and the master wallet's transaction history. Deposit checks ask for in (the live monitor adds pending), never pool, and set no height filter, so they receive the wallet's whole incoming history and nothing that is still in the mempool
get_transfer_by_txid Proving the transfer being credited
transfer Always with do_not_relay: true: the fee probe, and building the real withdrawal with get_tx_metadata: true
relay_tx Broadcasting the transaction transfer built — one attempt, never retried

The addon never calls create_address, incoming_transfers or any integrated-address method.

monerod

Method Used for
get_info Height, synchronized, nettype
get_fee_estimate The network fee estimate — when the customer submits a withdrawal, before anything is debited; again inside the withdrawal handler; and when pool backing plans an XMR send
/get_transactions After an ambiguous relay, of a withdrawal or of a pool-backing send: whether the daemon has the hash, in its pool or in a block. A plain endpoint, not JSON-RPC, on the host of the daemon that last answered

get_info and get_fee_estimate go to the daemon list when one is set (XMR_<NETWORK>_RPC, failing over between its entries) and to XMR_DAEMON_RPC_URL otherwise. /get_transactions goes to the one daemon that last answered either of those calls — which is also the daemon a set_daemon repair points a wallet at.

Timing and limits

Session lock

One lock per wallet RPC, shared by every backend process.

Constant Value
Lock expiry 30 seconds
Renewal every 10 seconds
No successful renewal for 25 seconds, and the session is lost and sends nothing more
Hard cap on one session 30 minutes
Retry while the lock is busy every 250 ms, plus up to 250 ms of jitter
Waiting out a timed-out call get_version probes of up to 60 seconds, 1 second apart, at most 15 minutes
Live-monitor marker 30 seconds, refreshed while the check runs

How long each kind of session waits for the lock:

Session Waits at most
Withdrawal 90 seconds, including the wait in this process's own queue
Pool-backing send 20 minutes
Live deposit check 2 minutes
Background scan of one wallet 60 seconds
Wallet creation 2 minutes per attempt. A customer with no XMR wallet yet gets two attempts
Admin balance and history reads, and pool backing's balance reads 2 minutes
Dormant-wallet refresh Does not wait
Following a daemon move outside a session Does not wait

Only the withdrawal's figure includes the wait in the process's own queue. Each process runs one Monero session at a time, and every other kind of session first waits its turn there without a limit; the figure above starts when it reaches the front. A wallet creation queued behind a long session in the web process can therefore outlast the reverse proxy — 300 seconds with the documented Nginx configuration — and reach the deposit page as a 504 instead of the 500 that two refused attempts produce.

Deposits

Constant Value
Confirmations required to credit 6
Per-wallet check interval 5 seconds
Wallets checked per batch 3, sequentially
Loop sleep between passes 2 seconds
Empty checks before stopping 3 consecutive. "Empty" means the in and pending lists are both empty, so only a wallet that has never received anything can stop this way
Minimum monitoring before an early stop 10 minutes
Absolute monitoring cap 90 minutes
Idle retry cap 120 checks — only applies with nothing in flight. This is what ends the monitor of a wallet with any earlier deposit
First sight of a deposit Its first confirmation — transfers still in the mempool are not requested
Credited-transaction dedup cache 30 minutes
Scanner active list for an XMR address A customer's: 72 hours after the last deposit-page or wallet visit. The pool-backing treasury's: 72 hours after a settlement into it was dispatched or last checked. The Instant Convert house's, where that addon is installed: permanently, re-registered by the addon

Background refresh

Constant Value
Runs in The cron process, or the single process of an inline deployment
Exists from The first use of the Monero service in that process, and only if the chain is active then. In the cron process: an XMR withdrawal it runs (a PENDING row picked up by the sweep at boot, the 5-minute watchdog or the 30-minute job that runs the same pass), the scanner's first sweep of an XMR address on its active list (none with ECOSYSTEM_BACKGROUND_SCAN="false"), or pool backing reading or sending XMR. Until one of those happens after a restart, no dormant refresh runs at all; if the chain was inactive when it happened, none runs until the next restart
Considered stale after 6 hours
Refresh budget per wallet 10 minutes
Spacing 3 minutes from the end of one refresh to the start of the next. The first waits 3 minutes from when the process's monitoring loop first reaches the refresh — not from when the process starts
Throughput At most 20 wallets an hour, about 120 in the 6 hours a refresh counts as fresh
What counts as refreshed Kept in memory by the process that ran the refresh; empty after a restart, and not shared between the web and cron processes
Runs when That process's loop is idle, there is no daemon back-off, and no wallet is live-monitored in any process
Lock Tried once, never waited for; 30 seconds before the next try when busy
Priority Low — always behind deposits and withdrawals
Eligible wallets ECO wallets with a positive balance and a recorded XMR address

Withdrawals

Constant Value
Wait for the wallet RPC 90 seconds, then requeued — never failed for being busy
The process's withdrawal queue Held for the whole session, not only the wait: no withdrawal of any chain starts in that process until the XMR one has finished
Busy spell After one withdrawal waits 90 seconds in vain, the others only try, for 90 seconds
Watchdog retry of a requeued row Every 5 minutes, for rows older than 3 minutes
Maximum wait for locked outputs 2 hours from row creation
Persistent-refusal bound 24 hours — XMR_WITHDRAWAL_REFUSAL_FAIL_AFTER_HOURS
Gap that restarts the refusal clock 6 hours
Wallet open budget, refreshed by this process in the last 6 hours 2 minutes
Wallet open budget, otherwise 15 minutes
A refresh that outlasts its budget The withdrawal is FAILED and refunded ("Wallet RPC is not responding…"), not requeued
transfer call timeout 120 seconds, for the fee probe and for the build
relay_tx 1 attempt, 120 seconds — never retried
Daemon check after an ambiguous relay /get_transactions, 15 seconds
Stale PROCESSING threshold in the queue 5 minutes from the row's creation, after which a row with no trxId that is not in the sweeping process's own queue is flagged for manual review rather than reverted. The sweep runs where the scheduler runs — the cron process by default — so a withdrawal the web process is still working on is flagged too
Longest a single attempt can last About three quarters of an hour at the very most: a 90-second wait, a session capped at 30 minutes, and up to 15 minutes waiting out a timed-out call

RPC transport

Constant Value
Default call timeout 30 seconds
Daemon calls 3 attempts, one second apart
Wallet calls Retried only when the connection was refused; a timeout is never re-sent
Refresh default budget 60 seconds
Balance read refresh budget 120 seconds
Daemon back-off 30 s, doubling, capped at 5 minutes
Licence The service's own gate reads the file each time; the gate on the chain's routes caches its answer for 5 minutes

Redis keys

Key Holds
xmr:wallet-rpc:<host:port>:session The session holding the wallet RPC: <session number>:<hostname>:<pid>:<id>. Empty when free
xmr:wallet-rpc:<host:port>:fence The last session number handed out — one per attempt to take the lock, busy attempts included
xmr:monitoring:<walletId> A live monitor is checking this wallet; the background scanner skips it
xmr:monitoring-active Some wallet is being live-monitored somewhere; no dormant refresh starts

<host:port> is taken from XMR_WALLET_RPC_URL.

Transaction row fields

What a Monero withdrawal writes to its own transaction row beyond the status:

Field Written when Meaning
txHashPending Just before relay_tx The built transaction's hash: the relay was claimed. With no trxId, the outcome was never recorded — check the hash before doing anything. It is cleared only when a claim is given back, so it stays on a completed row
trxId After a relay that succeeded The hash relay_tx answered with; the row is COMPLETED. The same as txHashPending — if the two ever differ, the log says "answered tx …, but the built transaction was …"
fee When the request is accepted The platform fee debited on top of the amount. The fee record (adminProfit and the PLATFORM_FEE credit) is written only by the handler after its own COMPLETED write; a row completed by hand has none
metadata.xmrRelayClaim With txHashPending txHash, the session number and the time. Stays even if the claim is given back
metadata.xmrPersistentRefusal On each refusal because the wallet row has no recorded address or the file opens as another wallet, raised before anything was built reason (no_recorded_address or file_is_another_wallet), firstAt, lastAt, count, and failedAt once the row was failed for it
description Rewritten at every stage Held: "Funds are locked (x/y XMR unlocked). Waiting to process." Completed: "Withdrawal of … XMR to … completed. Tx: …". TIMEOUT: the manual-review text with the built hash. Failed and refunded: always "Refund of" and the row's amount to eighteen decimals, whatever the cause — the refund overwrites the failure reason, which survives only in the log (Failed to process transaction <id>: …) and in the customer's failure email

A failed withdrawal's refund is a second row: type REFUND, status COMPLETED, referenceId the withdrawal's id, amount the withdrawal's metadata.totalAmount (amount plus fee). The customer's own transaction history does not list it.

Ports

Only the P2P port needs to face the internet. Both RPC listeners should stay on loopback — the backend runs on the same host.

Port Service Exposure
18080 monerod P2P Public
18081 monerod RPC Loopback
18083 monero-wallet-rpc Loopback

The RPC ports are conventions, not requirements. Change them and change the two URLs in .env to match — nothing infers a port. The wallet RPC's port is part of the session lock's name, so every backend process must see the same URL.

Files that matter

Path Contents Loss means
<--wallet-dir>/<uuid> and .keys One customer's Monero private keys Their balance is unspendable. Not recoverable from the master wallet
<--wallet-dir>/master_wallet and .keys The platform's XMR wallet Accumulated withdrawal profit is unspendable
lic/54578959.lic Machine-bound licence The chain cannot be enabled
monerod data-dir The blockchain A full resync, not a loss of funds

The mnemonics are also held, encrypted with the Ecosystem vault key, in wallet_data. That copy is only usable with the key material in .env — see Master wallets and the vault.

Admin endpoints

GET/api/admin/ecosystem/blockchain/requirementspermission: view.ecosystem.blockchain
Full per-chain requirements report including every Monero key and prerequisite
POST/api/admin/ecosystem/blockchain/requirements/testpermission: view.ecosystem.blockchain
Live probes — wallet-rpc get_version, monerod get_info, and the XMR_NETWORK against nettype comparison
PUT/api/admin/ecosystem/blockchain/{id}/statuspermission: edit.ecosystem.blockchain
Enables or disables the Monero chain row by product ID. It only writes the row: a process whose Monero service is already active does not see it until it restarts, and no part of a withdrawal ever reads it
POST/api/admin/ecosystem/wallet/masterpermission: create.ecosystem.master.wallet
Creates the XMR master wallet — calls create_wallet with the filename master_wallet
GET/api/admin/ecosystem/wallet/masterpermission: view.ecosystem.master.wallet
Lists master wallets with their balances. The XMR balance is read from master_wallet in a wallet-rpc session; the list waits five seconds for it and otherwise shows the balance last stored. A read that succeeds is cached for one minute
GET/api/admin/ecosystem/wallet/master/{id}permission: view.ecosystem.master.wallet
One master wallet. For XMR it reads master_wallet in a wallet-rpc session on every call, uncached, and the request waits for it

Both reads are admin sessions: up to two minutes waiting for the wallet RPC, then a refresh of up to 120 seconds. Two things follow.

  • The one-minute cache is written only by a read that succeeds. While a read fails, or takes longer than a minute, every load of the list starts another session, and the five-second wait does not cancel the one already running — they queue behind each other. Reloading the list while the wallet RPC is slow adds to its queue rather than getting an answer sooner.
  • The detail endpoint has no cache at all. Each call is a fresh session, and the response does not come back until it has finished or failed. A failed read is not an error on the screen: the balance last stored is returned. Nor is a read of exactly 0 stored, so an emptied master_wallet keeps showing its last non-zero balance on the detail view until the list, which does store a 0, is loaded.
POST/api/admin/ecosystem/token/importpermission: create.ecosystem.token
Registers XMR as a NATIVE token with a blank contract
PUT/api/admin/ecosystem/token/{id}/statuspermission: edit.ecosystem.token
Enables or disables one Ecosystem token — what the token list's switch calls. Refuses every change, disabling included, while the token's chain row is switched off
PUT/api/admin/ecosystem/token/statuspermission: edit.ecosystem.token
Sets the status of several Ecosystem tokens at once, from a body of ids and a status. It does not check the chain's row, so it can disable the XMR token while the chain is off. No admin screen calls it
GET/api/ecosystem/wallet/{currency}
Returns the user's ECO wallet, creating the Monero wallet file on first call