Exchange Engine 6.4.9

2 September 2026

This release has upgrade notes. Read them before updating — they describe behaviour changes that need your attention.

DIAGNOSTICSWITHDRAWALSBITCOINMONEROSOLANACONFIGURATIONECOSYSTEM

Ecosystem v6.4.9

Release Date: September 2, 2026 Tags: DIAGNOSTICS, WITHDRAWALS, BITCOIN, MONERO, SOLANA, CONFIGURATION, ECOSYSTEM

Overview

A diagnostics release with one withdrawal fix. Monero address validation was refusing valid stagenet and testnet addresses as Invalid Monero address. If you run any chain off its main network, read the Upgrade Notes. The rest of the release changes whether the chain requirements screen tells you the truth.

Two of its live checks were reporting failures on installs that were working. A self-hosted Bitcoin Core node that bitcoin-cli could talk to came back as Bitcoin Core RPC (getblockchaininfo): fetch failed, and a Monero pair that was crediting deposits came back as 401 after Digest auth — bad XMR_RPC_USER / XMR_RPC_PASSWORD. Both were faults in the test, not in the chain. An operator who trusted either one would have gone looking for a problem that was not there; one who learned to ignore them lost the screen entirely.

They are fixed, and the messages they print now name the address, the socket error and the thing to change. A new row reports currencies being hidden from the deposit picker because their token's network label disagrees with the chain's configured network — until now the only trace of that was a line in the log.

Requires Core v6.7.5.

Update Instructions

# 1. Update the addon in Admin → System → Extensions
# 2. Restart the backend
pnpm updator

No schema change, no seeder, nothing to repair. Re-run Admin → Ecosystem → Blockchains → Requirements afterwards.


Upgrade Notes

A currency missing from the deposit picker, with nothing on screen to say why

A currency is offered for deposit only while its token's network label agrees with that chain's {CHAIN}_NETWORK. The rule is right — a deposit against a mismatched row could never be credited, because the monitor is watching a different network — but its only output was a line in the backend log.

In 6.4.8 that rule began applying to the four chains that had been exempt from it: Solana, Tron, TON and Monero. On an install running one of them off its main network, token rows still labelled mainnet quietly stopped being offered, and nothing on any screen connected the disappearance to the label. That change shipped without a note; this is it.

  • Added a token-network row to the chain requirements screen for those four chains, naming each currency being withheld and the two values that disagree.
  • If a currency has vanished from your deposit picker, open Admin → Ecosystem → Blockchains → Requirements and read the new row for that chain. Correct whichever side is wrong — the token's network field, or the chain's {CHAIN}_NETWORK.

Monero withdrawals to stagenet and testnet addresses were refused

Address validation matched only the first character of the address against a set chosen by XMR_NETWORK, read that key without trimming it, and treated anything it did not recognise as mainnet. A valid stagenet address was refused as Invalid Monero address — blaming the customer's address for a server setting.

  • Fixed Monero validation to decode the address and read the network from the address itself, and to distinguish a wrong-network address from a malformed one.
  • If withdrawals have been refused this way, no funds moved and there is nothing to repair — set XMR_NETWORK to the network your daemon runs and retry.

Chain requirements rows that change verdict

Read these only if a chain requirements row changes verdict after you update — a row that was red and turns green was a false alarm, and a row that stays red is now telling you why.

  • A Bitcoin Core row that was failing may go green. The old check reported every transport failure as fetch failed with no address attached. If the cause was that the backend was dialling the mainnet port while your node runs a test network, the row now says so before you run the test.
  • A Monero row that was failing may go green. The old check answered the daemon's authentication challenge on a second TCP connection, which monerod refuses whatever the password is. Credentials that were reported as wrong were never wrong.
  • A Monero chain with XMR_<NETWORK>_RPC set now has every daemon in the list probed, not just one. A second daemon that is stale, unreachable or on the wrong network has always been dialled by the running platform; it is now visible on the screen as well.
  • pnpm btc-node:setup writes a different bitcoin.conf than it used to. If you run it on an existing install it will now match the file to BTC_NETWORK — see Changed.

Fixed

Monero addresses judged by their first character

Monero's address prefix is network-specific, and validation chose which prefixes to accept from XMR_NETWORK — comparing a single character, looking that key up without trimming it, and standing in the mainnet set for any value it did not recognise. Two things followed. A valid stagenet or testnet address was refused on any install whose XMR_NETWORK did not arrive spelled exactly as expected, reported as Invalid Monero address so the operator went to inspect an address that was correct. And a mistyped address that happened to begin with the right character passed the form, to be refused later by the wallet service.

  • Fixed validation to decode the address, verify the checksum Monero appends to it, and take the network from the address itself.
  • Fixed XMR_NETWORK to be trimmed and case-folded here the way the deposit currency list already read it, so the two can no longer disagree about which network the install runs.
  • Changed an address for the wrong network to be refused as exactly that, naming both networks, instead of as a malformed address.

A Bitcoin Core node that answers bitcoin-cli reported as "fetch failed"

The node RPC check reported the bare string fetch failed for every transport failure. That text is what the HTTP client produces when it cannot open a connection; the real cause — connect ECONNREFUSED 127.0.0.1:8332, and the address it was refused on — is carried underneath it and was being discarded. An operator comparing that against a working bitcoin-cli on the same box had nothing to reconcile the two with, because the screen never said which address it had tried.

The most common cause was invisible for the same reason. bitcoin-cli reads bitcoin.conf; the backend reads BTC_NODE_HOST and BTC_NODE_PORT. Bitcoin Core serves testnet4 on 48332, testnet3 on 18332 and signet on 38332, while BTC_NODE_PORT defaults to 8332 on every network — so a testnet install with the port left unset had a working CLI and a backend knocking on a closed door.

  • Fixed the Bitcoin Core RPC check to report the underlying socket error and the host:port it happened on, and to add the port a test network implies when .env is still on 8332.
  • Fixed a 401 to explain that bitcoin-cli authenticates from bitcoin.conf or the cookie file and the backend can use neither, so .env needs the rpcuser= / rpcpassword= pair or an rpcauth= user.
  • Fixed a 403 to name both refusals Bitcoin Core answers with an identical empty response — rpcallowip= and rpcwhitelist= — rather than asserting the first.
  • Fixed a request that never answers so it reads as a timeout with its budget rather than as an unreachable host.
  • Added a warning on the requirements report itself, before any test is run, when BTC_NETWORK names a test network and BTC_NODE_PORT is unset or 8332.

The estimatesmartfee and ZMQ rows failed for a cause already reported above them

One unreachable node produced three red rows. The fee and ZMQ probes call the same RPC endpoint as the check above them, so they failed for the same reason and said so in three different ways, none of which named the cause.

  • Fixed the estimatesmartfee(6) and ZMQ rows to be skipped, not failed, when the getblockchaininfo call itself failed. One cause now shows as one red row.

Monero credentials reported as wrong while deposits were being credited

The Monero checks reported 401 after Digest auth — bad XMR_RPC_USER / XMR_RPC_PASSWORD against credentials the deposit monitor was using without complaint.

monerod and monero-wallet-rpc hold the HTTP Digest session — the nonce and its request counter — on the TCP connection that issued the challenge, and treat an otherwise valid answer arriving on any other connection as stale. The diagnostics were built on a pooled HTTP client that gave no guarantee the retry would reuse the socket, so the correct password was refused. The addon's own client has always reused its connection, which is why the same credentials worked everywhere else on the platform.

  • Fixed both Monero probes to answer the challenge on the connection that issued it, the way the addon does.
  • Added a distinction the screen could not previously draw: a challenge the service marks stale is reported as the connection being closed or re-routed between the two requests — a reverse proxy or load balancer — and only a genuine mismatch is reported as the credentials.
  • Fixed an unreachable Monero service to name the socket error and the host:port, and to say that a working local tool proves the process is up rather than that .env points at it.

A single daemon probed where the platform uses a pool

With XMR_<NETWORK>_RPC set, the addon builds a failover pool. The check probed one address and declared the chain healthy on that evidence, so a second daemon that was unreachable, unsynchronised or on the wrong network stayed invisible until it served a query.

  • Fixed the Monero daemon check to probe every endpoint in the list, report how many answered, and name any that failed.
  • Added a failure when the endpoints in one pool disagree about which network they are on, and made a network mismatch on any of them reach the XMR_NETWORK comparison rather than being masked by a healthy sibling.
  • Fixed the daemon row to name the key it took its list from, so the row and the .env line you need to edit agree.

An https:// Monero URL rejected as unusable when the service dials it anyway

The Monero client passes only the host, port and path to a plain HTTP connection and never reads the scheme, so https://host:18081/json_rpc reaches a stock daemon exactly as http:// does. Only a URL with no port is genuinely broken, because it then resolves to port 80.

  • Fixed the URL rule to refuse only the case that cannot work — https:// with no port the parser keeps, which includes an explicit :443 — and to warn on the rest that the URL claims TLS the connection never negotiates.

Changed

Currencies withheld from the deposit picker now have a row of their own

The eligibility rule 6.4.1 introduced decides whether a currency can be offered at all, and 6.4.8 extended it to the four chains served by their own service. Neither release gave it a place on the requirements screen, so the one verdict that removes a currency from the customer's view was the one verdict an operator could not see.

  • Added a token-network check for Solana, Tron, TON and Monero that lists every enabled token row whose network label disagrees with the chain's configured network, and is therefore hidden from the deposit currency list. It reads the same rule the list itself filters on, so the screen cannot drift from what it is explaining.

pnpm btc-node:setup writes a configuration that matches BTC_NETWORK

The setup script wrote a mainnet bitcoin.conf and BTC_NODE_PORT=8332 whatever network the platform was configured for. Off mainnet that produced exactly the failure above: a node on one port, a backend on another.

It is also the file's own trap. Bitcoin Core reads rpcport= — along with port, bind, rpcbind, addnode, connect and wallet — from the top of bitcoin.conf for mainnet only; on any other chain a top-level value is ignored and the chain default applies. A port written at the top of the file therefore looks correct and does nothing.

  • Changed the script to read BTC_NETWORK from .env first and write the chain flag, the RPC port inside that chain's own [testnet4] / [test] / [signet] section, and a matching BTC_NODE_PORT.
  • Changed it to read and rewrite .env through the same parser the backend uses, so an export KEY=, a spaced KEY = or a duplicated key resolves the same way for both. It refuses to write a file that would not read back as intended rather than reporting success.
  • Changed it to stop and say so when BTC_NETWORK holds a value the platform does not accept, instead of configuring a node for mainnet.

Environment rows that described keys as decorative

Three keys on the requirements screen were labelled display-only after the services had begun reading them. SOL_<NETWORK>_RPC and XMR_<NETWORK>_RPC each replace an endpoint the platform actually dials, and a screen that calls them decorative invites an operator to leave a stale value in one.

  • Changed the XMR_<NETWORK>_RPC row to state that it replaces XMR_DAEMON_RPC_URL for every daemon query, and added the _FALLBACK row beside it.
  • Changed the SOL_<NETWORK>_RPC row the same way, and keyed it to the cluster the service resolved to rather than the value typed into SOL_NETWORK — so an install that fell through to devnet sees the key that is really read.
  • Changed the XMR_NETWORK row to say it names the daemon-list key as well as driving address validation, and the XMR_DAEMON_RPC_URL row to say it is still the address monero-wallet-rpc is first pointed at.
  • Changed the message a failing Bitcoin Core row prints about the fallback to mempool.space. The provider is chosen once and cached for the life of the process, and the deposit scanner and the withdrawal path choose at different moments, so a restart is required either way — the old wording implied the platform would return to the node on its own.