Skip to content

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:

  1. 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.
  2. Confirm what was sent (settlements rows with status = 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.
  3. 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. Otherwise matchOrders is signed with the next nonce and sent; the row becomes submitted with its hash, nonce and raw transaction. A node refusing to accept a transaction ends the run; the next one retries with a fresh nonce.
  4. Open chain markets. Every active market gets a chain_markets row and its condition prepared on CTF (prepareCondition, by the operator), 20 per run; once the chain shows it, the row is open with the two token ids. A prepare reverted or never mined is retried; after five attempts the row is failed and left for staff.
  5. Report results. For each chain_results row still pending: once every settlement of the market is confirmed or failed, the resolver's reportPayouts is 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 with lastError "CTF holds X, Jua's books Y: staff must look". A market that never opened on chain is marked resolved with nothing to report.
  6. 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 ​

WhereHealthyLook when
settlements (evm)pending and submitted rows are seconds to a minute oldA row is pending/submitted for over 10 minutes, or attempts climbs, or last_error is set
chain_marketsNew markets open within a minute or twoA row is failed, or pending/submitted with last_error for long
chain_resultspending rows resolve within minutes of the resultlast_error mentions a disagreement, or a Safe resolver has not reported
The operator's BNBAbove 0.1 BNBBelow 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:

  1. 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 undoFillsTx as 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.
  2. 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 to pending and sent again.
  3. Queue new markets. Every open market gets its two share minters on the exchange, once.
  4. Confirm commissions. Each TON commission payout is read from the vault's payout state (sent, delivered, returned, refused); one sent for over an hour raises "A referral commission payout on TON is not confirmed".
  5. 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.
  6. Send the resolver's batch: results (RecordResult) for finalised markets whose TON trades have all settled, when this server holds the resolver key.
  7. The market maker (optional): re-quotes Jua's own bids on the busiest TON markets at most every three minutes.

What to watch ​

WhereHealthyLook when
ton_cursorAdvances; failures = 0failures climbs or last_error is set: the stuck alert
settlements (ton), ton_refundspending/submitted for secondsRows older than a few minutes, or last_error "the exchange never answered: sent again" repeating
ton_messagesApplied within secondsMessages not applied for minutes: the operator wallet's TON, or the indexers
ton_marketsopen within a minute of a market openingStuck pending/submitted
The operator wallet's TONSeveral TON above the 1 TON reserveThe 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. submitted payouts are confirmed from receipts (reverted → failed; lost → failed; not mined → rebroadcast); pending ones are sent as JuaVault.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 VaultPayout to 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 ​

WhereHealthyLook when
commission_payoutspending → submitted → confirmed within minutesfailed rows (the referrer can collect again; find why: vault paused, limits, gas), or submitted for over an hour
The fee vaultsBalance ≥ what referrers can collectA sweep took too much: collections fail with "Commissions cannot be paid out right now" (Recurring tasks)

Jua Markets · Africa's prediction market.