MMashDiv

House movements between the exchange and the chains

Why the house's inventory drifts between its Ecosystem side and its exchange side, the two movement directions and their lifecycle, the bands that trigger a movement, starting one by hand, resolving one in review, and the canary to run on each venue before you rely on them.

7 min readUpdated 1 October 2026admin, movements, rebalance, exchange, custody, bands, canary

Admin → Convert → Movements (/admin/convert/rebalance). Viewing needs view.convert.rebalance; starting a movement by hand needs edit.convert.rebalance; resolving one in review needs manage.convert.rebalance, which no role holds until you grant it.

Ecosystem converts take coins in on-chain: into the pool, or to the house's own addresses. Hedges trade on the exchange. So a house that hedges its Ecosystem position ends up long coins on-chain and short them on the exchange, or the reverse, and the coins have to be moved. A house movement moves the house's own coins between its exchange account and its on-chain custody. It never moves a customer's balance, never involves the pool-backing treasury, and writes no pool-backing obligation rows.

Movements are off by default (convertRebalanceEnabled). The switch is on the core house page, with the Super Admin role and a fresh two-factor code.

The two directions

Exchange → chain (TO_CHAIN):

  1. Claim. The amount plus the venue's listed withdrawal fee is held on the house's Spot wallet, in the same transaction as the PLANNED row.
  2. Dispatch. The exchange is asked to withdraw to the house's own deposit address on the chosen chain. An ambiguous answer is never re-issued: the withdrawal is adopted by address and amount, or goes to a person. SUBMITTED.
  3. The exchange completes it. Its COMPLETED settles the hold: the part that went on-chain leaves the Spot ledger. IN_TRANSIT.
  4. The coins arrive. The deposit scanner's credit on the house's Ecosystem wallet is the arrival, and the only credit of the Ecosystem side; nothing pre-credits it, so it is credited exactly once. The venue's fee comes out of the rest of the hold, the remainder is released. COMPLETED.

Chain → exchange (TO_EXCHANGE):

  1. Claim. The house's Ecosystem ledger is debited. A currency held at the house's own address also reserves the coins on its chain, so Sends cannot spend them meanwhile.
  2. Dispatch. The ecosystem addon signs from the house: from its own address for native coins, SOL, SPL, and tokens it holds there; from the pool, in the order a customer settlement uses, for UTXO coins, EVM tokens, TRC-20 and TRX. Every transaction is recorded before it is broadcast. IN_TRANSIT.
  3. The exchange credits it. When the venue lists the deposit as accepted, the house's Spot wallet is credited the amount the venue lists, minus the deposit fee it lists. An amount or fee the venue does not state sends the movement to review rather than guessing. COMPLETED.

A movement that fails puts back its own wallet leg (the entry it made on the house's wallet: the hold released, or the Ecosystem debit credited back) in the failing transaction. Each cost is recorded on the movement with who paid it. Costs the house wallets bore (venue fees, what a venue kept inside the amount, gas the house's own key burned, a write-off) are house costs in the Profit & loss, with no admin_profit row. Gas and top-ups the master wallet paid are also written as one record-only CONVERT_HEDGE row each. The Super Admin is never charged.

Bands: when a movement is planned

Each side of each currency has a band from its asset rows: it wants to hold at least max(target, floor). Every five minutes the cycle plans every currency and writes the plan the Plan tab shows, even while movements are off. It moves when:

Trigger Direction
The Spot side is below its floor chain → exchange
The Ecosystem side is below its floor exchange → chain
The hedger needs more of the currency on the exchange than the house holds there chain → exchange
A side is under its target by at least the hedge threshold's worth in USD towards that side

For a currency paid from house addresses the Ecosystem band is per chain: the Send chain rows' floor and target, and an exchange-to-chain movement goes to the chain with the lowest available-to-target ratio. A movement never takes the giving side below its own band, and is not planned when the venue's withdrawal fee is more than 2% of the amount.

What stops a movement

New movements stop, and movements already started carry on, when any of these holds:

  • convertRebalanceEnabled is off, convertHedgePause is on, or pool-backing's own global pause is on (read fresh; an unreadable pause counts as on);
  • the addon is disabled, unlicensed or draining;
  • the exchange is banned or Convert's breaker is open;
  • the core is below Convert level 3;
  • the Ecosystem gates do not hold.

And per currency:

  • it lacks a Spot or an Ecosystem asset row, or, for any currency but USDT, Hedging is off on either (a movement would lower the hedged book while the coins land in an unhedged one);
  • it is frozen, paused or closed for quoting, or has a hedge in review;
  • pool-backing reports unacknowledged drift on it;
  • a movement of it is already in flight (one per currency at a time);
  • the venue cannot list its own withdrawals (exchange to chain) or deposits (chain to exchange), because the movement could then never be followed;
  • the venue's deposit address for it needs a memo or tag: a house send carries none, so it is refused at planning.

poolBackingMode and poolBackingCapUsd do not gate movements.

Starting one by hand

Start a movement on the Plan tab opens a dialog that picks the currency (the Ecosystem currencies Instant Convert knows, or Another currency… to type one), the direction (Exchange → chain or Chain → exchange), the amount and, for a currency on several chains, the chain, with Let the planner choose first. Under the amount, the side it leaves says what it holds above its floor (On the exchange (Spot) above its floor: …), and Max fills that. The button stays unavailable until the plan has been read and the gates allow a movement. Every switch, band floor and exchange limit of the automatic cycle still applies; a refused plan answers with its reason and moves nothing.

The review queue and its decisions

A separate job follows every movement in flight once a minute, and it keeps running while the addon is disabled or unlicensed, so a movement already started is never left half-booked. Besides booking each stage, it settles what it can prove by itself: a claim that was never sent is marked failed after 10 minutes and what it took is put back, a withdrawal the exchange lists as failed or cancelled is marked failed and its hold released, and a chain-to-exchange dispatch that stopped before signing anything is marked failed after 30 minutes.

Only what it cannot prove goes to review: an exchange request with an ambiguous answer that nothing adopted within 30 minutes, a broadcast hash the chain has not seen for 30 minutes, a credit the venue does not state, or anything still unproven after 72 hours. Its money stays where it is until a person decides. The Needs review tab carries the count, and from 6.0.1 the dashboard's Needs attention card lists it too.

Decision For What you enter
The exchange sent it exchange → chain its transaction ID from the venue's withdrawal history; the fee is optional
It arrived either exchange → chain: the scanner must already have credited the house's deposit of that transaction (rescan the house addresses in Inventory first). Chain → exchange: the amount the exchange credited and its deposit fee, exactly as its deposit history shows them; a credit of zero writes the movement off and needs a tick to confirm
Attach the transaction ID exchange → chain the ID the exchange published late; the arrival is followed from there
Look it up again exchange → chain nothing: the venue is searched again for the one withdrawal this request made, and nothing changes unless exactly one matches
Mark failed either a reason of at least ten characters, and proof: the exchange's own failure or cancellation, the chain's proof that the send dropped, or, when nothing can prove more, the movement's ID typed back. Refused once the exchange completed it or the chain moved its coins

These decisions are also on the core house page, worded Confirm the exchange sent it, Confirm it arrived and Attach a transaction ID there.

Run a canary first

While an exchange-to-chain movement is in flight, the pool-backing reconciliation must know whether the venue has already taken the coins out of the account total. Venues differ: some take a pending withdrawal out at submission, others only when it completes. This build assumes Binance takes it at submission and KuCoin and OKX only at completion, and treats any other venue like KuCoin. That table has not yet been proven against live accounts.

On each venue you use, before you rely on movements: switch movements on with a small band, start one small exchange-to-chain and one small chain-to-exchange movement by hand, and watch Finance → Transaction Management → Pool Backing through every stage. A residual that appears while the movement is in flight and clears when it completes means the venue's timing differs from the table; stop, and report it before running larger movements.