Retiring a token, market or chain
What the delete buttons under Admin - Ecosystem actually do, which ones are permanent, which have no guard at all, and the reversible way to take an asset out of service instead.
Adding a market is reversible. Removing one is, in this product, the single most destructive action an admin can take from a data table — and the delete button looks exactly like every other delete button in the panel.
Deleting a market is permanent. The route forces a hard delete regardless of what the UI asked for, so the ordinary "Delete" row action removes the row outright. There is no restore, and the market table's "show deleted" toggle will never surface it.
Deleting a token has no guard whatsoever. No balance check, no market check, no address check. Customers can be holding the asset, markets can be quoting it, and the delete still succeeds.
Neither action moves, refunds or reconciles a single unit of customer money.
Prefer status over delete
Almost every retirement an operator actually wants is a status change. It is reversible, it is enforced in the code paths that matter, and it leaves the configuration intact so you can put the asset back.
| Action | Where | What it stops | What it leaves |
|---|---|---|---|
| Disable a market | Markets table, status toggle | The pair disappears from /ecosystem and its market WebSocket refuses new subscriptions |
Configuration, candles, and every resting order |
| Disable a token | Tokens table, status toggle | Deposits and withdrawals 404, the token cannot back a new market, no new addresses are derived for it | Balances, existing addresses, existing markets |
| Disable a custom EVM chain | Custom EVM Chains, status toggle | Removes the chain from the live registry on reload | The chain row, its master wallet, customer addresses and balances |
| Disable a licensed chain | Blockchains, status toggle | The chain's flows stop | Everything else |
Disabling a token is the strongest reversible lever in the addon. The withdrawal
handler resolves an asset with status: true and nothing else, so a disabled
token makes every withdrawal on that chain and currency answer
Token not found for chain: <CHAIN> and currency: <CURRENCY>. The same lookup
gates deposit-address issuance. It is the real off switch.
A disabled market keeps its whole book. Users cannot reach the pair to cancel
their own orders, and the funds stay held in inOrder until somebody cancels
them. If you are pausing a pair for an hour that is fine. If you are retiring it,
cancel the book first — see the ordered procedure at the end of this page.
Deleting a market
The handler overrides whatever the caller asked for with force: true, so
ecosystem_market is destroyed with force even though the model is paranoid.
The row does not come back.
The one guard
Before deleting, the handler counts copyTradingTrade rows on the market's
symbol whose status is PENDING, OPEN or PARTIALLY_FILLED. If there are
any, the request fails with 400:
{ "message": "Cannot delete market with 3 open copy trading trades. Please close or cancel all copy trades first." }That is the only thing standing between the click and the deletion. Notably it is a copy-trading guard, not a market guard — ordinary resting orders on the symbol do not block it.
After a successful delete, follower allocations and leader markets for the symbol
are set isActive: false. If the Copy Trading addon is not installed, both the
check and the cleanup are skipped silently.
What the market-data cleanup is meant to do
The delete calls deleteAllMarketData, which for a given symbol is designed to:
- read every order on the symbol and pass each one through
cancelAndRefundOrder— which refunds anOPENorder's remaining hold to the wallet type it was funded from (quote currency for a BUY, base for a SELL); - delete those orders from
ordersand their mirrors fromopen_orders_by_market, which is the table the matcher actually loads from at boot; - delete every candle for the symbol;
- delete every aggregated order-book level for the symbol.
All of that is issued in unlogged chunks of 100 statements, because a batch proportional to a market's whole history gets rejected on size — and a rejected batch deletes nothing and only logs.
The single-market handler builds the full BASE/QUOTE symbol for the
copy-trading checks, but passes only the market's base currency to
deleteAllMarketData. Every query inside that function matches on the full
symbol, so BTC finds no orders, no candles and no book levels for BTC/USDT.
The practical result: the market row is destroyed, and the orders, their held
funds, the candles and the book levels are all left behind. Nothing refunds
those holds, and open_orders_by_market still carries the book — so the orders
are reloaded by the matcher at the next boot, on a market that no longer exists.
Do not rely on the delete to release customers' money. Cancel the book yourself
first, and confirm inOrder is back to zero for the affected wallets before you
delete anything.
Deleting a token
There is no guard of any kind on this route. It passes the request straight to the generic delete helper. Specifically, it does not check whether:
- any customer holds a balance in the currency;
- any market is built on the token;
- any wallet carries a deposit address derived for its chain;
- any withdrawal is in flight against it.
ecosystem_token is paranoid, so the ordinary "Delete" row action is a soft
delete (deletedAt is stamped) and "Permanent delete" sends force=true and
destroys the row.
The tokens screen is rendered with paranoid mode off, so the "show deleted"
toggle is not offered on it. A soft-deleted token vanishes from the list with no
UI path back. Recovering it means clearing deletedAt in the database by hand.
What breaks the moment the row is gone, soft or hard:
- Deposits and withdrawals for that chain and currency answer 404 — the lookup only sees live, enabled rows.
- Address generation skips the chain, so new wallets get no address for it.
- Balances stay exactly where they are, in MySQL and on-chain, with no way for the customer to move them.
- A market built on the token is left pointing at an asset that no longer resolves.
If your intent is "stop offering this asset", set its status to false. Delete is for a token that was created in error and never held anything.
The bulk endpoints
The bulk market delete is riskier than the single one in a specific way: it forces the same permanent delete, but it carries no copy-trading guard at all. The one check that exists on the single-row path is simply not there. Selecting rows with a checkbox and using the bulk action bypasses it.
Both bulk routes are reachable from the data table's selection toolbar. Treat the selection checkbox on these two screens as a loaded action, not a filter.
Retiring a chain
A chain is not one record. Retiring one means deciding what happens to the tokens on it, the master wallet that signs for it, and the customer addresses already derived on it.
Disabling the master wallet is not a kill switch
Admin → Ecosystem → Wallets → Master Wallets has a status toggle. Turning it off does two real things:
- the chain diagnostics treat a disabled master wallet exactly like a missing one
and downgrade the chain's withdrawal readiness to failed, with the reason
Signing prerequisites missing: master wallet disabled; - address derivation for
PERMITtokens on that chain stops — the generator logsSkipping chain <CHAIN> - Master wallet not found or not enabledand issues no address, so new customers get no deposit address there.
What it does not do is stop signing. The gas-payer lookup used by the EVM
withdrawal path fetches the master wallet by chain and never inspects its
status, so a queued withdrawal on a disabled chain will still be signed and
broadcast. If you need signing to stop, disable the chain's tokens — that is
the check the withdrawal endpoint actually performs.
There is no delete route for a master wallet, and the screen offers no delete
action. That is deliberate: the master wallet's HD material is what every
PERMIT deposit address on the chain was derived from, and the encrypted blob in
the database is the only copy.
Customer addresses survive everything
Disabling a token, a chain or a master wallet does not touch a single
wallet.address entry or wallet_data row. The addresses stay valid on-chain,
the keys stay in the database, and coins sent to them after you retire the chain
still arrive — they just will not be detected or credited, because nothing is
watching any more. Tell customers before you retire a chain, not after.
Deleting a custom EVM chain
This is the one delete in the addon with real custody guards, and both refuse with 400:
- A master wallet exists for the chain —
Cannot delete '<CHAIN>': a master wallet exists for this chain. Remove it first.Since there is no way to remove a master wallet from the panel, in practice a chain that ever had one cannot be deleted without a database edit. Disable it instead. - Any ECO wallet holds a positive balance in the chain's native currency —
Cannot delete '<CHAIN>': users still hold <CURRENCY> balances. Disable the chain instead, or have balances withdrawn first.
Past both guards it is a force delete followed by a live registry reload, so the chain disappears from the running process immediately.
Licensed chains (Solana, Tron, TON, Monero) have no delete at all — they are seeded rows, and the status toggle is the whole surface.
A retirement order that works
For a pair you are taking out of service permanently:
-
Announce it. Customers with resting orders and on-chain balances need notice; nothing in the following steps notifies them.
-
Disable the market. Admin → Ecosystem → Trading → Markets, status toggle. New orders stop immediately.
-
Clear the book. Cancel every resting order on the symbol and confirm the holds came back — the affected wallets'
inOrdershould return to zero. Do not skip this: nothing later in the sequence does it for you. -
Let customers withdraw. Leave the tokens enabled and the chain running for as long as you told them they had.
-
Disable the tokens. Deposits and withdrawals for that chain and currency now refuse. This is the point of no return for customers.
-
Only then consider deleting, and only if the market and token genuinely hold nothing. Take a MySQL dump and a ScyllaDB snapshot first — the market delete is permanent and Scylla has no backup path in the product.
Permissions
| Action | Key |
|---|---|
| Delete a market, single or bulk | delete.ecosystem.market |
| Delete a token, single or bulk | delete.ecosystem.token |
| Enable or disable a market | edit.ecosystem.market |
| Enable or disable a token | edit.ecosystem.token |
| Delete or disable a custom EVM chain | edit.ecosystem.blockchain |
delete.ecosystem.market and delete.ecosystem.token are the two keys on this
addon that no support role should hold. Grant them to the same people who hold
your database credentials, and nobody else.
Related
- Tokens and markets — creating what this page removes
- Master wallets and the vault — why the master wallet cannot be replaced
- Admin console — every screen named here
- Backups — what to take before a permanent delete