MMashDiv

Exchange Engine 6.6.0

Latest

13 October 2026

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

WITHDRAWALSADDRESSESGASTRADES FEEDDEPOSITSCOVERAGE

Ecosystem v6.6.0

Release Date: October 13, 2026 Tags: WITHDRAWALS, ADDRESSES, GAS, TRADES FEED, DEPOSITS, COVERAGE

Overview

A withdrawal to an address that cannot be right is now refused before the customer is charged. A Bitcoin, Litecoin, Dogecoin or Dash withdrawal to a malformed address, and a Tron one that fails its checksum, was accepted, queued and debited, and came back as Failed with a refund minutes later. A tester's Bitcoin testnet withdrawal to mbullbubllbullbullbull666 did exactly that. It is now refused on the spot, with the reason.

Two other changes decide what a customer can withdraw. A native-coin withdrawal on an Ethereum-style chain has to leave its network fee in the wallet, so a withdrawal of the whole balance is refused with the amount that can be sent. And a payment to an address nothing ever funded gets the gas that chain charges for creating it, which Sepolia now prices at about 207,000.

The Trades tab no longer shows another market's prints, and the custody overview lists a coin once however its tables spell it.

Requires Core v6.8.6 for one fix, a deposit page opened with a lower-case currency. Everything else works on Core 6.8.5.

Update Instructions

pnpm updator

No schema change, no seeder, no .env change. The backend restarts at the end. Update Core 6.8.6 in the same window.


Upgrade Notes

A customer can no longer withdraw a whole native balance

Only the amount and the platform fee were taken from the customer's balance when a native withdrawal was requested. The gas was taken after the transfer confirmed, and when the wallet no longer held it, that second debit failed and the platform paid. The gas estimate was only informational, so a withdrawal of the whole balance passed.

  • Changed the withdraw request on every Ethereum-style chain with a native-coin gas price. A withdrawal that would not leave the gas behind is refused with "Insufficient funds for the network fee: about X must stay in your wallet to pay it. You can withdraw at most about Y to this address." On Ethereum the reserve is 21,000 gas at the current price.
  • It applies to a customer's own withdrawal and to Instant Convert's one-click withdrawal. Sweeps and Convert & Send are not held to it.
  • The withdraw form asks for the maximum before an address is typed, so the Max figure reserves the dearest case: a payment to an address nothing ever funded. On a chain that charges for creating the account that is a large reserve (about 207,000 gas on Sepolia). On Ethereum it is 21,000 gas.
  • Tell your support team: a customer whose full-balance withdrawal is refused should withdraw the amount in the message.

Withdrawals to a malformed address

  • The check also runs for a Convert & Send and for a spot-custody payout. A spot-custody destination that fails it is held for review when the payout is created, instead of failing later.
  • A refused withdrawal has already used the customer's one-time two-factor code. They need a new one to try again.
  • Write BTC_NETWORK in lower case. The payout side has always treated only the exact word mainnet, or an empty variable, as mainnet, and anything else, Mainnet included, as testnet, while deposits read the variable without regard to case. On an install with Mainnet deposits were credited, and a withdrawal to a mainnet address was debited and refunded. It is now refused at once with "configured for testnet".

Changed

Native withdrawals leave their gas in the wallet

  • Changed how a native withdrawal on an Ethereum-style chain is quoted. The platform asks the node what the transfer will cost: from the chain's master wallet, with 1 wei, to the destination, or to a never-funded address when no destination is given yet. The withdraw request and the Max endpoint use the same figure, and the Max endpoint takes an optional destination. See Upgrade Notes for the refusal.
  • Not changed: the quotes for a Convert & Send fee and for a sweep still assume 21,000 gas. On a chain that charges more for a first payment to an address, the platform pays the difference on those.

Fixed

Withdrawals to a malformed address were debited, then failed

Reported from a live install ("No address validation?"): a Bitcoin withdrawal on testnet4 to mbullbubllbullbullbull666 was accepted, queued, debited and ended Failed with "Refund of 0.01". The withdrawal check had no address rule at all for Bitcoin, Litecoin, Dogecoin and Dash, so the destination reached the batch builder, which was the first thing to judge it. The format check admits any alphanumeric string and the network guard only looks at the first character, and m is a testnet prefix. The same road was open for a plain Tron withdrawal: the checksum test ran for a Send and not for a withdrawal, so Tnotarealaddress took it.

  • Fixed it by asking the payout code's own question before the wallet is read or anything is written. A destination that fails is refused with "Invalid Bitcoin address: …. It is not a well-formed Bitcoin address (check for a typo or a truncated paste)." (the coin's name changes). The door and the payout cannot disagree about what an address is.
  • Fixed the wrong-network case. A Bitcoin address that is well formed on the other network is named as that: "This is a Bitcoin mainnet address, but this platform is configured for testnet. Use a testnet address." The generic sentence would send the customer looking for a typo that is not there.
  • Bitcoin follows BTC_NETWORK, as payouts do. Litecoin, Dogecoin and Dash each have one network, so valid means what the payout accepts. Tron takes the same checksum test for every purpose, not only a Send. Ethereum-style chains, Solana, Monero and TON were checked before and are unchanged.

A first payment to a never-funded address ran out of gas

Sepolia now charges for creating the account a first value transfer opens: about 207,000 gas to an address nothing ever funded, 21,000 to one that was funded before. Mainnet charges 21,000 for both. A native withdrawal or a Convert house Send was sent with a fixed 21,000 gas limit, so on such a chain a first payment to a fresh address ran out of gas and burned its fee.

  • Fixed it: the platform asks the node. Where the node says 21,000 or less, the limit is exactly 21,000. Above that the limit is the node's estimate plus 20%, only for a destination with no code and only up to 400,000. The ceiling is applied to the buffered figure, so an estimate above 333,333 gas is refused unsent, not clamped. A destination with code keeps 21,000, so a contract that burns gas cannot set the limit the payer fronts.
  • On a chain that answers 21,000 or less nothing changes, except that every native withdrawal now makes one gas-estimate call to the node first. An estimate error other than insufficient funds fails the withdrawal, and the customer is refunded.

The Trades tab showed another market's prints

Reported from a live install: BTC/USDT prices (82351.36, 83187.42, 75000.00) in the XMR/USDC order book's Trades tab, among the XMR trades (590.00, 550.00), gone after a page reload.

Trades arrive under a stream key that has no market in it, so every trades listener on a connection is handed every trades frame that connection receives, and a symbol's frames keep coming until its unsubscribe lands. A trade row names no market either, so the only thing the website could filter on was the frame, and the frame named none.

  • Fixed it: both places that write a trades frame, the engine's fill push and the market route's poll and subscribe answer, now put the market symbol on it. The website already dropped a frame for another market, so no frontend update is needed and the fix works with Core 6.8.5. An older backend that sends no symbol is still delivered as before.
  • Orderbook frames carry no symbol of their own, so a stray one is still not filtered by this change.

The custody overview showed one coin twice

A tester saw the BSC testnet coin twice on the Settlement coverage card of Admin → Ecosystem → Overview: TBNB owed 3.39 with nothing recorded, rated Unbacked with "No chain address", and tBNB recorded 3.39 with nothing owed and "0 wallets", each the mirror of the other, when the coin was covered to the last digit.

The coin is tBNB in the token registry and in the custody rows made from it, and TBNB on every customer wallet and in the Instant Convert house wallet. The database merged the spellings inside each total, so the numbers agreed, but the page then joined the three results on the exact string.

  • Fixed it by keying every total on the upper-cased code and adding rows that share a key. The coin is shown once, under the wallet's spelling, and a chain card lists it once.
  • The counts in the roll-up fall by one for each coin that used to appear twice (the assets tracked, the assets short, and the covered, partial and unbacked bands). Display only: no balance, ledger or withdrawal reads this page.

A deposit page opened with a lower-case currency heard nothing

The deposit page subscribes with the currency from its link, the live feed delivers a deposit only to an identical subscription, and every deposit frame says XMR. A link ending ?currency=xmr showed nothing while the deposit confirmed.

  • Fixed the deposit route to state the currency as the customer's wallet spells it before a subscription is stored. This mends links and frontends that already exist, but only with Core 6.8.6, which calls it. It acts on a currency that contains a lower-case letter.
  • Fixed the deposit monitor and the background watch to use the wallet's currency, never the client's. A page opened as xmr used to arm a second monitor and a second watch entry.

The background scanner kept watching a disabled token

  • Fixed a background watch for a token an admin had since disabled. It scanned and failed ("Token not found") on every pass for as long as the entry lived, up to seven days after the customer last signed in, or 72 hours for Monero. The scanner now drops it, and the log says "Dropped watch …: no enabled ecosystem token any more". It is registered again the next time the owner signs in or opens the wallet, with the token enabled. Monero, which is off the background lane by default, needs the wallet or the deposit page opened.