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 revertThe 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.

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.
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.
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%.
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.
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./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.
Bridge history now lives behind a sub-tab in the swap history drawer.
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
Congratulations @hive-swap.com! You have completed the following achievement on the Hive blockchain And have been rewarded with New badge(s)
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
STOPCheck out our last posts: