UpgradesSeptember 14, 2026by
EmpoorioChain Core
EmpoorioChain Core

Postmortem: The DEX Treasury Account That Never Existed

Summary

pallet-dex was in the runtime, compiled, passed its tests and appeared in the pallet reference as functional. On the live public testnet, no swap had ever settled. The cause was a single account that did not exist. Fixed on 14 September 2026 in runtime 220; the first real swap followed the same day.

Background

In Substrate, an account exists only once its balance is at least the existential deposit (0.01 DMS on EmpoorioChain). Below that, the account is not in state at all. A transfer into a non-existent account creates it — if the amount clears the deposit. A pallet that tries to credit fees to an account through a path that does not create accounts simply fails.

What happened

The DEX collects a share of each swap's fee into a treasury account derived from a pallet id (emp/dext). That account had never been funded. Every dex.swap on the live chain executed the pricing, computed the fee, attempted to move the fee share to the treasury — and the extrinsic failed. The pool reserves never changed. EmpooScan, whose indexer was itself intermittently down, showed no swaps; nobody asked why a DEX on a testnet had no volume.

Unit tests passed because the mock runtime's genesis funded every account the tests touched, including the treasury.

Detection

The Eoonia wallet audit reached its DEX verification item (F1-11) on 14 September and attempted a real swap against the live chain. It failed with a balance error on an account nobody had heard of.

Fix

Runtime 220 reorganised the DEX treasury per pool and, as a one-time operational step, one DMS was sent from the sudo account to create the treasury account. The next swap settled. Eoonia's swap flow was then verified end to end.

Lessons

  1. Nothing is free. An account that must receive fees must be created — by genesis, by a migration, or by a funded transfer. "The pallet will create it" was an assumption, not a behaviour.
  2. Mock genesis hides existential-deposit bugs. Every test runtime funds the accounts it needs. The live chain funds only what someone actually paid for.
  3. "Wired" and "exercised" are different columns. This finding is why the ecosystem's status pages now distinguish in the runtime from has carried a real transaction, pallet by pallet.
  4. A DEX with zero volume on a testnet is a symptom, not a fact about demand.

The same day's audit found the treasury spending window had been mis-set (see the treasury postmortem) and that the AI reward pool had never paid because no operator had been authorised. Three treasuries, three ways of holding money nobody could move. The pattern is now a checklist item: for every account a pallet pays into, show the transaction that created it.

Based on the runtime 220 notes and the Eoonia audit block 13 (2026-09-14).

Share this article