Hive-Swap Dev Update — August 19 → September 1, 2026

This cycle was almost entirely about one property of Hive Engine that we had been treating too casually: atomicity is per-op, not per-Hive-transaction. A custom_json carrying a fee transfer and three swap ops is not all-or-nothing. Each op succeeds or reverts independently, and the fee transfer — the simplest, cheapest, most likely-to-succeed op in the bundle — is the one that always lands.

That means a swap we build badly doesn't just fail. It charges the user 0.25% and gives them nothing back, on every retry. We found four separate ways that was happening, fixed all four, and went back through the history to un-count the damage.


/swap/build now refuses builds it knows will revert

The public build endpoint used to do format validation only — valid account, valid symbols, positive amount — and hand back ops. Two classes of request were dead on arrival:

Stale amounts. Transaction 8dab7897 re-submitted 9182.39746734 SCRAP whose balance had been spent 40 minutes earlier. The fee transfer landed; both marketpools hops reverted with insufficient input balance.

Book-only pairs with no BEED. APE and SHEKEL have no AMM pool, so the router correctly falls back to the Hive Engine order book. But HE's market contract burns 0.001 BEED per market.buy/market.sell via resourcemanager. With no BEED the market op reverts at the burn step while the fee — the one op with no burn — succeeds.

/swap/build now reads the caller's balance uncached (a stale read is the exact failure being guarded against) and refuses:

{
  "ok": false,
  "error": {
    "code": "BEED_REQUIRED",
    "message": "APE → SWAP.HIVE has no pool route, so it must trade on the order book, which burns 0.001 BEED per leg. <account> holds less than 0.002 BEED. Fund the account with BEED, or prepend your own `beedollar.convert` op — see /llms-full.txt."
  }
}

INSUFFICIENT_BALANCE covers the other case. If the balance RPC itself is unreachable we treat that as unknown and skip the check rather than blocking builds during a node outage. /quote is unchanged — it still returns the book route so you can show a price and then tell the user what they need.

Swap widget showing a split pool/book route and the BEED requirement panel

The widget side of the same guard: a 75% pool / 25% book split into FLUX, auto-slippage capped at 5% off a 19.16% price impact, and the BEED panel explaining exactly what the account is missing before anything gets signed.

The swap widget got the same treatment from the other side: its pre-broadcast balance refetch used to swallow its own RPC failure and "let the chain decide". The chain's decision costs money, so it now aborts and asks the user to retry.

Reverted swaps no longer count as volume

Once you accept that the fee transfer lands on a fully-reverted swap, an uncomfortable consequence follows: our own analytics were counting those as swaps. Volume, points, the leaderboard, the profile activity feed — all of it was fed by fee_events, and a fee event is exactly what a doomed swap produces.

Sync now reads each new swap transaction's HE ops and flags rows where every marketpools/market op errored. Consumer queries filter them out.

Two deliberate choices in there. It's flag, not drop — the fee genuinely was collected, and keeping the row means affected accounts stay identifiable for refunds. And it's "every op errored", not "any" — a three-hop swap that only failed on the last leg leaves the user holding the intermediate token. That's a partial swap, not a no-op, and it still counts.

Then we ran the detector over the full history: 6,045 swap transactions, 293 reverted — 4.9% of every swap the router has ever recorded. Not a recent regression either; it runs from the earliest swap in the table straight through to today. The largest single block was a daily automated job that had been paying the fee and receiving nothing every morning for over six weeks. The BEED_REQUIRED guard now stops that at build time.

A precision bug that broke 1 in 70 multi-hop swaps

Every hop's tokenAmount gets truncated to its input token's precision. But each hop's amount was computed independently from the quote, and an intermediate hop's minAmountOut is set to the next hop's tokenAmount — derived from the un-truncated figure. So the pool was asked to return what a larger input would have produced.

On a low-precision intermediate at small size that gap is brutal. 0.00739… truncated to 0.0073 on a precision-4 token is a 1.2% loss, against a 0.50% buffer:

marketpools.swapTokens  MEME:FUN     in 0.00099750  →  out 0.0074 MEME   ok
marketpools.swapTokens  MEME:PEPE    in 0.0073      minOut 4.49134435
    errors: ["exceeded max slippage for swap"]
marketpools.swapTokens  PEPE:SWAP.HIVE  in 4.49134435
    errors: ["insufficient input balance"]

Hop one paid out 0.0074 MEME. Hop two was built to spend 0.0073 and demand what 0.00739 would have yielded. It reverted, took hop three with it, and the fee was already gone.

The fix carries the real per-hop ratio forward so the buffer stays headroom instead of being eaten by truncation. Classifying every partial failure over 30 days: 15 of 1,055 swaps (1.4%) failed partway, every single one on exceeded max slippage for swap at a pool hop. Ten were this bug, with truncation losses between 1.1% and 9.1%.

Intermediate hops now respect your slippage setting

The other five were genuine quote-versus-chain drift, and they exposed a design problem worth explaining because it's not obvious.

Intermediate hops enforced a flat 0.50% regardless of the slippagePct you asked for. That number does double duty: it's both the drift a hop tolerates and the amount it strands, because hop N's minAmountOut is hop N+1's tokenAmount, and spending slightly less than the hop delivers is precisely what stops hop N+1 from overdrawing into your pre-existing balance. You cannot loosen one without loosening the other.

So it's now sized from your own tolerance — the per-hop share of your slippage, floored at the old 0.50%. Ask for 5% and each hop gets ~1.7% instead of 0.50%: more multi-hop routes complete, at the cost of stranding slightly more of the intermediate token.

Related, and the reason drift was biting at all: /swap/build was quoting off pool reserves up to 180 seconds stale and baking them into a transaction you sign. On a thin pool three minutes is more than enough movement to blow the tolerance. Build now quotes off uncached reserves. /quote keeps the cache, since nothing is signed from it — expect /swap/build to be a touch slower as a result, and don't treat the two as interchangeable.

One thing we could not fix, and integrators should know about it: a failed hop does not stop the hops after it. Per-op atomicity again. If hop N reverts, hop N+1 still executes and spends your existing balance of the intermediate token if you hold any. There is no Hive Engine mechanism to make one op conditional on a sibling, so the only lever is making the first failure less likely — which is what all of the above does. It's now documented in /llms-full.txt rather than left as a surprise.


Also shipped this cycle

  • HBD peg routes direct to graphene-swap; DSwap dropped entirely. Peg-ins built via the documented DSwap flow never delivered — its POST /SwapRequest auto-rejects any request with a null ChainTransactionId, which is unavoidable for an L1 input because the docs require registering before the deposit exists. Every request was dead on arrival and the pegged tokens went unmatched. HBD ↔ SWAP.HBD is a 1:1 gateway peg minus 0.75% and never needed a swap service. Memo grammar is now "<TOKEN> <destination-account>", receive amounts are local math instead of a remote quote that was ~13.5% off, and both directions are verified on chain to 8 decimal places.
  • External-chain bridges withdrawn from UI, API and docs. Three months of fee events tell the story plainly: 3,688 swaps, 723 HIVE pegs, 8 HBD pegs — and zero completions across every EVM, converter and HE-native route. Pure counterparty risk and support burden for no usage. Note this is a breaking /gateways response-shape change: evmBridges, converterBridges and nativeBridges are gone, as are the two /bridge/converter/* endpoints.
  • /bridge folded into /swap. The swap widget already performed both remaining pegs through the same gateway selection, so the page was duplicating it without adding capability. /bridge is now a redirect so existing links don't 404, and bridge history moved into the swap history drawer as a sub-tab.

History drawer on /swap with Swaps and Bridges sub-tabs

Bridge history now lives behind a sub-tab in the swap history drawer.

Smaller improvements

  • HBD slowness banner named DSwap and linked to dswap.trade long after the peg stopped touching DSwap — sending users with a slow peg to a site with no record of their transfer. Now names graphene-swap and links to the gateway account on hivehub.
  • Treasury sweep stopped persisting "no route to SWAP.BTC" rows. They were re-derivable from the pool map and had grown to 360,871 rows — 99.2% of the table. Table went from 28.6 MB to 3.6 MB.
  • Real lint coverage across both workspaces for the first time (turbo.json declared the task but nothing implemented it), then cleared all 42 React Compiler findings it surfaced. Two of those were user-visible: stale data could paint for a frame under a new label on account or token switches, and the wallet page flashed its loading skeletons every 30 seconds over data that was still good.

Try it: hive-swap.com
Public API docs: hive-swap.com/docs/api
Follow: @hive-swap.com

0.00577008 BEE
1 comments

Congratulations @hive-swap.com! You have completed the following achievement on the Hive blockchain And have been rewarded with New badge(s)

You received more than 400 upvotes.
Your next target is to reach 500 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

Check out our last posts:

0.00000000 BEE