
The latest bull-market excitement has eased for now, the meme frenzy has temporarily subsided, and the market seems quiet again. But this round of speculation certainly brought on-chain markets to everyone's attention. There is still plenty to explore in the on-chain primary market, so this time we set out to do some research.
What can you do when the market is quiet? Get back to straightforward arbitrage, of course. Today's subject, however, is not arbitrage between exchanges. It is arbitrage between an on-chain market and an exchange: PancakeSwap on BSC and Binance USDT-margined perpetual futures.
The idea is familiar: the price of the same token in an on-chain pool sometimes differs from its price on Binance. When it is cheaper on-chain, buy it there and simultaneously open a short on Binance. When the prices converge, close both sides and capture the difference. What follows is a chronological account of the past week's work, the problems we encountered, and the strategy we eventually arrived at.
1. The First Strategy: More Than 13 Hours Without a Single Entry
The first version took the most direct approach: "Spot a spread, then rush in and take it." After deployment on FMZ, it ran for more than 13 hours and completed over 43,000 scan cycles, each taking a little over 700 milliseconds. It did not open a single position.
The logs revealed a strikingly consistent pattern. There were roughly 30 entry signals during that period. When each signal appeared, the on-chain buy quote was 50–147 basis points (bp) below Binance's best bid, comfortably exceeding the entry threshold. But every time the strategy requested a fresh quote just before submitting an order, the spread had shrunk to 0–40 bp, back to the token's usual level. The final check therefore blocked the trade. Around 30 signals, without a single exception.
The signals were real, and the final checks were correct. The problem lay in the less than one second between the two.

2. Where the Spread Went: A Seller Unloading 369 USDT at a Time and the First Trade in the Next Block
To find out, I pulled ARIA's on-chain trades around 03:51 a.m. and examined them one by one. Within that minute, the logs recorded three almost identical signals: initial spreads of 92.6, 91.1, and 92.8 bp, which fell to 40.9, 41.5, and 42.3 bp at the final check. Each had lost about 51 bp.
The on-chain records showed a seller unloading ARIA in fixed amounts of 369 USDT. Each sale pushed the pool price from 0.03703 down to around 0.03684, a move of roughly 51 bp. Then, in the very next block, the first or second transaction was invariably a purchase worth a few hundred USDT that brought the price back to its previous level. Three sell-offs, three recoveries, with the timing matching the strategy logs exactly.

These were on-chain backrunning bots. They monitor every block, and when someone sells heavily, they buy the discounted tokens at the very front of the next block, then sell on Binance to hedge. BSC currently produces a block roughly every 0.45 seconds. These bots do not secure their place by paying higher gas prices; their gas price is often the minimum 0.05 gwei. Instead, they transfer a tip of about 0.0006 BNB directly to the block builder to buy a position at the start of the block.
Our strategy needs at least two or three blocks to scan, fetch Binance order-book depth, check two balances, request a fresh quote, and submit the transaction. The spread it "sees" is therefore already gone before it can act. Every opportunity rejected by the final check had expired.
3. Scanning the Entire Market: The Money Is Real, but More Than 50 Competitors Are Already Sharing It
One token could have been a coincidence. So I wrote a detector to scan all 867,000 trades over the previous 24 hours across 1,428 pools and 217 tokens that had both a Binance perpetual contract and a PancakeSwap pool. It identified every individual trade that moved the price by at least 30 bp, then checked how quickly the move was reversed and who reversed it.
| Time taken for the price to recover | Occurrences per day | Theoretical gross profit for the recovery traders | Could we participate? |
|---|---|---|---|
| Within 0–1 blocks | About 2,700 | About 1,300–2,900 USDT, before tips | Requires bidding for a position at the front of the block |
| 3 or more blocks | About 1,000 | Market-wide upper bound of about 140 USDT | Most impacts were 35–45 bp, leaving only a few bp after fees |
| Never recovered | About 1,400 | — | Genuine market moves, not arbitrage opportunities |

Two conclusions emerged:
- This business does exist. Across the market, it generates roughly one or two thousand USDT in gross profit per day, much of it from sellers like the one in ARIA who unload fixed amounts at regular intervals. On HANA, someone was selling about 2,700 USDT every 42 blocks or so; on AAVE, someone was selling 190 USDT every 90 blocks.
- It is already fully contested. More than 50 recovery bots appeared over the day, with a few leaders consistently taking the first position in the block. The median gross profit per trade was only 0.6 USDT, and in the handful of trades I checked, 30%–50% of that went to the builder as a tip. Few competitors bothered with the slower opportunities, but after pool fees and Binance fees, almost nothing remained.
4. Four Dead Ends
Over the next day, I tried several different approaches, validating each against the data.
1. Shadow mode. The program subscribed to real-time pool trades. Whenever it detected a sell-off, it simulated a recovery trade against the block state after that sale, calculated net profit after fees using Binance's live quotes, and then checked who ultimately won. Detection itself was within our reach: the median delay from block production to receiving the event was 204 milliseconds. But among 89 events over 30 minutes, 14 were already reversed by someone else within the same block, and 18 were captured by traders at the front of the next block. Of the remaining 33 events where others were slow enough to leave us a chance, only 11 were profitable.
2. Seeing large trades in advance. We subscribed to pending transactions through public nodes. Of 27 large trades, 21 were visible, but the median time at which we saw them was actually 148 milliseconds after the block had been produced. Those trades went through private channels; public nodes broadcast them only after block production. To see them genuinely early, we would need to buy a professional transaction-flow service and then compete with the existing bots on bids.
3. Using on-chain activity as a signal to trade on Binance. When recovery bots buy on-chain, they need to sell on Binance to hedge, which should theoretically put some downward pressure on Binance prices. We measured Binance price movements 1–60 seconds after 258 on-chain impacts. At 1, 3, and 10 seconds, the median change in the mid-price was zero. Prices followed the direction of the on-chain move only 21%–38% of the time, and entering and exiting as a taker lost money on average. On-chain prices followed Binance, not the other way around.
4. Holding inventory and waiting. Buy on-chain during a sell-off, open a short on Binance, then hold both until someone pushes the on-chain price up and we can sell. Under optimistic assumptions—execution in the very next block—both trades in a one-hour window were profitable. Under conservative assumptions—one block later—we managed to buy just once in roughly 280 sell-offs. Speed was still the bottleneck.

5. The Turning Point: Become the Counterparty the Bots Trade With
The change in perspective came from an article Xiaocao had written for the FMZ column a few years earlier. Looking at one-second STORJ candles on Binance, he had spotted a bot dumping ten or twenty thousand tokens with market orders every few seconds, regardless of cost. Instead of trying to beat it on speed, he placed buy orders in the book ahead of time, passively took the inventory when the bot sold into them, then placed sell orders after the rebound. My takeaway was simple: an order placed in advance does not need speed.
On-chain AMMs do not have order books, which was why I originally thought this approach would not work. Concentrating liquidity in a PancakeSwap V3 range just below the current price looks like placing a limit buy order. But when the price recovers, it crosses that same range in reverse and buys the tokens back at the same prices. The recovery bots still capture the spread.
The breakthrough came with the 1inch Limit Order Protocol. Its orders are signed off-chain and filled on-chain: I sign an order off-chain saying "buy this token at this price with 20 USDT," then submit it to the 1inch order platform. Posting the order requires no on-chain transaction and no gas. Anyone can take that signed order on-chain and fill it, provided they are willing to sell me the tokens at my price.
That reverses the situation. When a seller pushes the pool price below my buy order's price, a recovery bot sees a risk-free route: buy tokens cheaply from the affected pool, then sell them into my order within the same transaction, keeping the difference. Speed and tips become the bot's concern. I simply wait for a fill, then open a short on Binance once I have received the tokens.
First, I checked whether anyone was already doing this. Over roughly the previous five hours, about 260 1inch limit orders had been filled on BSC by six recurring bot contracts. Around one-third followed the pattern of swapping on a DEX first and then filling a limit order within the same transaction. The median fill value was around 50 USDT. The bots that could do the work for us were already there.

6. Strategy Implementation
The strategy is written in JavaScript on FMZ and requires two exchange objects: exchanges[0] for Binance USDT-margined perpetual futures, and exchanges[1] for Web3 on BSC. Here is the overall workflow:

1. Token Selection: Focus on Consistent Sellers
The profit comes from sellers who repeatedly dump tokens, so those are the only tokens where we place orders. The strategy first identifies tokens in the PancakeSwap token list that also have Binance perpetual contracts. It then uses Multicall to query all their USDT and WBNB pools in the V2 and V3 factories in one batch—about 200 tokens and 1,400 pools.
After that, it fetches new Swap events with eth_getLogs every 20 seconds and calculates the pool price before and after each trade. A trade worth at least 100 USDT that pushes the price down by at least 40 bp counts as a sell-off. Sell-offs in the same token are then grouped by value, with amounts within 10% treated as belonging to the same bucket. The more events in a bucket, the more likely it is that someone is selling a fixed amount each time:
javascript
s.dumps.forEach(function (u) { var b = Math.round(Math.log(u) / Math.log(1.1)); (bucket[b] = bucket[b] || []).push(u) })
var best = []
Object.keys(bucket).forEach(function (b) { if (bucket[b].length > best.length) best = bucket[b] })
return { sym: k, dumps: s.dumps.length, pumps: s.pumps, repeat: best.length, size: median(best), mv: median(s.mv) }
Finally, tokens are ranked first by the number of fixed-size sell-offs, then by the total number of sell-offs, and the top five are selected. The default lookback is three hours. The table below shows a trial run using real data from the previous hour:
| Token | Fixed-size sell-offs | Approximate value per sale (USDT) | Total sell-offs | Upward price impacts | Price drop per sell-off |
|---|---|---|---|---|---|
| DUSK | 9 | 118 | 12 | 4 | 0.71% |
| O | 4 | 175 | 16 | 11 | 0.63% |
| HANA | 4 | 2517 | 6 | 9 | 0.98% |
| BR | 2 | 213 | 7 | 4 | 3.03% |
| UNI | 2 | 538 | 6 | 4 | 0.56% |
DUSK had 12 sell-offs in one hour, nine of them worth about 118 USDT each. HANA still featured the familiar seller unloading around 2,500 USDT at a time.
2. Entry: Sign a 1inch Limit Buy Order
The order price is Binance's best bid × (1 − 40 bp), and the order is valid for 15 seconds. 1inch v4 orders use EIP-712 structured signatures, with both the expiration time and permission for partial fills encoded in makerTraits:
javascript
function makerTraits(expireTs) { return (BigInt(1) << BigInt(254)) | (BigInt(expireTs) << BigInt(80)) }
var o = {
salt: String(now()) + String(Math.floor(Math.random() * 1000)), maker: WALLET, receiver: ZERO,
makerAsset: USDT, takerAsset: t.addr,
makingAmount: usdRaw.toString(), takingAmount: tokRaw.toString(),
makerTraits: makerTraits(expire).toString(),
}
var hash = orderDigest(o), sg = DEX.IO("sign", "0x" + hash)
HttpQuery(API_BASE, { method: "POST", body: body, headers: { "Authorization": "Bearer " + P.KEY, "Content-Type": "application/json" } })
A single incorrect bit in the signature format can cause the platform to reject the order or prevent it from being filled on-chain. At startup, the strategy therefore signs a test order, checks its digest against hashOrder in the 1inch contract, and verifies the signer with the ecrecover precompiled contract. It proceeds only if both checks pass.
Orders are not posted at fixed intervals. If the previous order has expired, or Binance's best bid has moved by more than 10 bp relative to the quote used when that order was posted, the strategy posts a new order at the updated price. Old orders are not canceled, because on-chain cancellation costs gas; they are simply allowed to expire. Several orders may therefore be valid at the same time. Exposure is capped by requiring "inventory plus unexpired buy orders for any one token ≤ 60 USDT" and "the total across all tokens ≤ 200 USDT":
javascript
var fresh = o && now() < o.expire && Math.abs(t.bid / o.ref - 1) * 1e4 < P.DRIFT
if (fresh) return
var buyPx = t.bid * (1 - P.BUY_BPS / 1e4)
var room = Math.min(P.MAX_INV - inv.qty * t.bid - pendingUsd(t), P.MAX_TOTAL - totalUsd())
There is one more calculation before posting an order: assuming it fills, how far below Binance's best ask would the price be if we immediately sold the inventory on-chain? If the gap exceeds 30 bp, the pool is too shallow: buying the tokens would leave us unable to sell them back out effectively. The strategy skips orders for that token for now.
3. Fills and Hedging
In live trading, the strategy checks the wallet's balance of each token once per cycle. An increase means a buy order has been filled, so it immediately opens a market short on Binance for the filled quantity. Any remainder below Binance's minimum order size is accumulated until there is enough to hedge together.
Simulation mode (DRY_RUN) follows the same process as live trading, but does not submit orders or execute trades. A simulated fill occurs when, during an order's validity period, the price of an on-chain trade crosses the order price by enough to cover the pool fee, leaving a profit for the bot. The entire order is then treated as filled, and the hedge is recorded at Binance's best bid at that moment:
javascript
if (o.side !== "buy" || pxUsd > o.px * (1 - p.fee)) return
o.filled = true
hedge(t, o.qty, o.px, snapAt(t, ts))
4. Exit: Wait for the Spread to Converge, Then Close Both Sides
The original idea was to post a sell order 40 bp above Binance's best ask after acquiring inventory, then wait for a bot to fill it when someone drove the pool price up. But the token-selection data showed that many tokens had large trades in only one direction, so a sell order might wait a very long time. We therefore switched to an active exit: on each cycle, the strategy uses PancakeSwap's Quoter contract to request quotes from all USDT pools for that token, calculates how much selling now would return, and compares the result with Binance's best ask:
javascript
var entryBp = (inv.shortCost / inv.short / (inv.cost / inv.qty) - 1) * 1e4
var exitBp = (best.px / t.ask - 1) * 1e4
var netBp = entryBp + exitBp - 2 * TAKER_FEE * 1e4
if (netBp < P.TP_BPS && !force) return
When entry spread + exit spread − Binance fees on both trades ≥ 5 bp, the strategy sells on PancakeSwap and simultaneously closes the short on Binance. With both sides executed together, changes in the token price offset each other, leaving only the spread to capture. If the spread still has not converged after 72 hours, both positions are forcibly closed at the prevailing spread.
5. Risk Controls
With one leg on-chain and the other on an exchange, there are more ways for things to go wrong than with a single-exchange strategy. The risk controls are therefore fairly detailed:
| Risk control | Implementation |
|---|---|
| Total exposure | Inventory across all tokens plus unexpired buy orders ≤ 200 USDT; hold no more than three tokens at a time |
| Sharp market movements | Pause buy-order posting for a token if its Binance price moves by more than 50 bp within 60 seconds |
| Shallow pools | Do not post an order if the trial sell quote shows slippage above 30 bp |
| Unmatched legs | If on-chain inventory and the Binance short quantity do not match, try to complete the hedge every cycle. If they still do not match after 30 seconds, close the excess portion of the unmatched leg and put the token into a cooldown |
| Ledger reconciliation | In live trading, reconcile the ledger against actual on-chain balances and actual Binance short positions every 60 seconds |
| Stale orders being picked off | If the spread locked in when a buy fills is worse than −30 bp, someone is taking the order at a stale price. Put the token into a 60-minute cooldown |
| Per-token loss | If a token's combined realized and unrealized loss reaches 2 USDT, force-close its positions and put it into a cooldown |
| Total loss | At a total loss of 10 USDT, stop all new entries and allow exits only |
The core logic for handling unmatched legs is:
javascript
if (now() - t.legSince < P.LEG_TIMEOUT * 1000) {
hedge(t, 0, inv.qty > 0 ? inv.cost / inv.qty : t.bid, { bid: t.bid, ask: t.ask })
return
}
Unrealized profit on the on-chain leg is also calculated using the price at which the inventory could actually be sold on-chain now, rather than substituting the Binance price. That way, if the spread between the two venues widens, the loss appears directly in the status panel instead of being hidden.
7. Where We Are Now
The token-selection scanner has been successfully tested on real on-chain data, and the strategy is currently running in simulation mode. The next steps are:
- Run the simulation to assess whether token selection and order prices are reasonable, and examine the spread distributions for simulated entries and exits.
- Post one order far from the market price to confirm that the 1inch platform accepts it.
- Start small-scale live trading with 20 USDT per order.
The foreseeable risks are:
- Being picked off by better-informed traders. If Binance prices fall sharply while my buy order is still valid, the order becomes expensive relative to the new market price. The first trader to fill it may then be someone who knows the price is falling, rather than a recovery bot. This is adverse selection. The validity period has been reduced from the originally planned 45 seconds to 15 seconds, with a cooldown triggered when a fill locks in a particularly poor spread. How much protection these measures provide still needs to be measured in live trading.
- Unhedged exposure. There is a gap of a few seconds between the on-chain fill and the Binance hedge.
- An exit that never arrives. The spread may fail to converge for a long time, eventually triggering a forced exit after 72 hours at an unfavorable spread.
- The wallet must be left alone. Any change in the wallet's token balances will be treated as an order fill and hedged. Do not manually operate this wallet while the strategy is running.
8. Closing Thoughts
This was an exploration of a framework. The process showed that mature bots already capture on-chain arbitrage spreads extremely efficiently. They compete on speed, private transaction flow, and tips to builders—areas where an ordinary strategy has no advantage.
Even so, the work was valuable. We learned the rhythm of on-chain recovery trades, identified the behavior of fixed-size sellers, and finally found a route through 1inch limit orders that does not require winning a speed race. Whether it can actually make money remains to be tested with small live trades. Opportunities always exist in the market. The code, data, and lessons from this attempt can serve as a reserve, helping us recognize and act on the next opportunity more quickly.
Strategy source code: 1inch On-Chain Limit-Order Arbitrage — BSC and Binance
This article is a personal record of strategy research and does not constitute investment advice. On-chain trading involves risks including smart contracts, private keys, adverse selection, and hedging failures. Simulated results and historical data do not indicate future returns.
- 1
