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.
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):
- Claim. The amount plus the venue's listed withdrawal fee is held on the
house's Spot wallet, in the same transaction as the
PLANNEDrow. - 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. - The exchange completes it. Its
COMPLETEDsettles the hold: the part that went on-chain leaves the Spot ledger.IN_TRANSIT. - 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):
- 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.
- 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. - 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:
convertRebalanceEnabledis off,convertHedgePauseis 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.