Theme
The workers: settlement, TON and commissions
Three pieces of the API do the chain work: the settlement worker on BNB Smart Chain, the TON worker on TON, and the commission payouts each of them carries for its chain. All run from housekeeping under a lease, one instance at a time, so the operator's transactions share one nonce (EVM) or seqno (TON) sequence.
The settlement worker (BNB Smart Chain)
packages/data/src/exchange/worker.ts, runSettlements, every pass and right after an order trades. Lease exchange:operator, two minutes. Each run, in order:
- Retire unsigned orders. Resting orders placed before trading from wallets carry no signature, so the chain could never settle them: they are cancelled and their collateral released. In practice this happens once, at the switch.
- Confirm what was sent (
settlementsrows withstatus = submitted, by nonce). For each: a successful receipt →confirmed; a reverted one → failed and undone (below); no receipt after 10 minutes and the nonce used by another transaction on every RPC → undone as lost; otherwise, not mined after 60 s × (attempts + 1) → the same signed bytes are rebroadcast. - Send what waits (
status = pending, oldest first, 20 per run). Each match is simulated (eth_call) first. A match the chain would refuse waits if an earlier match of the market is still in flight (it may depend on it: shares bought there, sold here); alone, it is failed and undone. OtherwisematchOrdersis signed with the next nonce and sent; the row becomessubmittedwith its hash, nonce and raw transaction. A node refusing to accept a transaction ends the run; the next one retries with a fresh nonce. - Open chain markets. Every active market gets a
chain_marketsrow and its condition prepared on CTF (prepareCondition, by the operator), 20 per run; once the chain shows it, the row isopenwith the two token ids. A prepare reverted or never mined is retried; after five attempts the row isfailedand left for staff. - Report results. For each
chain_resultsrow stillpending: once every settlement of the market is confirmed or failed, the resolver'sreportPayoutsis sent (or, when the resolver is a Safe, the row waits with "Waiting for the resolver (a Safe) to report the result"). A result already on chain that disagrees with Jua's books stops withlastError"CTF holds X, Jua's books Y: staff must look". A market that never opened on chain is marked resolved with nothing to report. - Pay commissions (below).
Undoing a refused match
When the chain refuses a match (a wallet no longer holds, or no longer allows, what its order needs), the match is taken back out of Jua's books, newest fill first: positions, orders (the culprit's order and the taker's are cancelled; other resting orders are back on the book), trades, fees and commissions. Later matches of the same market that involve the same people are undone first; one already on its way to the chain is waited for. Everyone involved is told: "A trade could not be settled". Nothing ever left a wallet.
What to watch
| Where | Healthy | Look when |
|---|---|---|
settlements (evm) | pending and submitted rows are seconds to a minute old | A row is pending/submitted for over 10 minutes, or attempts climbs, or last_error is set |
chain_markets | New markets open within a minute or two | A row is failed, or pending/submitted with last_error for long |
chain_results | pending rows resolve within minutes of the result | last_error mentions a disagreement, or a Safe resolver has not reported |
| The operator's BNB | Above 0.1 BNB | Below 0.05 BNB: nothing is sent. Top it up (Recurring tasks) |
See Monitoring for queries.
The TON worker
packages/data/src/ton-exchange/worker.ts, runTonExchange, every five seconds while TON trading is on (and every housekeeping pass, so it never stops). Lease ton-exchange:operator. Each run:
- Watch. Reads JuaTonExchange's transactions after the cursor (
ton_cursor), 100 at a time, up to 10 pages, from both indexers, which must agree on every one. Each transaction is brought into the books in order:- a customer's order the exchange accepted enters the books for the account that linked the sending wallet; one that cannot trade (no linked wallet, no such market, a fee other than the platform's, too little or unsettled collateral) is closed on the exchange at once, so all of it goes back;
- a customer's cancel, or an expiry, ends the order in the books;
- the exchange's answer to every match, close, open-market and record-result message Jua sent, including a refused fill, which is taken back out of the books (the same
undoFillsTxas the EVM); - a stranded transfer (USDT or shares with too little TON to be returned), a bounced message: a durable alert each. A transaction that cannot be handled is retried; after repeated failures the cursor stops on it and the alert "TON trading is stuck on one of the exchange's transactions" is recorded, because nothing after it is read.
- Settle messages. Each signed message Jua sent (
ton_messages) is looked up on chain; applied, failed or lost, and what it carried is updated. A message applied over ten minutes ago whose exchange answers never arrived has its settlements, refunds and market openings put back topendingand sent again. - Queue new markets. Every open market gets its two share minters on the exchange, once.
- Confirm commissions. Each TON commission payout is read from the vault's payout state (
sent,delivered,returned,refused); onesentfor over an hour raises "A referral commission payout on TON is not confirmed". - Send the operator's batch: at most 60 internal messages in one signed message from the operator wallet, one in flight at a time: matches, closes of immediate-or-cancel orders' rests and of cancelled orders, refunds of orders that never entered the books, new markets' minters, commission payouts out of the fee vault, and a ClaimFees to move the exchange's fees into the vault. Before sending, the wallet's TON is checked against what the messages carry plus a 1 TON reserve; short, the alert "The TON operator wallet is short of TON" is recorded and nothing is sent.
- Send the resolver's batch: results (
RecordResult) for finalised markets whose TON trades have all settled, when this server holds the resolver key. - The market maker (optional): re-quotes Jua's own bids on the busiest TON markets at most every three minutes.
What to watch
| Where | Healthy | Look when |
|---|---|---|
ton_cursor | Advances; failures = 0 | failures climbs or last_error is set: the stuck alert |
settlements (ton), ton_refunds | pending/submitted for seconds | Rows older than a few minutes, or last_error "the exchange never answered: sent again" repeating |
ton_messages | Applied within seconds | Messages not applied for minutes: the operator wallet's TON, or the indexers |
ton_markets | open within a minute of a market opening | Stuck pending/submitted |
| The operator wallet's TON | Several TON above the 1 TON reserve | The short-of-TON alert |
Commission payouts
packages/data/src/exchange/commissions.ts. Jua's books split every fee with up to three referrers (affiliate_commissions) as the trade settles. A referrer's Collect (POST /api/me/referrals/collect, venueevm or ton) creates one commission_payouts row for what their settled trades on that chain earned and they have not collected: at least $1, at most the vault's per-payout limit and what the vault holds, within its daily limit, one collection at a time per chain, to their payout wallet (EVM) or the TON wallet connected last.
- EVM: the settlement worker's last step.
submittedpayouts are confirmed from receipts (reverted →failed; lost →failed; not mined → rebroadcast);pendingones are sent asJuaVault.payout(to, amount, id)with the operator's next nonce, 20 per run. The vault pays an id once, however often its transaction is sent; a payout that fails returns its amount to what the referrer can collect. - TON: part of the operator's batch, as
VaultPayoutto JuaTonVault; confirmed from the vault's payout state.
Without a fee vault configured (EXCHANGE_FEE_VAULT, TON_EXCHANGE_FEE_VAULT) no commission is recorded on that chain's trades and every fee is Jua's.
What to watch
| Where | Healthy | Look when |
|---|---|---|
commission_payouts | pending → submitted → confirmed within minutes | failed rows (the referrer can collect again; find why: vault paused, limits, gas), or submitted for over an hour |
| The fee vaults | Balance ≥ what referrers can collect | A sweep took too much: collections fail with "Commissions cannot be paid out right now" (Recurring tasks) |