Ecosystem 6.3.9
11 August 2026
This release has upgrade notes. Read them before updating — they describe behaviour changes that need your attention.
Ecosystem v6.3.9
Release Date: August 11, 2026 Tags: TRADING, FEES, SETTLEMENT, ORDERS, MATCHING-ENGINE, WALLETS, COPY-TRADING, MARKET-MAKER, KYC, PERFORMANCE, CHAINS, EXPLORERS, DEPOSITS, WITHDRAWALS, BITCOIN, UTXO, DATABASE, INDEXES, RELIABILITY, REPORTING, BUG-FIXES
Overview
Three bodies of work. The largest is a sweep through the trading engine's money paths: deleting a market refunded nothing and left every resting order's funds held, cancelling a copy-trading order released a different wallet's hold, and a single order that could never settle stopped every trade behind it from happening. The other two are older — every fill now records the fee it was actually charged, and the explorer a chain depends on is no longer a single point of failure.
A performance pass over order matching, deposit scanning and the UTXO chains changes no result: the same orders match, the same deposits credit, with far less work behind them. One new database index is built on the first start after you update, making that start slower.
If you have ever delisted an ecosystem market, look for customers with funds still held against it, and repair the stored fee and limit values on your tokens. Both are in Upgrade Notes, along with your markets' rates and your explorer keys.
Requires Core v6.6.3.
Update Instructions
pnpm updatorUpdate Core first — the trade panels that display these figures ship with it.
There is one database repair to run afterwards, and one thing to go and check if you have ever deleted a market. Both are in Upgrade Notes.
Highlights
The defects a reading of the routes could not reach
Most of the repairs below came out of an adversarial sweep of every money path in the product, route by route. That method found a great deal, and it has a blind spot: a route can be read on its own, an order book cannot. The worst defect in this release was in the matcher, where one resting order that could never settle quietly discarded whatever it was matched against — and it took a driver walking a customer from an empty wallet through a completed trade to see it at all. On the installation it was found on, 141 orders were sitting in that state.
That is why several entries below describe a symptom nobody reported. Funds held against a market that no longer exists, depth removed from a price level that belonged to somebody else, and a fill settled above the price its money was held at all share the same property: nothing errors, and the number on the screen looks ordinary.
Why the fee had to be recorded per fill
The matching engine recorded what a fill traded — amount, price, cost, side — but not what it cost in fees. One figure existed for the whole order, so a resting order that filled in five pieces gave the trade panel one number and five rows to spread it across. It spread it wrongly.
This closes the gap that made the wrong answer possible. On every fill recorded from this release onward the figure on the screen is the figure taken from the wallet rather than a reconstruction of it; fills already on record still have to be reconstructed, because the number was never stored.
The explorer stopped being a single point of failure
Etherscan cut the chain coverage of its free API tier on 22 November 2025. BSC, OP Mainnet, Base and Avalanche became paid-only, and Gnosis follows on 1 September 2026. Every one of those chains was configured to ask Etherscan first, so on a free key they opened by asking a provider certain to refuse them — and there was one global provider order, so the only way to fix one chain was to change the order for all of them.
Pulling on that thread found two features that had been broken for longer and more completely than the tier cut — the admin token-holders view, token ABI import, and Rootstock's transaction history are all in Fixed below, and none of them had ever been reported.
The replacement is deliberately unexciting. Each chain leads with a provider that is genuinely free for it. Every key variable accepts a list. Two providers need no credential at all and sit at the end of every chain's order, so an installation with nothing configured still resolves history on ETH, ARBITRUM, OPTIMISM, BASE, CELO and RSK on both of their networks, and on POLYGON mainnet. A custom chain added on Avalanche, Gnosis, zkSync or Scroll is covered too.
Upgrade Notes
Customers may have funds held against a market you delisted
Deleting an ecosystem market was meant to cancel every order resting on it and hand the held funds back. The sweep that does that was asked for the wrong thing, matched nothing, and reported success — so the market was force-deleted while every open order on it kept its funds held. Nothing errored, and the market disappeared from every list and every socket, so there was nothing left on screen to point at the money. The bulk delete was worse, because it loops: one call stranded the holds of every market in the batch.
- For each ecosystem market you have deleted, check the wallets of the customers who were trading it. An affected customer's balance is short by the amount that is still held in order.
- The orders themselves were never deleted, so they still appear in that customer's open orders and cancelling one releases its hold. That is the repair; there is nothing to run.
- Deletions from this release onward refund correctly, so this is a one-time check against markets you removed before updating.
Repair the stored fee and limit values on your ecosystem tokens
Three admin screens saved a token's fee and limit values with an extra layer of encoding around them, and every row in the table was written by one of the three. Nothing has been mischarged — the code that reads those values unwraps one extra layer, which is why this has been survivable — but that tolerance is exactly one layer deep. Re-importing a token whose values arrived as text could add a second, and at that point the withdrawal path reads them as no fee configured and charges nothing at all.
- The three writers are fixed, so anything you save from now on is stored correctly. The update does not repair the rows they already wrote.
- From your
backenddirectory, runnode scripts/migration-eco-token-json-unwrap.mjsfor a report of what would change, thennode scripts/migration-eco-token-json-unwrap.mjs --applyto make it. It refuses any value it cannot safely unwrap, a second run does nothing, and nothing needs restarting afterwards.
Confirm what your markets actually charge
Core v6.6.3 fixes an order ticket that displayed a fixed 0.100% fee on every market instead of reading the market's own rate. A market left on the default charges 1%. The engine has always charged the market's configured rate, so nothing was mischarged — but if you set, or left, a market's rate believing what the ticket showed, it is not what your customers have been paying.
- Open each ecosystem market and check its maker and taker rates. They are percentages:
1means 1%, not 0.01%.
KYC enforcement now reaches on-chain ecosystem withdrawals
If you enforce KYC features, the withdrawal permission bound on the spot and fiat withdrawal doors and not on this one. A level built the documented way — deposits and internal transfers granted, withdrawals withheld — could be walked around by moving funds into the ecosystem wallet and sending them off the platform from there.
- Users who lack the withdrawal feature will now be refused at this door as well, including when the destination address turns out to be another account on the same installation. If you were relying on ecosystem withdrawals staying open for an unverified tier, grant that tier the withdrawal feature before updating.
The first backend start after updating takes longer
This release adds one database index, to the ecosystem token list. It is created by the backend itself the first time it starts after the update, and on an installation with a large token table that start takes noticeably longer than usual while the table is read through once.
- Let that first start finish. Interrupting it leaves the index to be built again on the next attempt.
- There is nothing to run by hand and nothing to configure. Later starts are normal.
Five chains still need an explorer key
Every other chain in the built-in list now works with no key at all. These five have neither a hosted Blockscout instance nor Routescan coverage, so nothing keyless is appended to their order: BSC (56 and 97), FTM (250 and 4002), CRONOS (25), HECO (128 and 256) and Polygon's Amoy testnet (80002). Polygon mainnet is unaffected.
- If you run BSC, set
NODEREAL_API_KEY— it is free for BSC mainnet. On testnet, NodeReal and Ankr do not index chain 97 at all, so setMORALIS_API_KEYorCOVALENT_API_KEYinstead. - On CRONOS and HECO no provider indexes the chain, keyed or not. Deposit detection on those two falls back to scanning blocks over your own RPC, which only walks forward from now.
- Everywhere else, remove nothing and add nothing. An Etherscan key still buys richer data where its free tier reaches, and is simply skipped where it does not.
Rootstock no longer needs a workaround
Its transaction history failed on every call before this release, and read mainnet even when configured for testnet. If you pinned TRANSACTION_PROVIDERS_RSK or set an RSK explorer key to work around that, neither is needed now.
The keyless providers are rate limited per IP
Blockscout and Routescan serve without a credential, but their anonymous tier is throttled by IP address, and an installation polling many deposit addresses will meet that ceiling.
- If a chain served by one of them reports rate-limit failures, set
BLOCKSCOUT_API_KEYorROUTESCAN_API_KEY, or keep a keyed provider configured behind it. Neither provider stops working without one.
Added
The fee on each individual fill
- Added the charged fee to every fill the engine records, on both sides of the trade. A resting order that fills in five pieces now carries five fee figures, each the amount actually taken from that customer's wallet for that piece, rather than one figure for the order that something downstream has to divide up. Fills from before this release carry no fee of their own and are still reported by dividing the order's fee across them, which Core v6.6.3 now does correctly.
- Added the figure to the order's own record, visible to the customer who placed it. It is deliberately not published on the market's public trade feed, which every subscriber of a symbol receives — the fee a particular trader pays is theirs, and broadcasting it would leak which fee tier they are on.
Two explorers that need no API key
- Added Blockscout and Routescan as transaction-history providers. Both answer the same request format as Etherscan and neither asks for a credential, so they can serve a chain on an installation that has configured nothing.
- Added
<CHAIN>_BLOCKSCOUT_HOST, naming a self-hosted or long-tail instance for one chain. It is the only way to give a keyless provider to the five chains that have none.
A provider order chosen per chain
- Added
TRANSACTION_PROVIDERS_<CHAIN>, which pins the order for one chain and beats the globalTRANSACTION_PROVIDERS. Fixing a single chain no longer means changing the order every other chain uses. - Added
TRANSACTION_PROVIDERS_STRICT, which stops a keyless provider being appended to whatever order you pinned, for operators who would rather a chain fail than reach a provider they did not choose.
More than one key for a provider
- Added support for a comma-separated list in every provider key. A key that is rejected, out of quota, or on a plan that excludes the chain costs one attempt, and the next key in the list is tried before the provider is given up on.
- Added a chain-scoped name for every provider —
BSC_NODEREAL_API_KEYalongsideNODEREAL_API_KEY, and the same for the rest. The chain-scoped keys are tried first and the global key stays behind them as a spare, so one chain can have a paid plan without buying one for all of them.
Providers that fail are remembered
- Added a circuit breaker that skips a provider after it fails, for a period that reflects why: ten minutes for a rejected key, thirty for a plan that excludes the chain, an hour for a chain the provider does not index, a minute for a rate limit. A dead provider costs one request rather than every request. It can never be the sole cause of an outage — when every provider for a chain is cooling down, all of them are tried anyway.
- Added a quarantine on a key that comes back invalid or throttled, applied across every chain at once rather than being rediscovered chain by chain.
A time limit on every provider call
- Added
TRANSACTION_PROVIDER_TIMEOUT_MS, defaulting to twelve seconds. A provider that accepts a connection and then stalls used to hold the whole chain of alternatives open behind it. - Added
TRANSACTION_PROVIDER_LIMIT, defaulting to a thousand records — the ceiling Etherscan's free tier itself dropped to in July 2026.
Changed
What a fill is called in wallet history
An ecosystem fill is settled by consuming the funds reserved when the order was placed, and the ledger entry describing it said the seller "releases base" and the buyer "releases quote". Read on a transaction row, "release" sounds like funds being handed back — the opposite of what a fill does to them.
- Changed those entries to say the seller delivers and the buyer pays, each noting that it was settled from the reservation. The wording is all that changes; no figure in wallet history moves.
The average fill price on the order list
- Changed the order list to report an average fill price of 0 for an order that has not recorded one, which is what the live order stream already sends for the same order. The list answered with nothing at all, and a panel that merges the two sources had to reconcile them.
Which provider each chain asks first
- Changed the default order from one global list to a per-chain one, each leading with a provider that is actually free for that chain. Base and OP Mainnet lead with keyless Blockscout, BSC with NodeReal, Avalanche with Routescan, and the chains Etherscan's free tier still covers keep leading with Etherscan.
An unverified contract is no longer reported as a broken explorer
- Changed an unverified contract from an error into an ordinary answer: ABI import reports the contract as unverified, and the token-holders view returns an empty list. The repair to those two screens is in Fixed.
What the diagnostics console reports
- Changed the explorer check on Admin → Ecosystem → Blockchains → Requirements to report what the runtime will actually do: every provider in the order it will be tried, which one is serving the chain, where that order came from, how many keys each has, and which are being skipped by the circuit breaker.
- Changed the check to treat a working keyless provider as a pass. Having no API key stopped meaning "no transaction history" the moment a keyless provider joined every chain's order.
A chain with no key no longer asks a retired endpoint
With nothing configured, a chain used to fall through to the per-chain explorer host Etherscan retired, which answers with a deprecation notice rather than data. The attempt was spent, and the error that came back named neither the cause nor the fix.
- Changed that case to fail at once, naming the key to set, so the attempt goes to a provider that can actually serve the request.
Order matching does less work per cycle
- Changed the engine to save each matched order once per matching cycle rather than once for every price level it consumed. A market order that walked five levels was written five times, and every one of those writes carried the order's whole, growing list of fills — so the cost of recording a single order rose with the square of how far it swept. It is now written once, in its finished state, and the order update sent to that customer goes out once instead of once per level — every one of those copies already carried the same finished state. The order, the fills and the balances are the same either way.
- Changed the market-list ticker snapshot to be broadcast once per cycle instead of once for every symbol that traded in it. Each copy was byte-for-byte the same list, so a cycle that filled ten markets sent every connected trader ten identical frames. Per-symbol ticker and order book updates are unchanged, and a cycle in which nothing traded still broadcasts nothing.
- Changed cancelling many orders at once to sweep each market's queue one time, in a single pass, rather than searching the queue and shifting its contents for every order named. Cancel-all on a deep book was the case this cost the most, and it is the case that most often ran while the same market was matching.
Nudges from other processes are collapsed
When an order is placed in one process, the process holding the market tells itself to re-read that market. A market taking orders in bursts rang that bell once per order, and each ring ran a full resynchronisation of the market's window while holding the matching lock — so a busy minute spent most of itself re-reading the same book.
- Changed those nudges to collapse: a market already resynchronising records that it was asked again and runs one follow-up pass when it finishes, instead of queueing a pass per nudge. The marker that says a market needs attention is only cleared once a pass has actually covered the work it was raised for, so an order placed mid-pass is still picked up rather than stranded until the periodic sweep.
Token deposit scanning on MOCHAIN stops re-examining credited transfers
- Changed the background sweep that watches token deposit addresses on MOCHAIN to skip transfers it has already credited. That sweep re-reads the same window of recent blocks on every pass, so on an address with any history almost everything it looked at had been credited long ago and was fully re-processed only to be rejected as a duplicate at the last step. Which deposits credit, and for how much, is unchanged — the check asks exactly the question the crediting step already asks, so it can only skip work that would have been refused. If that check itself cannot be run, the sweep falls through to the crediting step rather than abandoning the rest of the pass. Deposit scanning on the other chains, and native deposits on MOCHAIN, are untouched.
UTXO chain lookups
- Changed the current block height to be reused for thirty seconds across the address, transaction and unspent-output queries that each fetched it separately, on Bitcoin and Litecoin. Blocks land minutes apart, so this roughly halves the calls made to the public explorer these two chains depend on — which is the one that rate-limits, and rate-limiting is what stalls deposit detection. A failed or nonsensical height is never stored, so a bad response cannot linger.
- Changed a confirmed transaction's confirmation count to be reported as at least one. A reused height can be a moment behind the block that has just confirmed a transaction, and without a floor that arithmetic would briefly report a confirmed transaction as having none.
UTXO withdrawals and consolidation
- Changed batch withdrawals and small-output consolidation on Bitcoin, Litecoin, Dogecoin and Dash to read the wallet records for the inputs they have locked in one query instead of one query per input, and to download a parent transaction once however many of the batch's inputs came from it. A consolidation runs on up to fifty inputs, which commonly share a handful of parents, so both were repeated work under a lock. The inputs chosen, the fee arithmetic and the broadcast order are untouched.
Token lookups are indexed
- Changed the ecosystem token list to carry an index on the currency it is looked up by — which is how the deposit-address path and every active-token check read it. Those lookups previously read the table through. The index is created on the first start after updating; see Upgrade Notes.
Fixed
Deleting a market left its orders funded, and still matching
Two halves of the same operation were wrong. The sweep meant to cancel and refund every order resting on the market was handed the base currency where the full market symbol was required; it matched nothing, which reads as an ordinary empty result rather than an error, so the refund loop ran zero times and the market was force-deleted anyway. Every resting order kept its funds held against a market that had ceased to exist. Separately, the matcher's copy of the book lives in memory and outlives the rows, so the deleted market's orders went on being offered for matching on every tick — and re-creating the same pair inherited the previous market's entire book, letting the first trade on a brand-new market fill against an order placed before that market existed.
- Fixed both delete routes to sweep the market they were actually asked to delete, so every resting order is cancelled and its funds returned before the market is removed. The bulk route could not even construct the market's name from what it had loaded, so a single call stranded the holds of every market in the batch.
- Fixed the matcher to drop a deleted market from memory at the same moment, on both routes. Money already held against a market you deleted before updating needs the check in Upgrade Notes.
Cancelling an order returned the funds to the wrong wallet
An ecosystem order can be funded from the ordinary trading wallet or from a copy-trading allocation, and the order records which one paid for it. Both cancel routes asked for the trading wallet regardless. Cancelling a copy-trading order therefore released funds from a wallet that had never held them — draining another order's hold if one happened to be sitting there — and left the real hold with no door to release it, with the order row gone and nothing remaining to point at the money.
- Fixed both routes to release back to the wallet the order was debited from. Cancel-all was the worse of the two because it loops: one call could strand every copy-trading hold a customer had.
- Applies to followers trading through someone else's strategy, which is where the second wallet type comes from.
A market buy could settle above the price its funds were held at
A buyer's payment is capped at what was locked when the order was placed, while the seller and the fee wallet are paid the full executed cost. Those two halves only add up while the fill price stays at or under the price the hold was sized at. A limit order can never breach that. A market order can: its hold is priced from the book as it stood at placement, its unfilled remainder rests in the queue, and the matcher will cross a market remainder against anything at any price — so a later, dearer ask could be paid more than the buyer was ever charged, and the difference was created out of nothing. The neighbouring balance check could not catch it, because it reads the customer's total across all their open orders and passes on another order's money.
- Fixed the engine to refuse a fill the buyer's hold does not cover and carry on matching everything else, rather than settling one and inventing the shortfall.
One account could be both sides of a trade
The matcher crossed any two orders that met on price without ever comparing the parties. One account could therefore trade with itself: wash volume printed on the public tape, a price nobody actually paid becoming the last price the chart, the candles and every stop trigger read, and a fee charged for trading with yourself. A market maker running several bots was the same thing at a larger size — its bots carry different identities, so two of them could cross, settle nothing at all between them, and still print the price and the volume.
- Fixed the matcher to skip a fill whose two sides are the same party, and to treat every bot of one market maker as one party. The order is not refused, only the crossing fill, so the aggressor goes on looking down the book for a real counterparty.
- Deliberately not enforced at placement instead: an earlier attempt refused a market order outright whenever the caller held any resting order on the other side, which is exactly the shape a grid bot has by construction — its stop-loss exit was refused outright.
One order that could never settle stopped every trade behind it
The engine walks the two sides of the book as a merge, and moving past an order discards it for the rest of that pass. That is only sound when the order genuinely cannot match anything further — which is true for exactly one reason, that the prices do not cross. On a settlement failure it discarded both sides, so a single resting order whose settlement can never succeed took an innocent counterparty down with it. The queue is unchanged next pass, so the same pair failed the same way and the same customer was discarded again, indefinitely. What a customer saw was a book advertising depth, an order placed at a price that crossed it, and nothing happening — on a market that was otherwise healthy and trading.
- Fixed it to retire only the order at fault. The settlement layer names which side failed, and where it genuinely cannot be attributed both are retired as before.
- Fixed a reference to a market maker's pool that no longer exists to be treated as permanent rather than retried every pass, since the pool is gone and no number of attempts will find it. That is one of the two ways an order reaches this state without anyone doing anything wrong; the other is an order whose funds are no longer there.
A cancel could remove depth belonging to other traders
Each price level on the book is one shared figure holding every participant's open size at that price, so a cancel may only ever subtract what that one order still has resting — every fill has already taken its own bite out of the same level. The cancel subtracted whatever the caller said instead. The market maker's figure was its order's original size, because the count of what had been filled was never written to during a run, so every fill a customer took out of a maker's quote was subtracted a second time on the way out. That came out of other people's liquidity, and deleted the level outright when it reached zero — depth vanishing from the chart while the orders behind it were still funded and still matchable.
- Fixed the cancel to take the size from the order's own record rather than from the caller whenever the caller asks for more than the order still has resting. An over-ask no larger than a single rounding step is deliberately left as it is, because every correct cancel over-asks by that much and it is what lets a fully cancelled price level disappear — cutting it back would leave the level standing with dust in it, which is the ladder of tiny levels at round prices this repair exists to prevent.
- Fixed the engine to tell a running market maker when a customer consumes one of its quotes, so its own account of what it has resting stops drifting from the ledger in the first place.
Order figures arrived from the ledger as the wrong kind of value
Seven of an order's numeric fields are stored in a form the database driver hands back as an object, while everything that reads an order had been told they were plain numbers. Arithmetic against one of those objects throws, and a stored zero tests as present rather than empty. The result was a cancel raised from outside the trading engine that threw before it wrote anything, with the failure swallowed and reported as an ordinary refusal while the order stayed resting.
- Fixed those fields to be converted once, where an order is read, so every part of the engine receives the value it was promised.
On-chain withdrawals ignored the KYC withdrawal permission
Two of the three withdrawal doors check whether the customer's verification level permits withdrawals. This one, the on-chain door, checked nothing. A level built the documented way — deposits and internal transfers allowed, withdrawals withheld — was defeated by moving funds into the ecosystem wallet and calling this route, and the funds left the platform entirely from the one door that never looked.
- Fixed the route to apply the same check as the other two, before any balance is touched, and to apply it to a transfer to another account on the same installation as well.
The Super Admin's trading fee was charged and then destroyed
Where the Super Admin account was one side of a trade, its share of the fee was excluded from the platform's recorded profit as a circular no-op. It was not circular: only the credit was ever skipped. The charge is unconditional and had already happened, so the quote left the account's wallet on every trade it took part in and was credited nowhere — the market's total quote shrank by exactly that fee, and the platform's profit record was short by it.
- Fixed the accounting to record what was actually charged. A bot's side is still excluded, because a bot is never charged a fee to begin with. Waiving the Super Admin's fee is a different decision and belongs where the fee is quoted, not after the money has moved.
Token fees and limits were stored wrapped in an extra layer
The three admin screens that write a token's fee and limit values handed them over already encoded, so they were encoded a second time on the way into the database — every row in the table. Nothing was mischarged, because the code that reads them tolerates one extra layer, and withdrawals have been charging the configured fee throughout. But the import screen could add a third layer by re-importing a token whose values arrived as text, and at three layers the value reads as absent, which the withdrawal path treats as no fee configured and charges nothing.
- Fixed all three writers to store the values as they are meant to be stored, and the import to read an incoming text value into its proper shape instead of passing it along untouched. Existing rows need the repair in Upgrade Notes.
Two admin custodial transfer screens refused every request
Both the native and token custodial transfer routes demanded, in the body of the request, a value that is part of the address of the route itself and can never appear there. Every request was refused before the handler ran, so custodial native and token transfers did not work at all from the admin interface.
- Fixed both to require only what a caller can actually send.
A rejected order placement was reported as a server fault
When the funds to cover an order are not there at the moment the hold is taken — ordinary under any concurrency, with several orders placed at once or a fill landing in between — the failure carries the available amount, the required amount and the currency. All of that was thrown away and replaced with a blanket server error, so a funding problem read as the platform being broken. A market maker saw nothing but the opaque message.
- Fixed placement to report the actual reason and the actual figures. Genuinely unexpected failures keep the server error, and the order is still rolled back either way.
A cancel that could never succeed was retried forever
The queue that carries cancellations between processes retried a failed request unconditionally, on a timer, for the life of the process. A request that fails for a permanent reason therefore never stopped — and each attempt pulled the order out of the matching queue for the engine to put back, so the pair produced two log lines per order per tick and buried everything else in the log.
- Fixed it to abandon a cancel after five consecutive failures and say so in one line that names the order and states that it is still open and still holding its funds. A brief lock timeout or database hiccup sits well inside that allowance and still retries, and a restart is a fresh start.
A request for one kind of wallet could be answered with another
The lookup that fetches a customer's wallet for a currency can only ever create the ordinary trading kind. Asked for any other kind that did not already exist, it created a trading wallet and returned it as though it were the kind requested — so the caller credited or debited an entirely different balance and left a stray wallet behind. A refund from a futures liquidation would have paid the customer into a balance they were not trading from.
- Fixed it to return nothing rather than the wrong wallet when it cannot make the kind being asked for. Every caller of another kind already copes with an empty answer — copy trading creates the wallet itself with the correct kind, and futures checks before using it — so nothing that worked before stops working.
Token holders and ABI import worked on Ethereum and nowhere else
Both built their requests against the per-chain explorer hosts Etherscan retired when it consolidated onto its V2 API. Those hosts no longer return data, so on every chain but Ethereum the token-holders view showed nothing and ABI import could not read a contract. Both also refused to run without an API key, on chains where none was needed.
- Fixed both to query a live endpoint, with the same per-chain order and failover as transaction history. On a chain with a keyless provider they now work with nothing configured.
Rootstock transaction history failed on every call
Rootstock did not go through the provider system at all. It called an endpoint whose response shape the transaction parser rejects, so every request ended in an error rather than a list of transactions — and the endpoint was hardcoded to mainnet, so an installation configured for testnet was reading the wrong network.
- Fixed Rootstock to use the ordinary provider path, which resolves the right host from the chain id — mainnet and testnet each reaching their own.
Half a result could go missing
Transaction history from NodeReal is assembled from two requests, one for incoming transfers and one for outgoing. Only the first was checked for an error, so when the second was rejected — a throttle, an expired key — it contributed nothing and the result was silently half a history.
- Fixed the check to cover both, so a partial answer is a failure that fails over rather than an answer that looks complete.